from pathlib import Path
import csv,json,hashlib,shutil,zipfile,os
R=Path(__file__).resolve().parents[1];B=R/'inputs/final27'
D=R/'deliverables';D.mkdir(exist_ok=True);P=D/'Walkie_One_28_R1_Complete_Handoff';P.mkdir(exist_ok=True)
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
def copy(a,b):
 b.parent.mkdir(parents=True,exist_ok=True)
 if a.resolve()!=b.resolve():shutil.copy2(a,b)
for directory in ['cad','meshes','drawings','documents']:
 for f in (R/directory).rglob('*'):
  if f.is_file() and not f.name.startswith('Concept_28_R1_'):copy(f,P/f.relative_to(R))
for f in (R/'renders/studio').rglob('*'):
 if f.is_file():copy(f,P/'renders'/f.relative_to(R/'renders/studio'))
reference=R/'references/Approved_Chalk_Ember_Inspiration.png'
copy(reference,P/'references/Approved_Chalk_Ember_Inspiration.png')
for name in ['Concept_28_R1_Fusion_manifest.json','fusion_import_inventory.json','native_preservation_review.json','independent_geometry_review.json','pdf_visual_review.json','handoff_build.json','light_position_review.json','native_archive_integrity.json','final27_casting_adaptation.json','trace_reconciliation.json','Concept_28_R1_functional_inputs.json','Concept_28_R1_static_audit.json','Concept_28_R1_retention_audit.json','Concept_28_R1_switch_motion.json','Concept_28_R1_cover_insertion.json','Concept_28_R1_fastener_access.json','Concept_28_R1_USB_insertion.json','Concept_28_R1_LTE_service_path.json','Concept_28_R1_attachment_graph.json','STL_world_alignment.json','native_archive_reopen.json','Concept_28_R1_control_capture.json']:
 copy(R/'audit'/name,P/'audit'/name)
# Exact completed 27 inputs; no remote generation or source-task execution.
for folder in ['cad','docs']:
 for f in (B/folder).rglob('*'):
  if f.is_file():copy(f,P/'inputs/final27'/f.relative_to(B))
copy(B/'audit/Concept_27_R2_build_manifest.json',P/'inputs/final27/audit/Concept_27_R2_build_manifest.json')
copy(B/'source/Concept_27_R2_parameters.json',P/'inputs/final27/source/Concept_27_R2_parameters.json')
copy(R/'source/preview-materials.json',R/'source/preview-materials.json')
for name in ['01_import_27.py','02_inspect.py','03_build_28.py','04_finish_export.py','05_details.py','06_restore_pilots.py','07_world_coordinate_parts.py','08_native_verification.py','09_lower_status_light.py','11_casting_release_tapers.py','12_reopen_review.py','audit_functional_28.py','audit_access_28.py','audit_attachment_28.py','audit_control_capture_28.py','reconcile_trace_28.py','render.py','verify_exports.py','build_handoff.py','package_release.py','preview-materials.json']:
 f=R/'source'/name;text=f.read_text()
 text=text.replace("pathlib.Path(C28_ROOT)","pathlib.Path(C28_ROOT)")
 text=text.replace('inputs/final27','inputs/final27')
 text=text.replace("R/'references/Approved_Chalk_Ember_Inspiration.png'","R/'references/Approved_Chalk_Ember_Inspiration.png'")
 text=text.replace("R/'source/preview-materials.json'","R/'source/preview-materials.json'")
 text=text.replace("OUT/'inputs/27_UC1'","OUT/'inputs/27_UC1'")
 text=text.replace("ROOT/'inputs/27_UC1'","ROOT/'inputs/27_UC1'")
 text=text.replace("BASE/'documents/Parts_Schedule_BOM.csv'","BASE/'documents/Parts_Schedule_BOM.csv'")
 text=text.replace("BASE/'documents'","BASE/'documents'")
 text=text.replace("source / 'source/preview-materials.json'","source / 'source/preview-materials.json'")
 (P/'source').mkdir(exist_ok=True);(P/'source'/name).write_text(text)
