"""Stato SHADOW di TP01 (Deribit mainnet, SOLA LETTURA): target causali + conto/posizioni REALI +
ordini di ribilancio COSTRUITI (mai inviati). Modulo condiviso da `scripts/live/live_trend.py` (CLI)
e dalla dashboard, cosi' i due non divergono. Robusto ai fallimenti di rete: degrada a offline/flat
senza sollevare eccezioni (la dashboard non deve crashare se il mainnet non risponde)."""
from __future__ import annotations

import json
from pathlib import Path

import numpy as np
import pandas as pd

from src.backtest.harness import load
from src.live.deribit import DISASTER_LABEL, INSTRUMENT, DeribitRead, build_rebalance_order
from src.strategies.trend_portfolio import CANONICAL, TrendPortfolio, resample_1d

PROJECT_ROOT = Path(__file__).resolve().parents[2]
ASSETS = ["BTC", "ETH"]
WEIGHT = 0.5
FALLBACK_CAPITAL = 2000.0
PAPER_STATE = PROJECT_ROOT / "data" / "paper_trend" / "state.json"


def tp01_trades(limit: int = 15) -> list[dict]:
    """Lista dei TRADE di TP01 = cambi di posizione (ENTRY long / EXIT flat) dedotti dal segnale
    causale `target_series` sui dati certificati. Account-independent (gira anche offline nel
    container). Si ricalcola a ogni render -> include sia lo storico sia i trade forward man mano
    che arrivano. Ritorna gli ultimi `limit` (piu' recenti primi)."""
    tp = TrendPortfolio(**CANONICAL)
    out: list[dict] = []
    for a in ASSETS:
        df = resample_1d(load(a, "1h"))
        c = df["close"].to_numpy(dtype=float)
        dt = pd.to_datetime(df["datetime"]).to_numpy()
        tgt = tp.target_series(df)
        prev = 0.0
        for i in range(len(tgt)):
            if np.sign(tgt[i]) != np.sign(prev):
                out.append(dict(
                    ts=int(pd.Timestamp(dt[i]).value // 10**6),
                    date=str(pd.Timestamp(dt[i]).date()),
                    asset=a,
                    action="ENTRY" if tgt[i] != 0 else "EXIT",
                    from_pos=round(float(prev), 3),
                    to_pos=round(float(tgt[i]), 3),
                    price=round(float(c[i]), 1),
                ))
            prev = tgt[i]
    out.sort(key=lambda r: r["ts"], reverse=True)
    return out[:limit]


def _safe_client() -> DeribitRead | None:
    try:
        return DeribitRead()
    except Exception:
        return None


def _marks(client, dfs):
    marks, src = {}, {}
    for a in ASSETS:
        if client is None:
            marks[a], src[a] = float(dfs[a]["close"].iloc[-1]), "close certificata"
            continue
        try:
            marks[a], src[a] = client.mark_price(INSTRUMENT[a]), "mainnet"
        except Exception as e:
            marks[a], src[a] = float(dfs[a]["close"].iloc[-1]), f"fallback close ({type(e).__name__})"
    return marks, src


def _positions(client):
    if client is None:
        return {a: 0.0 for a in ASSETS}, "offline -> assunto flat"
    pos, note = {}, "mainnet"
    for a in ASSETS:
        try:
            pos[a] = client.position_usd(INSTRUMENT[a])
        except Exception as e:
            pos[a], note = 0.0, f"read fallito ({type(e).__name__}) -> assunto flat"
    return pos, note


def _equity(client, marks):
    if client is None:
        return None, "offline"
    try:
        eq = float(client.account_summary("USDC").get("equity") or 0)
        if eq > 1:
            return eq, "mainnet USDC"
    except Exception:
        pass
    tot, any_ok = 0.0, False
    for a in ASSETS:
        try:
            eq = float(client.account_summary(a).get("equity") or 0)
            tot += eq * marks[a]
            any_ok = True
        except Exception:
            pass
    if any_ok and tot > 1:
        return tot, "mainnet coin-margined"
    return None, "conto flat / non finanziato"


def shadow_report(offline: bool = False, equity_override: float | None = None) -> dict:
    """Calcola lo stato shadow completo. NON invia nulla. Ritorna un dict serializzabile."""
    dfs = {a: resample_1d(load(a, "1h")) for a in ASSETS}
    tp = TrendPortfolio(**CANONICAL)
    targets = {a: tp.current_target(dfs[a]) for a in ASSETS}
    last_ts = min(int(dfs[a]["timestamp"].iloc[-1]) for a in ASSETS)

    client = None if offline else _safe_client()
    marks, marks_src = _marks(client, dfs)
    positions, pos_src = _positions(client)
    real_eq, eq_src = _equity(client, marks)

    paper = json.loads(PAPER_STATE.read_text()) if PAPER_STATE.exists() else None
    paper_cap = float(paper["capital"]) if paper else FALLBACK_CAPITAL
    paper_pos = paper.get("positions") if paper else None
    paper_ts = int(paper["last_ts"]) if paper else 0

    equity = equity_override if equity_override is not None else (real_eq if real_eq else paper_cap)
    eq_basis = ("override" if equity_override is not None
                else eq_src if real_eq else "paper capital (ipotetico: conto non finanziato)")

    assets, orders = [], []
    for a in ASSETS:
        inst = INSTRUMENT[a]
        order = build_rebalance_order(inst, targets[a], WEIGHT, equity, positions[a], price=marks[a])
        if order:
            orders.append(order)
        parity = None
        if paper_pos is not None:
            parity = abs(float(paper_pos.get(a, 0.0)) - targets[a]) < 1e-9
        assets.append(dict(
            asset=a, instrument=inst, target=targets[a],
            target_notional=WEIGHT * targets[a] * equity,
            position_usd=positions[a], mark=marks[a], mark_src=marks_src[a],
            order=order, paper=(float(paper_pos.get(a, 0.0)) if paper_pos else None), parity=parity,
        ))
    live_trades = []
    if client is not None:
        for a in ASSETS:
            try:
                for tr in client.trade_history(INSTRUMENT[a], limit=8):
                    live_trades.append(dict(
                        ts=int(tr.get("timestamp") or 0), instrument=INSTRUMENT[a],
                        direction=(tr.get("direction") or "").upper(),
                        amount=float(tr.get("amount") or 0), price=float(tr.get("price") or 0),
                        fee=float(tr.get("fee") or 0)))
            except Exception:
                pass
        live_trades.sort(key=lambda r: r["ts"], reverse=True)
        live_trades = live_trades[:12]

    disaster_sls = []
    if client is not None:
        for a in ASSETS:
            try:
                for o in client.open_orders(INSTRUMENT[a]):
                    if (o.get("label") or "") == DISASTER_LABEL:
                        disaster_sls.append(dict(
                            asset=a, instrument=INSTRUMENT[a],
                            amount=float(o.get("amount") or 0),
                            stop=float(o.get("trigger_price") or o.get("stop_price") or o.get("price") or 0)))
            except Exception:
                pass

    return dict(
        last_data=str(pd.Timestamp(last_ts, unit="ms", tz="UTC").date()),
        online=(client is not None and marks_src.get("BTC") == "mainnet"),
        real_equity=real_eq, equity=equity, eq_basis=eq_basis,
        pos_src=pos_src, assets=assets, orders=orders, live_trades=live_trades,
        disaster_sls=disaster_sls,
        flat=all(abs(targets[a]) < 1e-9 for a in ASSETS),
        paper_aligned=(paper_ts == last_ts),
    )
