"""Independent OpenCascade verification of Fusion exports. Does not author CAD."""
from pathlib import Path
import json,math
from build123d import import_step,Box,Pos,Align,Compound
from OCP.BRepCheck import BRepCheck_Analyzer
OUT=Path(__file__).resolve().parents[1];BASE=OUT/'inputs/final27'
def bb(b):
 q=b.bounding_box();return [q.min.X,q.max.X,q.min.Y,q.max.Y,q.min.Z,q.max.Z]
def box(a):return Pos(a[0],a[2],a[4])*Box(a[1]-a[0],a[3]-a[2],a[5]-a[4],align=(Align.MIN,Align.MIN,Align.MIN))
def vol(b):
 if b is None:return 0
 if isinstance(b,(list,tuple)):return sum(vol(i) for i in b)
 return abs(b.volume)
def shape(b):
 return Compound(children=list(b)) if isinstance(b,(list,tuple)) else b
def overlap(a,b):return vol(a.intersect(b))
def bounds_overlap(a,b):return all(min(a[k+1],b[k+1])-max(a[k],b[k])>1e-5 for k in [0,2,4])
parts={};data={}
for p in sorted((OUT/'cad/parts').glob('*.step')):
 s=import_step(p);parts[p.stem]=s
 data[p.stem]={'valid':BRepCheck_Analyzer(s.wrapped).IsValid(),'solids':len(s.solids()),'volume_mm3':s.volume,'bbox_mm':bb(s)}
 print('Read',p.stem,flush=True)
errors=[k for k,v in data.items()if not v['valid']]
changed=[n for n in parts if n.startswith('C28_')]
pairs=[]
for i,a in enumerate(changed):
 for b in parts:
  if a==b or (b in changed and changed.index(b)<i):continue
  if not bounds_overlap(data[a]['bbox_mm'],data[b]['bbox_mm']):continue
  v=overlap(parts[a],parts[b])
  if v>1e-4:pairs.append({'a':a,'b':b,'overlap_mm3':v})
report={'validity_failures':errors,'part_files':len(parts),'parts':data,'changed_part_interferences':pairs}
# The Fusion-kernel preservation audit avoids cross-kernel coincident-face
# subtraction instability on the old shell's trimmed toroidal surfaces.
report['native_preservation']=json.loads((OUT/'audit/native_preservation_review.json').read_text())
report['cross_kernel_note']='OpenCascade coincident-shell subtraction was unstable (even for unchanged back shell). Exact interior preservation is measured with Fusion boolean differences; exported STEP validity and non-coincident interference are independently checked here.'
new_shell=parts[next(k for k in parts if k.startswith('C28_R1_01'))]
new_lens=parts[next(k for k in parts if k.startswith('C28_R1_04'))]
visible=shape(new_lens.intersect(box([-5,5,-5,10,0,.3])))
vbb=bb(visible)
assert abs((vbb[2]+vbb[3])/2-2)<1e-6
assert abs((vbb[0]+vbb[1])/2)<1e-6
assert abs(vbb[1]-vbb[0]-4.7)<1e-6
report['visible_light']={'center_mm':[0,2],'diameter_mm':4.7,'lowered_mm':3,'upper_stem_bbox_mm':vbb,'optical_uniformity_qualification_required':True}
clamp=parts['C27_R2_05_Front_Switch_Clamp']
report['play_travel_checks']=[]
for stroke in [0,.5]:
 for n in ['C28_R1_03_Round_Action_Carrier_Original_Retention','C28_R1_10_Round_Brushed_Steel_Play_Cap']:
  moving=Pos(0,0,-stroke)*parts[n]
  report['play_travel_checks'].append({'part':n,'inward_stroke_mm':stroke,'shell_overlap_mm3':overlap(moving,new_shell),'clamp_overlap_mm3':overlap(moving,clamp)})
# Check modifications against original OEM electronics and fasteners in world coordinates.
hardware=[]
for p in sorted((BASE/'cad/hardware').glob('*.step')):
 s=import_step(p);bounds=bb(s)
 for n in changed:
  if bounds_overlap(bounds,data[n]['bbox_mm']):
   v=overlap(parts[n],s)
   if v>1e-4:hardware.append({'changed':n,'hardware':p.stem,'overlap_mm3':v})
report['changed_vs_hardware_interferences']=hardware
old_shell=import_step(next((BASE/'cad/parts').glob('*01_Front*')))
old_lens=import_step(next((BASE/'cad/parts').glob('*04_Status*')))
inherited=overlap(old_shell,old_lens)
report['known_inherited_interferences']=[{'pair':'Front shell / lower lens collector flange','baseline_overlap_mm3':inherited,'status':'Physical fit qualification required.'}]
report['new_detected_interferences']=[p for p in pairs if not ('Circular_Status_Lens' in p['b'] and abs(p['overlap_mm3']-inherited)<1e-5)]
# Round-trip assemblies must contain valid solids and the same external bounding envelope.
report['assemblies']={}
for name in ['CASE','REFERENCE_ASSEMBLY']:
 s=import_step(OUT/'cad'/f'Walkie_One_28_R1_{name}.step')
 report['assemblies'][name]={'valid':BRepCheck_Analyzer(s.wrapped).IsValid(),'solids':len(s.solids()),'bbox_mm':bb(s),'volume_mm3':s.volume}
(OUT/'audit/independent_geometry_review.json').write_text(json.dumps(report,indent=2))
print(json.dumps({k:v for k,v in report.items()if k not in ['parts','unchanged_part_checks']},indent=2))
