#!/usr/bin/env python3
"""Reconstruct journal.csv — all closed trades + manual PHOR close."""

import csv
import json
import re
from datetime import datetime
from pathlib import Path
from collections import defaultdict

LOG_DIR = Path(__file__).parent.parent / "logs"
SIGNALS_LOG = LOG_DIR / "signals.log"
VT_DIR = LOG_DIR / "virtual_trading"
STATES_DIR = VT_DIR / "states"
JOURNAL_FILE = VT_DIR / "journal.csv"

PHOR_CURRENT_PRICE = 5922.00

# ── Parse signals.log ──────────────────────────────────────────────────────

def parse_signals_log():
    TS_RE = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})'
    
    opens = []
    details = []
    closes = []
    
    with open(SIGNALS_LOG, "r") as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            
            # SIDE TICKER: entry=..., sl=..., tp=..., size=...
            m = re.match(rf'^{TS_RE} \| INFO\s+\| (\w+) (\w+): entry=([\d.]+), sl=([\d.]+), tp=([\d.]+), size=(\d+)$', line)
            if m:
                ts_str, side, ticker, entry, sl, tp, size = m.groups()
                ts = int(datetime.strptime(ts_str, "%Y-%m-%d %H:%M:%S").timestamp())
                details.append(dict(
                    ticker=ticker, side=side, entry_price=float(entry),
                    sl_price=float(sl), tp_price=float(tp), size=int(size),
                    timestamp=ts,
                ))
                continue
            
            # [TICKER] POSITION OPENED: SIDE @ PRICE
            m = re.match(rf'^{TS_RE} \| INFO\s+\| \[(\w+)\] POSITION OPENED: (\w+) @ ([\d.]+)$', line)
            if m:
                ts_str, ticker, side, price = m.groups()
                ts = int(datetime.strptime(ts_str, "%Y-%m-%d %H:%M:%S").timestamp())
                opens.append(dict(ticker=ticker, side=side, entry_price=float(price), timestamp=ts))
                continue
            
            # [TICKER] POSITION CLOSED: STATUS PnL=... RR=... Capital=...
            m = re.match(rf'^{TS_RE} \| INFO\s+\| \[(\w+)\] POSITION CLOSED: (\w+) PnL=([\d.-]+) RR=([\d.-]+) Capital=([\d.]+)$', line)
            if m:
                ts_str, ticker, status, pnl, rr, capital = m.groups()
                ts = int(datetime.strptime(ts_str, "%Y-%m-%d %H:%M:%S").timestamp())
                closes.append(dict(ticker=ticker, status=status, pnl=float(pnl), rr=float(rr), timestamp=ts))
                continue
    
    # Sort
    opens.sort(key=lambda x: x["timestamp"])
    details.sort(key=lambda x: x["timestamp"])
    closes.sort(key=lambda x: x["timestamp"])
    
    # Group by ticker
    opens_t = defaultdict(list)
    for o in opens: opens_t[o["ticker"]].append(o)
    details_t = defaultdict(list)
    for d in details: details_t[d["ticker"]].append(d)
    closes_t = defaultdict(list)
    for c in closes: closes_t[c["ticker"]].append(c)
    
    # Merge opens with details (chronological pairing)
    merged = defaultdict(list)
    for ticker in set(list(opens_t.keys()) + list(details_t.keys())):
        op_list = opens_t.get(ticker, [])
        det_list = details_t.get(ticker, [])
        for i, op in enumerate(op_list):
            m = dict(op, sl_price=None, tp_price=None, size=None)
            if i < len(det_list):
                d = det_list[i]
                m.update(sl_price=d["sl_price"], tp_price=d["tp_price"], size=d["size"])
            merged[ticker].append(m)
    
    # Pair opens with closes, accounting for gaps
    trades = []
    for ticker in sorted(merged.keys()):
        op_list = merged[ticker]
        cl_list = closes_t.get(ticker, [])
        
        ci = 0
        for op in op_list:
            if op["sl_price"] is None:
                continue
            if ci >= len(cl_list):
                break
            cl = cl_list[ci]
            # Skip close if it's before the open (belongs to a missing open)
            while ci < len(cl_list) and cl_list[ci]["timestamp"] < op["timestamp"]:
                ci += 1
            if ci >= len(cl_list):
                break
            cl = cl_list[ci]
            
            if op["side"] == "LONG":
                exit_price = op["entry_price"] + (cl["pnl"] / op["size"])
            else:
                exit_price = op["entry_price"] - (cl["pnl"] / op["size"])
            trades.append(dict(
                ticker=ticker, side=op["side"],
                entry_time=op["timestamp"], exit_time=cl["timestamp"],
                entry_price=op["entry_price"], exit_price=round(exit_price, 2),
                sl_price=op["sl_price"], tp_price=op["tp_price"],
                size=op["size"], pnl=cl["pnl"], rr=cl["rr"], status=cl["status"],
            ))
            ci += 1
    
    return trades, closes_t, merged


# ── Manual trades ──────────────────────────────────────────────────────────

