"""Narrow, recoverable reconciliation of the shared mirror. Never deploys."""
from pathlib import Path
import json,hashlib,fcntl,shutil,datetime,argparse
root=Path(__file__).resolve().parents[1];repo=root.parents[1]
a=argparse.ArgumentParser();a.add_argument('--task-routes',action='store_true');args=a.parse_args()
canonical=Path('/Users/agency/.agents/cf-review');snapshot=root/'private/mirror-recovery/verified-host-snapshot';baseline=root/'private/mirror-recovery/baseline-preserved'
changed={'komplex-journey-2026-10-02-v1','komplex-rita-funnel-2026-09-26-v1','komplex-journey-contract-2026-10-01-v1'}
globals_={'index.html','review-manifest.json','_worker.js','_routes.json'}
stamp=datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ');backup=root/'private/canonical-reconciliation'/stamp;backup.mkdir(parents=True)
def sha(p):return hashlib.sha256(p.read_bytes()).hexdigest()
added=[];removed=[];preserved=[];replaced=[]
with open('/tmp/clientsflow-review-host.lock','a') as lock:
 fcntl.flock(lock,fcntl.LOCK_EX)
 for row in json.loads((repo/'evidence/mirror-recovery/omitted-catalogue-fallbacks.json').read_text()):
  rel=row['path'];p=canonical/rel;cached=Path(row['recoverable_cached_source'])
  if (baseline/rel).is_file() or not p.is_file():continue
  if sha(p)!=row['sha256']:
   preserved.append({'path':rel,'reason':'changed since task-created fallback inventory'});continue
  assert cached.is_file() and sha(cached)==row['sha256'],f'No exact restore source: {rel}'
  removed.append({'path':rel,'sha256':row['sha256'],'restore_source':str(cached.resolve())});p.unlink()
 for p in snapshot.rglob('*'):
  if not p.is_file():continue
  rel=p.relative_to(snapshot);name=str(rel)
  if rel.parts[0] in changed or name in globals_:continue
  q=canonical/rel
  if q.is_file():
   if sha(q)!=sha(p):preserved.append({'path':name,'reason':'existing unrelated bytes preserved','canonical_sha256':sha(q),'published_snapshot_sha256':sha(p)})
  else:
   q.parent.mkdir(parents=True,exist_ok=True);shutil.copy2(p,q);assert sha(q)==sha(p);added.append({'path':name,'sha256':sha(q),'restore_action':'remove only if unchanged'})
 if args.task_routes:
  for name in changed|globals_:
   p=snapshot/name;q=canonical/name
   assert p.exists(),f'Final published source absent: {name}'
   old=backup/name
   if q.is_dir():shutil.copytree(q,old);shutil.rmtree(q)
   elif q.is_file():shutil.copy2(q,old);q.unlink()
   if p.is_dir():shutil.copytree(p,q)
   else:shutil.copy2(p,q)
   replaced.append({'path':name,'backup':str(old),'existed_before':old.exists()})
 receipt={'at':stamp,'canonical':str(canonical),'snapshot':str(snapshot),'added':added,'removed_task_created_fallbacks':removed,'preserved_different_unrelated_files':preserved,'replaced_task_routes_and_generated_globals':replaced,'backup':str(backup),'no_deployment':True}
 (backup/'restore.json').write_text(json.dumps(receipt,indent=2));(root/'evidence'/f'canonical-reconciliation-{stamp}.json').write_text(json.dumps(receipt,indent=2))
print(json.dumps({'added':len(added),'removed_task_fallbacks':len(removed),'preserved_unrelated_differences':len(preserved),'replaced':len(replaced),'restore_receipt':str(backup/'restore.json')},indent=2))
