"""PAPER TRADER — TP01 Trend Portfolio (PORT LF4h), forward-only, simulato.

Esegue la strategia VINCENTE (src/strategies/trend_portfolio.py, config CANONICAL) in
paper trading FORWARD-ONLY su capitale virtuale (default 2000 USDT), portafoglio 50/50
BTC+ETH a 4h. Stato persistente -> resume al riavvio.

DESIGN (onesto, niente esecuzione reale: l'esecuzione e' DISABILITATA nel progetto):
  - Legge i parquet certificati locali (data/raw, BTC/ETH 1h) e resampla a 4h.
  - Alla prima esecuzione parte dall'ultima barra 4h CHIUSA disponibile (forward-only:
    NON include lo storico nel PnL di paper, traccia solo da ora in avanti).
  - Ad ogni run processa le NUOVE barre 4h chiuse dall'ultima volta: applica il rendimento
    della posizione tenuta, addebita le fee sul turnover, registra i trade sui cambi di
    posizione, poi ricalcola la posizione-bersaglio (decisa con dati <= ultima barra chiusa).
  - Per avere barre fresche, aggiornare prima i dati:
        uv run python scripts/analysis/rebuild_history.py --asset BTC ETH

Stato: data/paper_trend/state.json + trades.jsonl (append-only).

    uv run python scripts/live/paper_trend.py            # avanza il paper col dato disponibile
    uv run python scripts/live/paper_trend.py --status   # solo stato, non avanza
    uv run python scripts/live/paper_trend.py --reset     # azzera lo stato (riparte da ora)
"""
from __future__ import annotations

import json
import sys
from pathlib import Path

import numpy as np
import pandas as pd

PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))

from src.backtest.harness import load
from src.strategies.trend_portfolio import (
    TrendPortfolio, CANONICAL, resample_tf, DEPLOY_TF, simple_returns)

STATE_DIR = PROJECT_ROOT / "data" / "paper_trend"
STATE_FILE = STATE_DIR / "state.json"
TRADES_FILE = STATE_DIR / "trades.jsonl"
ASSETS = ["BTC", "ETH"]
WEIGHT = 0.5
INITIAL_CAPITAL = 2000.0


def build_bars() -> dict[str, pd.DataFrame]:
    return {a: resample_tf(load(a, "1h"), DEPLOY_TF) for a in ASSETS}


def load_state() -> dict | None:
    if STATE_FILE.exists():
        return json.loads(STATE_FILE.read_text())
    return None


def save_state(st: dict):
    STATE_DIR.mkdir(parents=True, exist_ok=True)
    STATE_FILE.write_text(json.dumps(st, indent=2))


def append_trade(rec: dict):
    STATE_DIR.mkdir(parents=True, exist_ok=True)
    with open(TRADES_FILE, "a") as f:
        f.write(json.dumps(rec) + "\n")


def init_state(dfs) -> dict:
    last_ts = min(int(dfs[a]["timestamp"].iloc[-1]) for a in ASSETS)
    tp = TrendPortfolio(**CANONICAL)
    positions = {}
    for a in ASSETS:
        df = dfs[a]
        df = df[df["timestamp"] <= last_ts]
        positions[a] = tp.current_target(df)
    return dict(
        capital=INITIAL_CAPITAL, initial_capital=INITIAL_CAPITAL,
        start_ts=last_ts, last_ts=last_ts, positions=positions, n_bars=0,
        peak=INITIAL_CAPITAL, max_dd=0.0,
    )