def add_missing_trades(trades):
    """Add trades with closes but no opens in signals.log (NVTK first trade)."""
    nvtk1 = {
        "ticker": "NVTK", "side": "LONG",
        "entry_time": 1781694000,
        "exit_time": 1781722813,
        "entry_price": 1032.80,
        "exit_price": 1020.75,
        "sl_price": 1020.78,
        "tp_price": 1050.87,
        "size": 193,
        "pnl": -2325.24,
        "rr": -1.00,
        "status": "SL",
    }
    trades.append(nvtk1)
    print(f"Added missing NVTK trade #1: SL PnL={nvtk1['pnl']:+.2f}")
    return trades


# ── PHOR manual close ─────────────────────────────────────────────────────

def add_phor_close(trades):
    phor_sf = STATES_DIR / "PHOR_state.json"
    if not phor_sf.exists():
        return trades
    with open(phor_sf) as f:
        state = json.load(f)
    pos = state.get("positions", {}).get("PHOR")
    if not pos:
        print("PHOR already closed, skipping manual close")
        return trades
    
    entry = pos["entry_price"]
    exit_p = PHOR_CURRENT_PRICE
    size = pos["size"]
    side = pos["side"]
    pnl = (exit_p - entry) * size if side == "LONG" else (entry - exit_p) * size
    risk = abs(entry - pos["sl_price"])
    rr = pnl / (size * risk) if risk > 0 and size > 0 else 0.0
    
    trades.append({
        "ticker": "PHOR", "side": side,
        "entry_time": int(pos["entry_time"]),
        "exit_time": int(datetime.now().timestamp()),
        "entry_price": entry, "exit_price": exit_p,
        "sl_price": pos["sl_price"], "tp_price": pos["tp_price"],
        "size": size, "pnl": round(pnl, 2), "rr": round(rr, 2),
        "status": "MANUAL",
    })
    
    state["capital"] = round(state["capital"] + pnl, 2)
    state["journal_count"] = state.get("journal_count", 0) + 1
    state["positions"] = {}
    with open(phor_sf, "w") as f:
        json.dump(state, f, indent=2)
    print(f"PHOR closed MANUAL: entry={entry} exit={exit_p} PnL={pnl:+.2f}")
    print(f"PHOR state updated: capital={state['capital']:,.2f}")
    return trades


# ── Write CSV ──────────────────────────────────────────────────────────────

def write_journal(trades):
    trades.sort(key=lambda t: t["entry_time"])
    with open(JOURNAL_FILE, "w", newline="") as f:
        w = csv.writer(f)
        w.writerow(["id","ticker","side","entry_time","exit_time",
                     "entry_price","exit_price","sl_price","tp_price",
                     "size","pnl","rr","status","duration_bars",
                     "entry_date","exit_date"])
        for i, t in enumerate(trades, 1):
            w.writerow([
                i, t["ticker"], t["side"], t["entry_time"], t["exit_time"],
                t["entry_price"], t["exit_price"], t["sl_price"], t["tp_price"],
                t["size"], round(t["pnl"], 2), round(t["rr"], 2), t["status"],
                (t["exit_time"] - t["entry_time"]) // 3600,
                datetime.fromtimestamp(t["entry_time"]).strftime("%Y-%m-%d"),
                datetime.fromtimestamp(t["exit_time"]).strftime("%Y-%m-%d"),
            ])


# ── Main ───────────────────────────────────────────────────────────────────

def main():
    print("=== Reconstructing journal.csv ===\n")
    
    trades, closes_t, merged = parse_signals_log()
    print(f"Parsed {len(trades)} trades from signals.log")
    
    print()
    trades = add_missing_trades(trades)
    print()
    trades = add_phor_close(trades)
    print()
    
    write_journal(trades)
    
    trades.sort(key=lambda t: t["entry_time"])
    print(f"\n{'#':>3s} {'Ticker':6s} {'Side':5s} {'Entry':>10s} {'Exit':>10s} {'Status':6s} {'PnL':>10s} {'Size':>5s} {'Dur':>4s}")
    print("-" * 80)
    for i, t in enumerate(trades, 1):
        dur = (t["exit_time"] - t["entry_time"]) // 3600
        print(f"{i:3d} {t['ticker']:6s} {t['side']:5s} {t['entry_price']:10.2f} {t['exit_price']:10.2f} {t['status']:6s} {t['pnl']:+10.2f} {t['size']:5d} {dur:4d}")
    
    total = sum(t["pnl"] for t in trades)
    wins = [t for t in trades if t["pnl"] > 0]
    losses = [t for t in trades if t["pnl"] <= 0]
    print(f"\nSummary: {len(trades)} trades | PnL={total:+,.2f} | Capital={1_000_000+total:,.2f}")
    print(f"Wins={len(wins)} ({sum(t['pnl'] for t in wins):+,.2f}) | Losses={len(losses)} ({sum(t['pnl'] for t in losses):+,.2f})")
    
    print(f"\nWritten to: {JOURNAL_FILE}")


if __name__ == "__main__":
    main()
