from pathlib import Path
import argparse,json,tarfile,shutil,subprocess,hashlib,sys
R=Path(__file__).resolve().parents[1]
p=argparse.ArgumentParser();p.add_argument('--out',required=True);p.add_argument('--node',default='node');p.add_argument('--materials',required=True);p.add_argument('--product');p.add_argument('--source-root',required=True);p.add_argument('--download-directory',required=True);a=p.parse_args();R=Path(a.source_root).resolve()
O=Path(a.out).resolve();assert not O.exists();shutil.copytree(Path(a.product).resolve() if a.product else R/'product',O/'product');project=O/'product'
def sha(p):
 h=hashlib.sha256()
 with p.open('rb') as f:
  for b in iter(lambda:f.read(1048576),b''):h.update(b)
 return h.hexdigest()
for e in json.loads((R/'reports/SIZE-50-static-dependencies.json').read_text()):
 archive=R/e['archive'];assert sha(archive)==e['sha256'];temp=O/'dependencies'/e['name'].replace('/','__');temp.mkdir(parents=True)
 with tarfile.open(archive) as t:
  for m in t.getmembers():assert not Path(m.name).is_absolute() and '..' not in Path(m.name).parts and not m.issym() and not m.islnk()
  t.extractall(temp,filter='data')
 dest=project/'node_modules'/e['name'];dest.parent.mkdir(parents=True,exist_ok=True);shutil.copytree(temp/'package',dest)
materials=Path(a.materials).resolve();original=json.loads(materials.read_text());assert original['version']=='PUB-01-1'
download=Path(a.download_directory).resolve()
relocated=[]
for row in original['files']:
 name=row['target_build_relative_path']
 candidates=[download/name,download/Path(name).name]
 if name=='source.html':candidates.append(download/'source-materials-PUB-01-1.html')
 if name.startswith('licenses/LICENSES/'):candidates.append(R/'LICENSES'/name.removeprefix('licenses/LICENSES/'))
 elif name.startswith('licenses/'):candidates.append(R/name.removeprefix('licenses/'))
 found=next((f for f in candidates if f.is_file() and f.stat().st_size==row['bytes'] and sha(f)==row['sha256']),None)
 assert found is not None,'Download required material: '+name
 relocated.append({**row,'source_absolute_path':str(found)})
if not any(row['target_build_relative_path']=='downloads/PUB-01-1-BUILD-MATERIALS.json' for row in relocated):
 relocated.append({'source_absolute_path':str(materials),'target_build_relative_path':'downloads/PUB-01-1-BUILD-MATERIALS.json','bytes':materials.stat().st_size,'sha256':sha(materials)})
original={**original,'files':relocated};materials=O/'relocated-OWNER-COPY-MAP.json';materials.write_text(json.dumps(original,indent=2)+'\n')
r=subprocess.run([a.node,'scripts/import-materials.mjs',str(materials)],cwd=project,capture_output=True,text=True);(O/'import.log').write_text(r.stdout+r.stderr);assert r.returncode==0,r.stderr
report={'node_version':subprocess.check_output([a.node,'--version'],text=True).strip(),'new_CPP_compile':False,'preferred_source_unchanged':True,'modes':{}}
for mode in ['preview','production']:
 cmd=[a.node,'scripts/build.mjs','--mode',mode]+(['--origin','https://stlfixer.io'] if mode=='production' else [])
 r=subprocess.run(cmd,cwd=project,capture_output=True,text=True);(O/(mode+'-build.log')).write_text(r.stdout+r.stderr);assert r.returncode==0,r.stderr
 out=project/'dist'/mode;bm=json.loads((out/'build-manifest.json').read_text());checks={}
 for e in bm['files']:
  f=out/e['path'];assert f.stat().st_size==e['bytes'] and sha(f)==e['sha256'];checks[e['path']]={'bytes':e['bytes'],'sha256':e['sha256']}
 assert bm['materialVersion']==bm['releaseId']=='PUB-01-1'
 cfg=json.loads((out/'config.json').read_text());assert cfg['distribution']['correspondingSourceReady'] and cfg['limits']['bytes']==50000000 and cfg['limits']['faces']==1000000 and cfg['limits']['complexFaces']==100000 and cfg['limits']['timeoutMs']==60000
 checks['build-manifest.json']={'bytes':(out/'build-manifest.json').stat().st_size,'sha256':sha(out/'build-manifest.json')}
 report['modes'][mode]={'exit_code':r.returncode,'build_id':bm['buildId'],'mode':mode,'all_file_hash_size_match':True,'files':checks,'material_entries':len(original['files'])}
for e in original['preferredSource']:assert sha(project/e['path'])==e['sha256'] and (project/e['path']).stat().st_size==e['bytes']
(O/'result.json').write_text(json.dumps(report,indent=2)+'\n');print(json.dumps({m:{'build_id':v['build_id'],'exit':v['exit_code'],'files':len(v['files'])} for m,v in report['modes'].items()}))
