def uq():return str(uuid.uuid4())
def qt(s):return json.dumps(s,ensure_ascii=False)
def prop(name,value,x,y,hide=False):return f'(property {qt(name)} {qt(value)} (at {x} {y} 0) (effects (font (size 1.0 1.0))'+(' (hide yes)' if hide else '')+'))'
root=uq();lib=[];placed=[];connections=[]
for idx,p in enumerate(parts):
    name='Part_'+p['ref'].replace('#','');libid='ILIO24:'+name;n=len(p['pins']);left=(n+1)//2;right=n-left
    height=max(left,right)*2.54+5.08;h=height/2;pinloc=[];pins=[]
    for k,pin in enumerate(p['pins']):
        side=0 if k<left else 1;j=k if side==0 else k-left;size=left if side==0 else right;y=(size-1)*1.27-j*2.54;x=-17.78 if side==0 else 17.78;angle=0 if side==0 else 180
        pins.append(f'(pin {pin["type"]} line (at {x} {y} {angle}) (length 5.08) (name {qt(pin["name"])} (effects (font (size 0.9 0.9)))) (number {qt(pin["number"])} (effects (font (size 0.9 0.9)))))');pinloc.append((x,y,side))
    lib.append(f'(symbol {qt(libid)} (pin_names (offset 0.5)) (in_bom {"no" if p["ref"].startswith("#") or not p.get("in_bom",True) else "yes"}) (on_board {"no" if p["ref"].startswith("#") else "yes"}) '+prop('Reference',p['ref'],0,h+5)+prop('Value',p['value'],0,h+2)+prop('Footprint',p['footprint'],0,0,True)+f'(symbol {qt(name+"_0_1")} (rectangle (start -12.7 {h}) (end 12.7 {-h}) (stroke (width 0.254) (type default)) (fill (type background)))) (symbol {qt(name+"_1_1")} '+''.join(pins)+'))')
    col=idx%8;row=idx//8;cx=76.2+col*139.7;cy=63.5+row*63.5
    placed.append(f'(symbol (lib_id {qt(libid)}) (at {cx} {cy} 0) (unit 1) (in_bom {"no" if p["ref"].startswith("#") or not p.get("in_bom",True) else "yes"}) (on_board {"no" if p["ref"].startswith("#") else "yes"}) (uuid {uq()}) '+prop('Reference',p['ref'],cx,cy-h-5)+prop('Value',p['value'],cx,cy-h-2)+prop('Footprint',p['footprint'],cx,cy,True)+prop('MPN',p['mpn'],cx,cy,True)+f'(instances (project "ILIO24-A01" (path "/{root}" (reference {qt(p["ref"])}) (unit 1)))))')
    for pin,(px,py,side) in zip(p['pins'],pinloc):
        x=round(cx+px,4);y=round(cy-py,4)
        if pin['net'] is None:connections.append(f'(no_connect (at {x} {y}) (uuid {uq()}))');continue
        end=round(x+(-5.08 if side==0 else 5.08),4)
        connections.append(f'(wire (pts (xy {x} {y}) (xy {end} {y})) (stroke (width 0) (type default)) (uuid {uq()}))')
        connections.append(f'(label {qt(pin["net"])} (at {end} {y} {180 if side==0 else 0}) (effects (font (size 0.9 0.9)) (justify {"right" if side==0 else "left"} bottom)) (uuid {uq()}))')
text='ILIO24-A01 R02 - PIN / NET CIRCUIT - FABRICATION HOLD\nIndependent controller; no AC on this board. All identical labels connect.\nLogic ground and field ground must remain isolated. Connector J1 pinout is a custom harness, not DevKit J1 numbering.\nExternal sources: V24_RAW/FGND24 from SYS09; protected V24 feeds all field loads and DDR-15L-5; V33_L/GND_L from separately isolated DevKit.\nPCB is an engineering candidate. All supplies, reverse/surge/DDR startup/physical capture remain unvalidated. RTN must not be tied to GND.'
sch=f'(kicad_sch (version 20250114) (generator "eeschema") (uuid {root}) (paper "A0") (title_block (title "ILIO24-A01 pin/net circuit") (date "2026-10-06") (rev "R02 CANDIDATE HOLD")) (lib_symbols '+''.join(lib)+') '+f'(text {qt(text)} (at 25.4 20.32 0) (effects (font (size 1.2 1.2)) (justify left top)) (uuid {uq()}))'+''.join(placed)+''.join(connections)+')'
(R/'ILIO24-A01.kicad_sch').write_text(sch)
library='(kicad_symbol_lib (version 20241209) (generator "kicad_symbol_editor") '+''.join(s.replace('"ILIO24:Part_','"Part_',1) for s in lib)+')'
(R/'ILIO24.kicad_sym').write_text(library)
(R/'sym-lib-table').write_text('(sym_lib_table (version 7) (lib (name "ILIO24") (type "KiCad") (uri "${KIPRJMOD}/ILIO24.kicad_sym") (options "") (descr "Pin-accurate candidate; Manufacturer-derived candidate footprints; assembly qualification pending")))\n')
