"""Status page — engine health at a glance."""

from __future__ import annotations

import os
from pathlib import Path

import streamlit as st

from cerbero_bite.gui.data_layer import (
    DEFAULT_AUDIT_PATH,
    DEFAULT_DB_PATH,
    EngineSnapshot,
    enqueue_arm_kill,
    enqueue_disarm_kill,
    enqueue_run_cycle,
    humanize_age,
    humanize_dt,
    load_engine_snapshot,
    load_open_positions,
    load_pending_manual_actions,
)
from cerbero_bite.gui.live_data import BalancesSnapshot, fetch_balances_sync


def _resolve_paths() -> tuple[Path, Path]:
    db_path = Path(os.environ.get("CERBERO_BITE_GUI_DB", DEFAULT_DB_PATH))
    audit_path = Path(os.environ.get("CERBERO_BITE_GUI_AUDIT", DEFAULT_AUDIT_PATH))
    return db_path, audit_path


_HEALTH_COLORS = {
    "running": ("🟢", "success"),
    "degraded": ("🟡", "warning"),
    "killed": ("🔴", "error"),
    "stopped": ("⚫", "warning"),
    "unknown": ("⚪", "info"),
}

_TYPED_PHRASE = "confermo"


def _render_force_cycle_panel(db_path: Path) -> None:
    st.subheader("Forza ciclo")
    st.caption(
        "Accoda una richiesta di esecuzione immediata di un ciclo. Funziona "
        "solo se il motore è in esecuzione (`cerbero-bite start`); il job "
        "`manual_actions` consuma la coda ogni minuto."
    )
    cols = st.columns(4)
    if cols[0].button(
        "▶ Forza entry",
        use_container_width=True,
        help="Esegue subito una valutazione del ciclo entry.",
    ):
        aid = enqueue_run_cycle(cycle="entry", db_path=db_path)
        st.success(
            f"✅ ciclo entry accodato (id #{aid}). "
            "Il motore lo eseguirà entro ~1 minuto."
        )
    if cols[1].button(
        "🔍 Forza monitor",
        use_container_width=True,
        help="Esegue subito un giro del monitor sulle posizioni aperte.",
    ):
        aid = enqueue_run_cycle(cycle="monitor", db_path=db_path)
        st.success(f"✅ ciclo monitor accodato (id #{aid}).")
    if cols[2].button(
        "💓 Forza health",
        use_container_width=True,
        help="Esegue subito un health check completo.",
    ):
        aid = enqueue_run_cycle(cycle="health", db_path=db_path)
        st.success(f"✅ ciclo health accodato (id #{aid}).")
    if cols[3].button(
        "📐 Forza snapshot",
        use_container_width=True,
        help="Esegue subito una raccolta market_snapshot (alimenta Calibrazione).",
    ):
        aid = enqueue_run_cycle(cycle="market_snapshot", db_path=db_path)
        st.success(f"✅ snapshot accodato (id #{aid}).")


@st.cache_data(ttl=60, show_spinner=False)
def _cached_balances() -> BalancesSnapshot:
    """Fetch balances at most once per minute per Streamlit session."""
    return fetch_balances_sync(timeout_s=10.0)


def _render_balances_panel() -> None:
    st.subheader("Saldi exchange")

    refresh = st.button("🔄 Aggiorna saldi", help="Forza un nuovo fetch dagli MCP.")
    if refresh:
        _cached_balances.clear()

    try:
        snap = _cached_balances()
    except Exception as exc:
        st.error(
            f"Impossibile leggere i saldi: {type(exc).__name__}: {exc}"
        )
        return

    rows = []
    for r in snap.rows:
        rows.append(
            {
                "exchange": r.exchange,
                "valuta": r.currency,
                "equity": (
                    f"{float(r.equity):,.2f}"
                    if r.equity is not None
                    else "—"
                ),
                "disponibile": (
                    f"{float(r.available):,.2f}"
                    if r.available is not None
                    else "—"
                ),
                "P&L non realizzato": (
                    f"{float(r.unrealized_pnl):+.2f}"
                    if r.unrealized_pnl is not None
                    else "—"
                ),
                "errore": r.error or "",
            }
        )
    st.dataframe(rows, use_container_width=True, hide_index=True)

    cols = st.columns(3)
    cols[0].metric("Totale USD", f"${float(snap.total_usd()):,.2f}")
    eur = snap.total_eur()
    cols[1].metric(
        "Totale EUR",
        f"€{float(eur):,.2f}" if eur is not None else "—",
    )
    cols[2].metric(
        "Cambio EUR/USD",
        f"{float(snap.eur_usd_rate):.4f}"
        if snap.eur_usd_rate is not None
        else "—",
    )

    if snap.fx_error:
        st.warning(f"FX non disponibile: {snap.fx_error}")
    age = (
        f" · letti {humanize_dt(snap.fetched_at)}"
        if snap.fetched_at is not None
        else ""
    )
    st.caption(
        f"Cache TTL 60s · saldi letti dal gateway MCP{age}"
    )