def advance(st: dict, dfs: dict) -> dict:
    """Processa tutte le barre 4h chiuse DOPO st['last_ts']."""
    tp = TrendPortfolio(**CANONICAL)
    # precompute per-asset: timestamps, returns, target series (causale)
    data = {}
    for a in ASSETS:
        df = dfs[a]
        c = df["close"].values.astype(float)
        data[a] = dict(
            ts=df["timestamp"].values.astype("int64"),
            dt=pd.to_datetime(df["datetime"]).values,
            r=simple_returns(c),
            tgt=tp.target_series(df),
        )
    # common new timestamps after last_ts (present in both assets)
    common = sorted(set(data["BTC"]["ts"]).intersection(data["ETH"]["ts"]))
    new_ts = [t for t in common if t > st["last_ts"]]
    if not new_ts:
        return st

    pos = dict(st["positions"])
    cap = st["capital"]
    peak = st.get("peak", cap)
    max_dd = st.get("max_dd", 0.0)
    idx = {a: {int(t): i for i, t in enumerate(data[a]["ts"])} for a in ASSETS}

    for t in new_ts:
        # 1) apply held position return over this bar, charge turnover fees vs new target
        combo = 0.0
        new_pos = {}
        for a in ASSETS:
            i = idx[a][int(t)]
            r = float(data[a]["r"][i])
            held = pos[a]
            new_t = float(data[a]["tgt"][i])
            turn = abs(new_t - held)
            net = held * r - CANONICAL["fee_side"] * turn
            combo += WEIGHT * net
            new_pos[a] = new_t
            # record a trade when the SIGN of position changes (entry/exit/flip)
            if np.sign(new_t) != np.sign(held):
                append_trade(dict(
                    ts=int(t), dt=str(pd.Timestamp(data[a]["dt"][i])),
                    asset=a, action="ENTRY" if new_t != 0 else "EXIT",
                    from_pos=round(held, 4), to_pos=round(new_t, 4),
                    capital=round(cap, 2),
                ))
        cap *= (1.0 + max(combo, -0.99))
        peak = max(peak, cap)
        max_dd = max(max_dd, (peak - cap) / peak if peak > 0 else 0.0)
        pos = new_pos

    st.update(capital=cap, last_ts=int(new_ts[-1]), positions=pos,
              n_bars=st.get("n_bars", 0) + len(new_ts), peak=peak, max_dd=max_dd)
    return st


def print_status(st: dict, dfs: dict):
    start = pd.Timestamp(st["start_ts"], unit="ms", tz="UTC")
    last = pd.Timestamp(st["last_ts"], unit="ms", tz="UTC")
    days = (last - start).total_seconds() / 86400
    cap = st["capital"]
    ret = cap / st["initial_capital"] - 1
    daily = (cap - st["initial_capital"]) / days if days > 0 else 0.0
    print("=" * 72)
    print(f"  PAPER TRADER — TP01 Trend Portfolio (PORT LF{DEPLOY_TF}, 50/50 BTC+ETH)")
    print("=" * 72)
    print(f"  start         {start:%Y-%m-%d %H:%M} UTC")
    print(f"  last bar      {last:%Y-%m-%d %H:%M} UTC   ({days:.1f} giorni, {st['n_bars']} barre {DEPLOY_TF})")
    print(f"  capitale      {cap:,.2f} USDT   (start {st['initial_capital']:,.0f})")
    print(f"  ritorno       {ret*100:+.2f}%   |  €/giorno {daily:+.2f}   |  maxDD {st['max_dd']*100:.1f}%")
    print(f"  posizioni now { 'flat' if all(p==0 for p in st['positions'].values()) else '' }")
    for a in ASSETS:
        p = st["positions"][a]
        state = "FLAT" if p == 0 else ("LONG" if p > 0 else "SHORT")
        print(f"     {a}: {state:<5s} target {p:+.3f}x  (frazione di equity dello sleeve)")
    # what the strategy decides at the latest available closed bar
    print("  ── prossima decisione (ultima barra chiusa disponibile) ──")
    tp = TrendPortfolio(**CANONICAL)
    for a in ASSETS:
        w = tp.current_target(dfs[a])
        print(f"     {a}: target {w:+.3f}x")
    if TRADES_FILE.exists():
        n = sum(1 for _ in open(TRADES_FILE))
        print(f"  trade registrati: {n}  ({TRADES_FILE})")


def main():
    argv = sys.argv[1:]
    dfs = build_bars()
    if "--reset" in argv:
        if STATE_FILE.exists():
            STATE_FILE.unlink()
        if TRADES_FILE.exists():
            TRADES_FILE.unlink()
        print("stato azzerato.")
    st = load_state()
    if st is None:
        st = init_state(dfs)
        save_state(st)
        print("paper trader inizializzato (forward-only da ora).\n")
    elif "--status" not in argv:
        st = advance(st, dfs)
        save_state(st)
    print_status(st, dfs)


if __name__ == "__main__":
    main()
