"""Self-contained, explicit native transfer-test inputs; never installs a ROM.

Routine probes declare their reset PC/scratch state. Current real roster data
supplies identities/prices/positions; full native game and physical USB coverage
are separate. No private workspace path or historical success is imported.
"""
from pathlib import Path
import argparse,hashlib,json,os,re,shutil,struct,subprocess
from allstar import catalogue
from transfer_model import Market,TransferError
from native_probe import NativeProbe

def context():
 ap=argparse.ArgumentParser();ap.add_argument('rom',type=Path);ap.add_argument('output',type=Path);ap.add_argument('--abi',action='store_true');ap.add_argument('--negative',action='store_true');ap.add_argument('--baseline',type=Path);ap.add_argument('--seed',type=Path);a=ap.parse_args();a.rom=a.rom.resolve();a.output=a.output.resolve();a.output.mkdir(parents=True,exist_ok=True)
 h=Path(__file__).resolve().parent;w=o=out=a.output;rom=a.rom;b=rom.read_bytes();m=Market(catalogue(bytearray(b)));initial=m.initial();state=initial
 for i in range(2):
  for uid in sorted(state.rosters[10],key=m.price):
   try:new=m.sell(state,10,uid)
   except TransferError:continue
   break
  state=new
 for low,high in[(85,99),(60,79)]:
  for uid in sorted(m.allowed,key=m.price,reverse=True):
   if not low<=m.players[uid]['overall']<=high:continue
   try:new=m.buy(state,10,uid)
   except TransferError:continue
   break
  state=new
 state=m.mark_ready(state);probe=NativeProbe(rom,h);canary=bytes((i*59+i//251+17)&255 for i in range(65536));seed=bytearray(canary);seed[0x4000:0x5000:2]=m.encode(m.mark_ready(initial),1);seed[0x5000:0x6000:2]=m.encode(state,1);seed[0xfa00:0xfa08:2]=bytes([0x54,0x46,1,0xfe])
 expected_cache=bytes.fromhex('5452000100205000000a0003')+bytes(4)+b''.join(struct.pack('>H',u)for club in[10,3]for u in state.rosters[club]+(65535,)*(26-len(state.rosters[club])))
 assert len(expected_cache)==120
 return dict(a=a,args=a,rom=rom,b=b,m=m,initial=initial,state=state,s=initial,probe=probe,canary=canary,seed=seed,expected_cache=expected_cache,h=h,w=w,o=o,out=out)

def symbols(module,origin,rom,output):
 """Independently assemble current source and require exact linked ROM bytes."""
 h=Path(__file__).resolve().parent;p=Path(output)/('symbols-'+module);p.mkdir(exist_ok=True)
 experimental=module in['seasonend','coach_lines','transfers_ui'];src=(h if experimental else h.parent/'season')/(module+'.s')
 text=src.read_text();text=(' .equ WITH_TRANSFERS,1\n'if experimental else'')+text
 if experimental:text=f' .equ WITH_REFINEMENTS,{int(Path(rom).read_bytes()[0x258000:0x258002]!=bytes.fromhex("ffff"))}\n'+text
 if module=='transfers_ui':
  data=Path(rom).read_bytes();assert data[0x22a000:0x22a002]==bytes.fromhex('6100');force=0x22a002+int.from_bytes(data[0x22a002:0x22a004],'big',signed=True);assert 0x22a000<=force<0x22b000;text=f' .equ MENU_FORCE,0x{force:x}\n'+text
 (p/(module+'.s')).write_text(text)
 for inc in(h.parent/'season').glob('*.inc'):shutil.copy2(inc,p/inc.name)
 command=f'm68k-elf-as -m68000 -o module.o {module}.s && m68k-elf-ld -Ttext=0x{origin:x} -e _start -o module.elf module.o && m68k-elf-objcopy -O binary module.elf module.bin && m68k-elf-nm -n module.elf'
 result=subprocess.run(['docker','run','--rm','--user',f'{os.getuid()}:{os.getgid()}','-v',str(p)+':/w','-w','/w','--entrypoint','sh','ghcr.io/stephane-d/sgdk','-c',command],check=True,capture_output=True,text=True);(p/'symbols.txt').write_text(result.stdout)
 from main_menu_scope import prior_scope_image
 comparison=prior_scope_image(Path(rom).read_bytes())
 code=(p/'module.bin').read_bytes();assert comparison[origin:origin+len(code)]==code,('independently assembled module differs from tested ROM',module)
 found={z[2]:int(z[0],16)for line in result.stdout.splitlines()if len(z:=line.split())==3 and re.fullmatch('[0-9a-fA-F]+',z[0])};(p/'verified.json').write_text(json.dumps(dict(module=module,origin=hex(origin),bytes=len(code),module_sha256=hashlib.sha256(code).hexdigest(),rom_sha256=hashlib.sha256(Path(rom).read_bytes()).hexdigest(),symbols=found),indent=2)+'\n');return found
