#!/usr/bin/env python3
"""Repair hybrid journal RR schema.

- planned_rr = setup potential RR (filter uses this)
- r = realized R (Win +planned_rr, Loss -1, BE 0)
- Aryy HTF Narrative Fib 50/62 rule:
  * 50% Fib entry => planned_rr 2.0
  * 62% Fib entry => planned_rr 3.0
  because SL is at 100% Fib and TP at -53% Fib.
"""
from __future__ import annotations

import json, shutil, statistics
from collections import Counter
from datetime import datetime, timezone
from pathlib import Path

ROOT=Path(__file__).resolve().parents[1]
DATA=ROOT/'data'; REPORTS=ROOT/'reports'

def now_iso(): return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace('+00:00','Z')
def stamp(): return datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')
def infer_fib_level(t):
    text=' '.join(str(t.get(k,'')) for k in ['fib','notes','ltf_trigger','htf_context','narrative']).lower()
    if '62' in text: return '62%'
    if '50' in text: return '50%'
    return None
def planned_rr_from_geometry(t):
    entry=float(t['entry']); stop=float(t['stop']); target=float(t['target']); direction=t['direction']
    if direction=='Long':
        risk=entry-stop; reward=target-entry
    else:
        risk=stop-entry; reward=entry-target
    if risk<=0: return None
    return round(reward/risk, 3)
def repair_trade(t):
    out=dict(t)
    strategy=out.get('strategy')
    geom_rr=planned_rr_from_geometry(out)
    fib_level=infer_fib_level(out)
    if strategy=='Aryy HTF Narrative Fib 50/62':
        if fib_level=='62%': planned=3.0
        elif fib_level=='50%': planned=2.0
        else:
            # If candidate came from this model but the exact fib level was not preserved,
            # use geometry if >=2, otherwise default to 50% model RR=2 rather than deleting the strategy.
            planned=geom_rr if geom_rr and geom_rr>=2 else 2.0
            fib_level='geometry/default_50%'
    else:
        planned=geom_rr if geom_rr is not None else float(out.get('planned_rr', out.get('r', 0)) or 0)
    out['planned_rr']=round(float(planned),3)
    out['fib_entry_level']=fib_level or out.get('fib_entry_level') or 'n/a'
    outcome=out.get('outcome')
    if outcome=='Win': out['r']=out['planned_rr']
    elif outcome=='Loss': out['r']=-1.0
    elif outcome=='BE': out['r']=0.0
    else:
        # fallback from old r sign
        old=float(out.get('r',0) or 0)
        out['outcome']='Win' if old>0 else 'Loss' if old<0 else 'BE'
        out['r']=out['planned_rr'] if out['outcome']=='Win' else -1.0 if out['outcome']=='Loss' else 0.0
    return out

def main():
    st=stamp(); summaries={}
    for rel in ['journal.json','backtest-data.json']:
        p=DATA/rel
        backup=DATA/f'{p.stem}_before_rr_schema_repair_{st}.json'
        shutil.copy2(p, backup)
        obj=json.loads(p.read_text())
        repaired=[repair_trade(t) for t in obj.get('trades',[])]
        # filter planned_rr >= 2.0 (not realized r)
        filtered=[t for t in repaired if float(t.get('planned_rr',0))>=2.0]
        obj['trades']=filtered
        obj.setdefault('metadata',{})['last_updated']=now_iso()
        obj['metadata']['trade_count']=len(filtered)
        obj['metadata']['source']='rr_schema_repaired_planned_rr_filter_ge_2'
        obj['metadata']['rr_definition']='planned_rr is setup potential; r is realized result (Win=+planned_rr, Loss=-1, BE=0)'
        p.write_text(json.dumps(obj,indent=2,ensure_ascii=False)+'\n')
        counts=Counter(t['outcome'] for t in filtered); strat=Counter(t['strategy'] for t in filtered); rs=[float(t['r']) for t in filtered]
        summaries[rel]={'backup':backup.name,'count':len(filtered),'outcomes':dict(counts),'strategy_counts':dict(strat),'winrate':round(counts.get('Win',0)/len(filtered)*100,2) if filtered else None,'total_R':round(sum(rs),2),'avg_R':round(statistics.mean(rs),3) if rs else None,'min_planned_rr':min(float(t['planned_rr']) for t in filtered) if filtered else None}
    REPORTS.mkdir(exist_ok=True)
    sp=REPORTS/'rr_schema_repair_summary.json'
    sp.write_text(json.dumps({'generated_at':now_iso(),'summaries':summaries},indent=2,ensure_ascii=False)+'\n')
    print(json.dumps({'summary_path':str(sp),'summaries':summaries},indent=2))
if __name__=='__main__': main()
