#!/usr/bin/env python3
"""Post-computation reconciliation. Arithmetic checks only, no interpretation."""
import csv, os, math, json
from collections import OrderedDict
HERE=os.path.dirname(os.path.abspath(__file__)); F=[]
exec(open(os.path.join(HERE,'backtest.py')).read().split("print('='*94)")[0])

def mets(series):
    n=len(series); yrs=n/12.0
    cagr=series[-1]**(1/yrs)-1
    mr=[series[0]-1]+[series[i]/series[i-1]-1 for i in range(1,n)]
    mu=sum(mr)/n; vol=math.sqrt(sum((x-mu)**2 for x in mr)/(n-1)*12)
    peak=-1e9; mdd=0.0
    for v in series:
        peak=max(peak,v); mdd=min(mdd,v/peak-1)
    return cagr,vol,mdd,series[-1],mr

print('='*80); print('  RECONCILIATION — arithmetic verification of computed results'); print('='*80)

# R1 weights sum to 1 and preserve 60/40 inside non-gold
print('\nR1 target weights')
for gw in ALLOC:
    t=[0.60*(1-gw),0.40*(1-gw),gw]
    ok = abs(sum(t)-1)<1e-15 and abs(t[0]/(t[0]+t[1])-0.60)<1e-15
    print('   %3d%% -> %.4f/%.4f/%.4f  sum=%.15f  eq share of non-gold=%.6f  %s'
          %(gw*100,t[0],t[1],t[2],sum(t),t[0]/(t[0]+t[1]),'OK' if ok else 'FAIL'))
    if not ok: F.append('R1 %d'%(gw*100))

# R2 ending wealth reconstructs from monthly returns
print('\nR2 ending wealth reconstructed from monthly return stream')
for gw in ALLOC:
    nom,_,_=run(gw); c,v,d,e,mr=mets(nom)
    p=1.0
    for r in mr: p*=(1+r)
    ok=abs(p-e)<1e-9
    print('   %3d%%  metric %.10f  reconstructed %.10f  %s'%(gw*100,e,p,'OK' if ok else 'FAIL'))
    if not ok: F.append('R2 %d'%(gw*100))

# R3 CAGR consistent with ending wealth
print('\nR3 CAGR <-> ending wealth')
for gw in ALLOC:
    nom,_,_=run(gw); c,v,d,e,_=mets(nom)
    yrs=len(K)/12.0; imp=(1+c)**yrs
    ok=abs(imp-e)<1e-9
    print('   %3d%%  (1+CAGR)^%.4f = %.6f  vs %.6f  %s'%(gw*100,yrs,imp,e,'OK' if ok else 'FAIL'))
    if not ok: F.append('R3 %d'%(gw*100))

# R4 real = nominal deflated by cumulative CPI
print('\nR4 real vs nominal deflated by cumulative CPI')
cum=1.0
for k in K: cum*=(1+R[k][3])
print('   cumulative inflation factor %s..%s = %.4f'%(K[0],K[-1],cum))
for gw in ALLOC:
    nom,real,_=run(gw)
    ok=abs(real[-1]-nom[-1]/cum)<1e-9
    print('   %3d%%  real %.6f  vs nominal/CPI %.6f  %s'%(gw*100,real[-1],nom[-1]/cum,'OK' if ok else 'FAIL'))
    if not ok: F.append('R4 %d'%(gw*100))

# R5 monotonic gold weight -> monotonic gold contribution sanity (not a rule, just report)
print('\nR5 0% portfolio identical to V5 validated baseline')
nom,_,_=run(0.0); c,v,d,e,_=mets(nom)
prev=json.load(open(os.path.join(HERE,'normalised','validation_v5_v6.json')))
ok = abs(c-prev['baseline_cagr'])<1e-12 and abs(d-prev['baseline_mdd'])<1e-12
print('   CAGR %.10f vs %.10f | MDD %.10f vs %.10f  %s'
      %(c,prev['baseline_cagr'],d,prev['baseline_mdd'],'OK' if ok else 'FAIL'))
if not ok: F.append('R5')

# R6 drawdown-frequency counts recomputed independently
print('\nR6 drawdown-frequency recount (10y, independent loop)')
def roll_mdd(series,w):
    out=[]
    for i in range(w,len(series)+1):
        base=series[i-w-1] if i-w-1>=0 else 1.0
        n=[x/base for x in series[i-w:i]]
        peak=-1e9; m=0.0
        for v in n:
            peak=max(peak,v); m=min(m,v/peak-1)
        out.append(m)
    return out
b=roll_mdd(run(0.0)[0],120)
res=json.load(open(os.path.join(HERE,'normalised','results.json')))
for gw in ALLOC[1:]:
    r=roll_mdd(run(gw)[0],120)
    n=sum(1 for i in range(len(r)) if r[i]>b[i])
    rep=res[str(gw)]['dd_beat']['120'][0]
    ok=n==rep
    print('   %3d%%  recount %d  reported %d  %s'%(gw*100,n,rep,'OK' if ok else 'FAIL'))
    if not ok: F.append('R6 %d'%(gw*100))

print('\n'+'='*80)
print('  RECONCILIATION: %s'%('ALL PASS' if not F else 'FAILURES: '+', '.join(F)))
print('='*80)