def _render_kill_switch_panel(db_path: Path, snap: EngineSnapshot) -> None:
    st.subheader("Comandi kill switch")

    if snap.kill_switch_armed:
        st.warning(
            "Kill switch **armato**. Disarmandolo viene accodata una "
            "azione `disarm_kill`; il consumer del motore la applica al "
            "prossimo tick di un minuto e la transizione viene registrata "
            "nella catena audit."
        )
        with st.form("kill_disarm_form", clear_on_submit=True):
            reason = st.text_input(
                "Motivo (obbligatorio)",
                placeholder="es. finestra macro superata",
            )
            confirm = st.text_input(
                f"Scrivi `{_TYPED_PHRASE}` per confermare",
                placeholder=_TYPED_PHRASE,
            )
            submitted = st.form_submit_button(
                "🟢 Accoda disarmo",
                type="primary",
                use_container_width=True,
            )
            if submitted:
                if confirm.strip() != _TYPED_PHRASE:
                    st.error(
                        f"Scrivi esattamente `{_TYPED_PHRASE}` per confermare."
                    )
                elif not reason.strip():
                    st.error("Il motivo è obbligatorio.")
                else:
                    aid = enqueue_disarm_kill(reason=reason, db_path=db_path)
                    st.success(
                        f"✅ disarmo accodato (id #{aid}). "
                        "Il motore lo applicherà entro ~1 minuto."
                    )
    else:
        st.info(
            "Kill switch **disarmato**. Armandolo viene accodata una "
            "azione `arm_kill`; il consumer del motore la applica al "
            "prossimo tick di un minuto."
        )
        with st.form("kill_arm_form", clear_on_submit=True):
            reason = st.text_input(
                "Motivo (obbligatorio)",
                placeholder="es. shock macro — sospendi trading",
            )
            confirm = st.text_input(
                f"Scrivi `{_TYPED_PHRASE}` per confermare",
                placeholder=_TYPED_PHRASE,
            )
            submitted = st.form_submit_button(
                "🔴 Accoda armamento",
                type="secondary",
                use_container_width=True,
            )
            if submitted:
                if confirm.strip() != _TYPED_PHRASE:
                    st.error(
                        f"Scrivi esattamente `{_TYPED_PHRASE}` per confermare."
                    )
                elif not reason.strip():
                    st.error("Il motivo è obbligatorio.")
                else:
                    aid = enqueue_arm_kill(reason=reason, db_path=db_path)
                    st.success(
                        f"✅ armamento accodato (id #{aid}). "
                        "Il motore lo applicherà entro ~1 minuto."
                    )


def render() -> None:
    st.title("📊 Stato")
    st.caption(
        "Salute del motore, kill switch, posizioni aperte e ancora audit."
    )

    db_path, _ = _resolve_paths()
    snap = load_engine_snapshot(db_path=db_path)

    icon, level = _HEALTH_COLORS.get(snap.health, ("⚪", "info"))
    banner = f"{icon} **{snap.health_label}**"
    if level == "success":
        st.success(banner)
    elif level == "warning":
        st.warning(banner)
    elif level == "error":
        st.error(banner)
    else:
        st.info(banner)

    if snap.kill_switch_armed:
        st.error(
            f"**Kill switch armato** — il motore rifiuterà nuove entrate.\n\n"
            f"- motivo: `{snap.kill_reason or '—'}`\n"
            f"- da: `{humanize_dt(snap.kill_at)}`"
        )

    # Top metrics
    cols = st.columns(4)
    cols[0].metric("Posizioni aperte", snap.open_positions)
    cols[1].metric(
        "Ultimo health check", humanize_age(snap.last_health_check_age_s)
    )
    cols[2].metric("Avviato il", humanize_dt(snap.started_at))
    cols[3].metric("Versione config", snap.config_version or "—")

    st.divider()

    # Saldi exchange (live MCP fetch, TTL 60s)
    _render_balances_panel()

    st.divider()

    # Forza ciclo
    _render_force_cycle_panel(db_path)

    st.divider()

    # Kill switch controls
    _render_kill_switch_panel(db_path, snap)

    st.divider()

    # Azioni manuali pendenti
    pending = load_pending_manual_actions(db_path=db_path)
    if pending:
        st.subheader("Azioni manuali pendenti")
        st.caption(
            "Accodate da questo cruscotto, non ancora consumate. Il motore "
            "drena la coda ogni minuto tramite il job `manual_actions`."
        )
        rows_pending = [
            {
                "id": a.id,
                "tipo": a.kind,
                "payload": a.payload_json or "",
                "creata il": humanize_dt(a.created_at),
            }
            for a in pending
        ]
        st.dataframe(rows_pending, use_container_width=True, hide_index=True)
        st.divider()

    # Ancora audit
    st.subheader("Ancora audit")
    if snap.last_audit_hash is None:
        st.info("Nessuna ancora registrata.")
    else:
        short = (
            f"{snap.last_audit_hash[:12]}…{snap.last_audit_hash[-12:]}"
            if len(snap.last_audit_hash) > 24
            else snap.last_audit_hash
        )
        st.code(short, language="text")
        st.caption(
            "Ultima testa della catena hash persistita in "
            "`system_state.last_audit_hash`. All'avvio l'orchestrator la "
            "confronta con la coda del file audit; un mismatch arma il "
            "kill switch (CRITICAL)."
        )

    st.divider()

    # Tabella posizioni aperte
    st.subheader("Posizioni aperte")
    positions = load_open_positions(db_path=db_path)
    if not positions:
        st.info("Nessuna posizione aperta.")
    else:
        rows = [
            {
                "proposal_id": str(p.proposal_id)[:8],
                "spread": p.spread_type,
                "asset": p.asset,
                "n. contratti": p.n_contracts,
                "credito (USD)": f"{p.credit_usd:.2f}",
                "max perdita (USD)": f"{p.max_loss_usd:.2f}",
                "strike short": f"{p.short_strike}",
                "strike long": f"{p.long_strike}",
                "stato": p.status,
                "aperta il": humanize_dt(p.opened_at),
                "scadenza": humanize_dt(p.expiry),
            }
            for p in positions
        ]
        st.dataframe(rows, use_container_width=True)


render()
