from pathlib import Path
import sys,re,json,shutil
ROOT=Path(__file__).resolve().parent;BASE=ROOT.parent
sys.path.insert(0,str(BASE/'parity_metadata_closure_A22'))
from prepare_candidates import forms,run,sha
from align_and_verify import footprint_rows,netlist,semantic_geometry
SRC=BASE/'compact_bench_suite/drill_control';DST=ROOT/'controller_candidate';DST.mkdir(exist_ok=True)
OLD='drill_control_evt_a1_compact_bench';NEW='drill_control_evt_a2_paired'
s=(SRC/'drill_control.kicad_sch').read_text();s=re.sub(r'\(project\s+"[^\"]+"','(project "'+NEW+'"',s)
lib=list(forms(s,'lib_symbols'));assert len(lib)==1
content=re.sub(r'^\(\s*lib_symbols\s+','',lib[0][2])[:-1];content=re.sub(r'(\(symbol\s+")Project:',r'\1',content)
(DST/'Project.kicad_sym').write_text('(kicad_symbol_lib (version 20241209) (generator "kicad_symbol_editor")\n'+content+'\n)\n')
(DST/'sym-lib-table').write_text('(sym_lib_table (version 7) (lib (name "Project") (type "KiCad") (uri "${KIPRJMOD}/Project.kicad_sym") (options "") (descr "Exact embedded symbols")))\n')
(DST/(NEW+'.kicad_sch')).write_text(s);shutil.copyfile(SRC/(OLD+'.kicad_pro'),DST/(NEW+'.kicad_pro'))
fps,pins=netlist(ROOT/'controller-netlist.xml');before=(SRC/(OLD+'.kicad_pcb')).read_text();seen=set()
for _,_,blk,ref,ident in footprint_rows(before):
 assert ident.rsplit(':',1)[-1]==fps[ref].rsplit(':',1)[-1]
 for _,_,pad in forms(blk,'pad'):
  num=re.match(r'\(pad "([^\"]+)"',pad)[1];k=(ref,num);assert k in pins;seen.add(k)
  m=re.search(r'\(net "([^\"]+)"',pad);v=m[1] if m else None
  if pins[k].startswith('unconnected-'):assert v is None or v.replace('{slash}','/')==pins[k]
  else:assert v and v.lstrip('/')==pins[k].lstrip('/'),(k,v,pins[k])
assert seen==set(pins)
lookup={v.lstrip('/'):v for v in pins.values() if not v.startswith('unconnected-')}
def align(m):
 v=m[1]
 return '(net '+json.dumps(v.replace('/','{slash}') if v.startswith('unconnected-') else lookup[v.lstrip('/')])+')'
s=re.sub(r'\(net "([^\"]+)"\)',align,before)
for st,en,blk,ref,ident in reversed(list(footprint_rows(s))):
 blk=re.sub(r'^(\(footprint\s+)"[^\"]+"',lambda m:m[1]+json.dumps(fps[ref]),blk,count=1)
 for ps,pe,pad in reversed(list(forms(blk,'pad'))):
  num=re.match(r'\(pad "([^\"]+)"',pad)[1];v=pins[(ref,num)]
  if v.startswith('unconnected-') and not re.search(r'\(net "',pad):
   pad=pad.rstrip()[:-1]+' (net '+json.dumps(v.replace('/','{slash}'))+'))';blk=blk[:ps]+pad+blk[pe:]
 s=s[:st]+blk+s[en:]
assert semantic_geometry(s)==semantic_geometry(before)
for st,en,blk,ref,ident in reversed(list(footprint_rows(s))):
 if ref=='U1':
  assert '(property "Datasheet" ""' in blk;blk=blk.replace('(property "Datasheet" ""','(property "Datasheet" "https://www.st.com/resource/en/datasheet/stm32g431cb.pdf"');s=s[:st]+blk+s[en:]
pcb=DST/(NEW+'.kicad_pcb');pcb.write_text(s)
for args,name in [(['sch','export','netlist','--format','kicadxml','--output',str(DST/'netlist.xml'),str(DST/(NEW+'.kicad_sch'))],'netlist.log'),(['sch','erc','--severity-all','--format','json','--output',str(DST/'erc.json'),str(DST/(NEW+'.kicad_sch'))],'erc.log'),(['pcb','drc','--severity-all','--all-track-errors','--schematic-parity','--format','json','--output',str(DST/'drc.json'),str(pcb)],'drc.log')]:assert run(args,DST/name)==0
_,newpins=netlist(DST/'netlist.xml');assert pins==newpins
d=json.loads((DST/'drc.json').read_text());e=json.loads((DST/'erc.json').read_text());vs=[v for x in e['sheets'] for v in x['violations']]
print(json.dumps({'drc':len(d['violations']),'unconnected':len(d['unconnected_items']),'parity':len(d['schematic_parity']),'ERC':len(vs),'types':list(set(v['type'] for v in vs))},indent=2))
(ROOT/'controller_pair_checks.json').write_text(json.dumps({'source_board_sha256':sha(SRC/(OLD+'.kicad_pcb')),'candidate_board_sha256':sha(pcb),'candidate_schematic_sha256':sha(DST/(NEW+'.kicad_sch')),'schematic_electrical_pins_unchanged':len(pins),'physical_geometry_preserved':True,'metadata_updates':'Project instances; local exact embedded symbols; library-qualified footprints; canonical net labels and explicit NC annotations','physical':'NOT_RUN_NO_SAMPLE','manufacturing_status':'FABRICATION_HOLD'},indent=2)+'\n')
