#!/usr/bin/env python3
"""Exact unmodified-image Quickgame lifecycle, native goals and preserved season records.

Empty and explicitly seeded three-game season fixtures; every human split.
Winning OT puck/skater geometry is declared, not a naturally played match.
Native goal, result, exit and return to the original season must actually occur.
"""
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import argparse,json,os,struct,subprocess,hashlib
ap=argparse.ArgumentParser(description=__doc__);ap.add_argument('rom',type=Path);ap.add_argument('output',type=Path);ap.add_argument('--cpu-only',action='store_true');ap.add_argument('--allstar',action='store_true');a=ap.parse_args()
h=Path(__file__).resolve().parent;o=a.output.resolve();o.mkdir(parents=True,exist_ok=True)
has_allstar=a.rom.read_bytes()[0x22c000:0x22c002]!=b'\xff\xff'
left_inputs=['1100 press 1 Left 5']+(['1250 press 1 Left 5']if has_allstar and not a.allstar else [])
if a.allstar:assert has_allstar

def battery(saved):
 b=bytearray(32768)
 if saved:
  struct.pack_into('>4s6H',b,0x1000,b'SESN',3,11,3,84,0,3)
  struct.pack_into('>8H',b,0x1010+10*16,3,4,2,1,0,10,8,0)
  b[0x1220+10*100]=2;b[0x1220+10*100+26]=1
  struct.pack_into('>4s4H',b,0x1ea0,b'S3SV',1,1,0,1)
  b[0x1ef0:0x1ef6]=b'PC\x02\xfd\x01\xfe'
 for offset in range(0,32768,8192):
  b[offset+0x1fff]=sum(b[offset:offset+0x1ffe])%256;b[offset+0x1ffe]=b[offset+0x1fff]^255
 raw=bytearray(65536);raw[1::2]=b;return bytes(raw)
def one(c):
 split,saved=c;p=o/f'split{split}-saved{int(saved)}';p.mkdir(exist_ok=True);(p/'spec.json').write_text('{"captures":{}}\n');raw=battery(saved);(p/'initial.srm').write_bytes(raw);(p/'battery.srm').write_bytes(raw)
 lines=left_inputs+['1400 press 1 Down 5']+[f'{1550+150*i} press 1 Right 5'for i in range(split)]+['2300 press 1 Up 5']+[f'{2500+700*i} press 1 Start 5'for i in range(4)]+['25000 exit'];(p/'commands.txt').write_text('\n'.join(lines)+'\n')
 env={k:v for k,v in os.environ.items()if not k.startswith('GPGX_')};env.update(GPGX_DEV='1281',GPGX_DEV2='1281',GPGX_SRM=str(p/'battery.srm'),GPGX_HOOK=str(h/'testcases/quickgame_flow_hook.py'),QUICK_SPLIT=str(split),EXPECT_ALLSTAR=str(int(a.allstar)))
 with(p/'log').open('w')as log:subprocess.run(['python3',str(h/'test_core.py'),str(a.rom.resolve()),str(p/'commands.txt'),str(p)],env=env,stdout=log,stderr=log,check=True,timeout=100)
 r=json.loads((p/'result.json').read_text());assert r['season_records_unchanged']
 if saved:
  b=(p/'battery.srm').read_bytes()[1::2]
  assert struct.unpack_from('>6H',b,0x1004)==(3,11,3,84,0,3),'saved season header changed'
  assert b[0x1220+10*100]==2 and b[0x1220+10*100+26]==1,'saved player statistics changed'
 return dict(split=split,saved_fixture=saved,result=r)
with ThreadPoolExecutor(max_workers=3)as pool:results=list(pool.map(one,[(s,saved)for saved in [False,True]for s in ([4]if a.cpu_only else range(4))]))
(o/'results.json').write_text(json.dumps(dict(rom_sha256=hashlib.sha256(a.rom.read_bytes()).hexdigest(),scope=__doc__,cases=results),indent=2)+'\n');print('PASS',len(results),'actual native Quickgame ends/exits/season returns; all season records exact')
