import adsk.core,adsk.fusion,pathlib,json,traceback
OUT=pathlib.Path(C28_ROOT);BASE=OUT
app=adsk.core.Application.get();source=app.activeDocument;des=adsk.fusion.Design.cast(app.activeProduct);tm=adsk.fusion.TemporaryBRepManager.get()
try:
    initial=json.loads((OUT/'audit/fusion_import_inventory.json').read_text())['components'];lookup={c['name']:c for c in initial}
    preserved=[]
    for c in des.allComponents:
        if c.name not in lookup or not c.bRepBodies.count:continue
        old=lookup[c.name]['bodies'];now=list(c.bRepBodies)
        preserved.append({'name':c.name,'bodies':len(now),'same_face_counts':len(old)==len(now) and all(a['faces']==b.faces.count for a,b in zip(old,now)),'volume_delta_mm3':sum(b.volume*1000 for b in now)-sum(a['volume_mm3']for a in old)})
    front=next(o for o in des.rootComponent.allOccurrences if o.component.name.startswith('C28_R1_01'))
    newbody=tm.copy(front.bRepBodies.item(0))
    newlens=tm.copy(next(o for o in des.rootComponent.allOccurrences if o.component.name.startswith('C28_R1_04')).bRepBodies.item(0))
    baseline=app.importManager.importToNewDocument(app.importManager.createSTEPImportOptions(str(BASE/'cad/Concept_28_R1_Functional_Baseline_CASE.step')))
    olddes=adsk.fusion.Design.cast(app.activeProduct)
    oldo=next(o for o in olddes.rootComponent.allOccurrences if '_01_Front' in o.component.name)
    oldbody=tm.copy(oldo.bRepBodies.item(0))
    oldlens=tm.copy(next(o for o in olddes.rootComponent.allOccurrences if '_04_Status_Lens' in o.component.name).bRepBodies.item(0))
    # Same Fusion kernel, same world coordinates, cut-only interior comparison.
    clip=tm.createBox(adsk.core.OrientedBoundingBox3D.create(adsk.core.Point3D.create(0,0,-1.6105),adsk.core.Vector3D.create(1,0,0),adsk.core.Vector3D.create(0,1,0),10,17,2.779))
    # z interval -30 to -2.21 mm.
    assert tm.booleanOperation(oldbody,clip,adsk.fusion.BooleanTypes.IntersectionBooleanType)
    assert tm.booleanOperation(newbody,clip,adsk.fusion.BooleanTypes.IntersectionBooleanType)
    volumes=[oldbody.volume*1000,newbody.volume*1000]
    diffs=[]
    for a,b in [(oldbody,newbody),(newbody,oldbody)]:
        q=tm.copy(a);ok=tm.booleanOperation(q,b,adsk.fusion.BooleanTypes.DifferenceBooleanType)
        diffs.append({'boolean_success':ok,'volume_mm3':q.volume*1000 if ok else None})
    report={'engine':'Autodesk Fusion 2704.1.53','unchanged_components':preserved,'front_shell_below_z_minus_2_21_mm':{'volumes_mm3':volumes,'directional_differences':diffs},'note':'Native verification uses the same Fusion kernel. Other checks are independently run on exported STEP with OpenCascade.'}
    lensclip=tm.createBox(adsk.core.OrientedBoundingBox3D.create(adsk.core.Point3D.create(0,.5,-.408),adsk.core.Vector3D.create(1,0,0),adsk.core.Vector3D.create(0,1,0),1,2,.384))
    # Lens rear collector: z=-6 to -2.16 mm, below the changed front stem.
    for body in [oldlens,newlens]:assert tm.booleanOperation(body,lensclip,adsk.fusion.BooleanTypes.IntersectionBooleanType)
    ld=[]
    for a,b in [(oldlens,newlens),(newlens,oldlens)]:
        q=tm.copy(a);ok=tm.booleanOperation(q,b,adsk.fusion.BooleanTypes.DifferenceBooleanType)
        ld.append({'boolean_success':ok,'volume_mm3':q.volume*1000 if ok else None})
    report['rear_lens_collector_below_z_minus_2_16_mm']={'volumes_mm3':[oldlens.volume*1000,newlens.volume*1000],'directional_differences':ld,'visible_output_lowered_mm':3,'internal_LED_and_coupler_unchanged':True}
    (OUT/'audit/native_preservation_review.json').write_text(json.dumps(report,indent=2))
    baseline.close(False);source.activate()
    # Compare actual assembly occurrences, including repeated instances. Component
    # definitions alone undercount repeated OEM subcomponents in a Fusion import.
    def occurrence_snapshot(design):
        result=[]
        for o in design.rootComponent.allOccurrences:
            n=o.component.name
            if n.startswith('C28_') or n.startswith(('C27_R2_01_','C27_R2_02_','C27_R2_03_','C27_R2_04_','C27_R2_10_','C27_R2_11_','C27_R2_16_')):continue
            for b in o.bRepBodies:
                q=b.boundingBox
                result.append({'component':n,'body':b.name,'faces':b.faces.count,'volume_mm3':b.volume*1000,'bbox_mm':[v*10 for v in [q.minPoint.x,q.maxPoint.x,q.minPoint.y,q.maxPoint.y,q.minPoint.z,q.maxPoint.z]],'transform':list(o.transform2.asArray())})
        return sorted(result,key=lambda r:(r['component'],r['body'],r['bbox_mm']))
    current=occurrence_snapshot(des)
    original=app.importManager.importToNewDocument(app.importManager.createSTEPImportOptions(str(OUT/'inputs/final27/cad/Walkie_One_Concept_27_R2_REFERENCE_ASSEMBLY.step')))
    original_snapshot=occurrence_snapshot(adsk.fusion.Design.cast(app.activeProduct))
    mismatches=[]
    if len(current)!=len(original_snapshot):mismatches.append({'body_counts':[len(original_snapshot),len(current)]})
    for a,b in zip(original_snapshot,current):
        if a['component']!=b['component'] or a['faces']!=b['faces'] or abs(a['volume_mm3']-b['volume_mm3'])>1e-5 or max(abs(x-y) for x,y in zip(a['bbox_mm'],b['bbox_mm']))>1e-5 or max(abs(x-y) for x,y in zip(a['transform'],b['transform']))>1e-7:mismatches.append({'baseline':a,'current':b})
    report['unchanged_assembly_occurrences']={'baseline_body_count':len(original_snapshot),'current_body_count':len(current),'mismatches':mismatches,'method':'same-kernel world bounds, volume, faces and occurrence placement; excludes six cosmetic substitutions and two locally tapered shells','baseline':'final 27 R2 reference STEP'}
    original.close(False);source.activate()
    (OUT/'audit/native_preservation_review.json').write_text(json.dumps(report,indent=2))
    print('Native preservation review recorded.')
except:
    (OUT/'audit/native_verification_error.txt').write_text(traceback.format_exc());print(traceback.format_exc())