(P/'source/run_fusion_pipeline.py').write_text('''"""Set C28_ROOT to the extracted package path in Fusion's Python console.
Then exec(compile(open(C28_ROOT+'/source/run_fusion_pipeline.py').read(), 'run_fusion_pipeline.py', 'exec')).
Creates a NEW Fusion document; leaves prior designs untouched. Requires desktop Autodesk Fusion.
"""
from pathlib import Path
# Preserve the supplied exact stock mesh export; Fusion still rebuilds its BRep.
C28_REUSE_STOCK_MESH=True
for name in ['01_import_27.py','11_casting_release_tapers.py','03_build_28.py','05_details.py','06_restore_pilots.py','09_lower_status_light.py','04_finish_export.py','07_world_coordinate_parts.py','08_native_verification.py']:
    filename=Path(C28_ROOT)/'source'/name
    exec(compile(filename.read_text(),str(filename),'exec'),globals())
''')
(P/'source/README.md').write_text('''# Editable source

Open `../cad/Walkie_One_28_R1_Chalk_Ember.f3d` for normal Fusion editing. This is the authoritative native document; imported 27 bodies are BRep base features, with native new fillet/laser features and cut-outline sketches. Historical sketches were not reconstructed.

To reproduce the complete edit sequence, make a working copy of this extracted package and set `C28_ROOT` in the Fusion Python Text Commands console to that copy's absolute directory. Execute `source/run_fusion_pipeline.py` as shown in its header. This uses the bundled exact completed final 27 R2 STEP inputs, creates a new document and rewrites derived exports in the working copy. It reuses the supplied unchanged purchased-mesh STEP/STL while rebuilding its native BRep, avoiding repeated tessellation of the 92 reference rods. It does not use Adam MCP. Do not execute arbitrary scripts included in third-party source packages; these listed scripts are the authoring recipe for this revision.

`render.py` uses Blender to render the actual exported part meshes and generate a compact GLB. `build_handoff.py` uses ReportLab to regenerate the seven-page guide after renders exist. Native checks are in `audit/native_preservation_review.json`. Independent OpenCascade checks are supplied as evidence; the final R2 per-part/hardware files and both baseline assemblies are bundled for rerunning the comparisons.

Nominal dimensions are explicit in `03_build_28.py`, the 3 mm light adjustment in `09_lower_status_light.py`, and the drawings. No shrink or tool compensation is applied. STEP and STL per-part files share world coordinates. DXF blanks are centered at their own origin in millimetres; laser artwork retains the play center Y=-47 mm.
''')
(P/'README.md').write_text('''# Walkie One 28 R1 - Chalk Ember

Start with `documents/Walkie_One_28_R1_Fusion_Handoff.pdf`.

- Native Fusion: `cad/Walkie_One_28_R1_Chalk_Ember.f3d`
- Assembly with OEM electronics: `cad/Walkie_One_28_R1_REFERENCE_ASSEMBLY.step`
- Case/custom parts + fasteners: `cad/Walkie_One_28_R1_CASE.step`
- 91 individual STEP/STL exports; 73 custom/material components and 18 fasteners.
- Seven DXF cut outlines/artwork; four nominal casting-master candidates.
- Material, proposed Pantone, BOM, first-article and open-item schedules.
- Exact final 27 R2 STEP inputs, Fusion authoring source and geometry evidence.

Created directly in Autodesk Fusion from the completed final Version 27 R2. The UC1 local release approach is adapted to the revised boss sites. The visible circular light is lowered 3 mm to Y=2 mm; the original internal LED/coupler and rear collector stay in place. No new Adam MCP generation. The mesh is purchased woven stock; its 92 reference rods are not separate fabrication jobs. Existing lens-flange fit, USB mating, resin/tooling, adhesion, acoustics, optics, RF and durability require qualification. See the fresh trace-reconciliation and functional audit results. Supplier-review prototype; not a fabrication release.
''')
# Source manifest contains checksums of immutable baseline files only.
inputs=sorted(f for f in (P/'inputs').rglob('*')if f.is_file())
with (P/'documents/Source_Inventory.csv').open('w',newline='')as f:
 w=csv.writer(f);w.writerow(['Path','Bytes','SHA256']);w.writerows([[str(x.relative_to(P)),x.stat().st_size,sha(x)]for x in inputs])
files=sorted(f for f in P.rglob('*')if f.is_file() and f.name not in ['SHA256SUMS.txt','File_Manifest.csv'])
with (P/'File_Manifest.csv').open('w',newline='')as f:
 w=csv.writer(f);w.writerow(['Path','Bytes','SHA256']);w.writerows([[str(x.relative_to(P)),x.stat().st_size,sha(x)]for x in files])
files.append(P/'File_Manifest.csv');(P/'SHA256SUMS.txt').write_text(''.join(sha(f)+'  '+str(f.relative_to(P))+'\n'for f in files))
def archive(path,files):
 with zipfile.ZipFile(path,'w',zipfile.ZIP_DEFLATED,compresslevel=6)as z:
  for f in files:z.write(f,P.name+'/'+str(f.relative_to(P)))
 with zipfile.ZipFile(path)as z:assert z.testzip()is None
 return sha(path)
allfiles=sorted(f for f in P.rglob('*')if f.is_file())
full=D/(P.name+'.zip');fullsha=archive(full,allfiles)
eng=D/'Walkie_One_28_R1_Fusion_CAD_Source.zip';engsha=archive(eng,[f for f in allfiles if f.relative_to(P).parts[0]not in ['renders','references']])
result={'revision':'R1','concept':'28','fileCount':len(allfiles),'crcVerified':True,'engineeringPackageSha256':engsha,'completePackageSha256':fullsha,'fabricationRelease':False,'publishedGeometryMatchesValidatedExports':all(sha(f)==sha(R/f.relative_to(P))for f in allfiles if f.relative_to(P).parts[0]in ['cad','meshes','drawings'])}
(D/'build-result.json').write_text(json.dumps(result,indent=2));print(json.dumps(result))
