"""Rich console output for paper trading status, positions, trades, and equity."""
from __future__ import annotations

import datetime as dt
from typing import Any

from rich import box
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.text import Text

from apps.paper_trader.alpaca_broker import AlpacaBroker, Position
from apps.paper_trader.state import SessionRow, StateManager, StrategyStateRow

_console = Console(width=140)


def _pnl_color(val: float) -> str:
    return "green" if val >= 0 else "red"


def _fmt_pct(val: float) -> str:
    sign = "+" if val >= 0 else ""
    return f"{sign}{val:.2f}%"


def _fmt_pnl(val: float) -> str:
    sign = "+" if val >= 0 else ""
    return f"{sign}${val:,.2f}"


# ------------------------------------------------------------------ #
# Session status
# ------------------------------------------------------------------ #

def print_status(
    session: SessionRow,
    broker: AlpacaBroker,
    state: StateManager,
) -> None:
    account = broker.get_account()
    session_st = state.get_session_state(session.session_id)
    snapshots = state.list_snapshots(session.session_id)

    initial_equity = session.initial_equity
    # Use latest snapshot equity if available, otherwise fall back to initial
    if snapshots:
        current_equity = snapshots[-1]["equity"]
    else:
        current_equity = initial_equity
    total_pnl = current_equity - initial_equity
    total_pnl_pct = total_pnl / initial_equity * 100 if initial_equity else 0.0
    peak_equity = state.get_peak_equity(session.session_id, initial_equity)
    drawdown_pct = max(0.0, (peak_equity - current_equity) / peak_equity * 100) if peak_equity > 0 else 0.0

    positions = broker.list_positions()
    kill_status = "[bold red]ON[/]" if session_st.kill_switch_triggered else "[green]OFF[/]"
    cooldown_str = f"{session_st.cooldown_remaining}d" if session_st.cooldown_remaining > 0 else "none"

    pnl_style = _pnl_color(total_pnl)

    _console.print()
    _console.print(Panel(
        f"[bold cyan]Session:[/] {session.session_name}  "
        f"[dim]|[/]  [bold]Config:[/] {session.config_path}  "
        f"[dim]|[/]  [bold]Status:[/] {session.status}",
        border_style="cyan",
        padding=(0, 1),
    ))

    tbl = Table(box=box.SIMPLE, show_header=False, padding=(0, 2))
    tbl.add_column("Key", style="bold yellow", no_wrap=True)
    tbl.add_column("Value", no_wrap=True)

    tbl.add_row("Equity", f"[bold]${current_equity:,.2f}[/]  [{pnl_style}]{_fmt_pnl(total_pnl)} ({_fmt_pct(total_pnl_pct)})[/]")
    tbl.add_row("Cash", f"${account.cash:,.2f}")
    tbl.add_row("Market Value", f"${account.long_market_value:,.2f}")
    tbl.add_row("Open Positions", str(len(positions)))
    tbl.add_row("Drawdown", f"[{'red' if drawdown_pct > 5 else 'green'}]{drawdown_pct:.2f}%[/]")
    tbl.add_row("Consecutive Losses", str(session_st.consecutive_losses))
    tbl.add_row("Cooldown", cooldown_str)
    tbl.add_row("Kill Switch", kill_status)
    tbl.add_row("Total Trades", str(len(state.list_trades(session.session_id))))
    if snapshots:
        tbl.add_row("Last Processed", snapshots[-1]["date"])

    _console.print(tbl)
    _console.print()


# ------------------------------------------------------------------ #
# Positions
# ------------------------------------------------------------------ #

def print_positions(
    session: SessionRow,
    broker: AlpacaBroker,
    state: StateManager,
) -> None:
    positions = broker.list_positions()
    strategy_states = {
        ss.symbol: ss
        for ss in state.get_open_strategy_states(session.session_id)
    }

    if not positions:
        _console.print("[dim]No open positions.[/]")
        return

    tbl = Table(
        box=box.SIMPLE_HEAD,
        show_header=True,
        header_style="bold yellow",
        padding=(0, 1),
        title=f"[bold cyan]Open Positions[/] — {session.session_name}",
        title_justify="left",
    )
    tbl.add_column("Symbol", style="bold", no_wrap=True)
    tbl.add_column("Qty", justify="right")
    tbl.add_column("Entry", justify="right")
    tbl.add_column("Current", justify="right")
    tbl.add_column("P&L $", justify="right")
    tbl.add_column("P&L %", justify="right")
    tbl.add_column("Days", justify="right")
    tbl.add_column("Stop", justify="right")
    tbl.add_column("Target", justify="right")

    for pos in sorted(positions, key=lambda p: p.symbol):
        ss = strategy_states.get(pos.symbol)
        stop_str = f"${ss.current_stop:.2f}" if ss else "-"
        target_str = f"${ss.target_price:.2f}" if ss else "-"
        days_str = str(ss.days_held) if ss else "-"

        pnl = pos.unrealized_pl
        pnl_pct = pnl / (pos.avg_entry_price * pos.qty) * 100 if pos.avg_entry_price and pos.qty else 0.0
        pnl_color = _pnl_color(pnl)

        tbl.add_row(
            pos.symbol,
            str(pos.qty),
            f"${pos.avg_entry_price:.2f}",
            f"${pos.current_price:.2f}",
            f"[{pnl_color}]{_fmt_pnl(pnl)}[/]",
            f"[{pnl_color}]{_fmt_pct(pnl_pct)}[/]",
            days_str,
            stop_str,
            target_str,
        )

    _console.print()
    _console.print(tbl)
    _console.print()


# ------------------------------------------------------------------ #
# Trades
# ------------------------------------------------------------------ #

def print_trades(
    session: SessionRow,
    state: StateManager,
    last: int | None = None,
) -> None:
    trades = state.list_trades(session.session_id, limit=last)

    if not trades:
        _console.print("[dim]No trades recorded yet.[/]")
        return

    tbl = Table(
        box=box.SIMPLE_HEAD,
        show_header=True,
        header_style="bold yellow",
        padding=(0, 1),
        title=f"[bold cyan]Trades[/] — {session.session_name}",
        title_justify="left",
    )
    tbl.add_column("Symbol", style="bold", no_wrap=True)
    tbl.add_column("Entry Date", no_wrap=True)
    tbl.add_column("Exit Date", no_wrap=True)
    tbl.add_column("Entry $", justify="right")
    tbl.add_column("Exit $", justify="right")
    tbl.add_column("Shares", justify="right")
    tbl.add_column("Net P&L", justify="right")
    tbl.add_column("R", justify="right")
    tbl.add_column("Days", justify="right")
    tbl.add_column("Reason", style="dim")

    for t in trades:
        pnl = t.get("net_pnl") or 0.0
        r = t.get("r_multiple") or 0.0
        pnl_color = _pnl_color(pnl)
        r_color = _pnl_color(r)

        tbl.add_row(
            str(t.get("symbol", "")),
            str(t.get("entry_date", "-")),
            str(t.get("exit_date", "-")),
            f"${t.get('entry_price') or 0:.2f}",
            f"${t.get('exit_price') or 0:.2f}",
            str(t.get("shares", "-")),
            f"[{pnl_color}]{_fmt_pnl(pnl)}[/]",
            f"[{r_color}]{r:+.2f}R[/]",
            str(t.get("holding_days", "-")),
            str(t.get("exit_reason", "-")),
        )

    _console.print()
    _console.print(tbl)
    _console.print()


# ------------------------------------------------------------------ #
# Equity curve (ASCII sparkline)
# ------------------------------------------------------------------ #

def print_equity(
    session: SessionRow,
    state: StateManager,
) -> None:
    snapshots = state.list_snapshots(session.session_id)

    if not snapshots:
        _console.print("[dim]No equity history yet.[/]")
        return

    tbl = Table(
        box=box.SIMPLE_HEAD,
        show_header=True,
        header_style="bold yellow",
        padding=(0, 1),
        title=f"[bold cyan]Equity Curve[/] — {session.session_name}",
        title_justify="left",
    )
    tbl.add_column("Date", no_wrap=True)
    tbl.add_column("Equity", justify="right")
    tbl.add_column("Daily P&L", justify="right")
    tbl.add_column("Total P&L", justify="right")
    tbl.add_column("Drawdown", justify="right")
    tbl.add_column("Positions", justify="right")

    for snap in snapshots:
        equity = snap.get("equity", 0.0)
        daily_pnl = snap.get("daily_pnl") or 0.0
        total_pnl = snap.get("total_pnl") or 0.0
        dd = snap.get("drawdown_pct") or 0.0
        n_pos = snap.get("open_position_count")

        daily_color = _pnl_color(daily_pnl)
        total_color = _pnl_color(total_pnl)
        dd_color = "red" if dd > 5 else "green"

        tbl.add_row(
            str(snap.get("date", "-")),
            f"${equity:,.2f}",
            f"[{daily_color}]{_fmt_pnl(daily_pnl)}[/]",
            f"[{total_color}]{_fmt_pnl(total_pnl)}[/]",
            f"[{dd_color}]{dd:.2f}%[/]",
            str(n_pos) if n_pos is not None else "-",
        )

    _console.print()
    _console.print(tbl)
    _console.print()


# ------------------------------------------------------------------ #
# Sessions list
# ------------------------------------------------------------------ #

def print_sessions(sessions: list[SessionRow]) -> None:
    if not sessions:
        _console.print("[dim]No sessions found.[/]")
        return

    tbl = Table(
        box=box.SIMPLE_HEAD,
        show_header=True,
        header_style="bold yellow",
        padding=(0, 1),
        title="[bold cyan]Paper Trading Sessions[/]",
        title_justify="left",
    )
    tbl.add_column("ID", style="dim", no_wrap=True)
    tbl.add_column("Name", style="bold", no_wrap=True)
    tbl.add_column("Config", no_wrap=True)
    tbl.add_column("Capital", justify="right")
    tbl.add_column("Status", no_wrap=True)
    tbl.add_column("Created", no_wrap=True)

    for s in sessions:
        status_color = "green" if s.status == "active" else "dim"
        tbl.add_row(
            s.session_id,
            s.session_name,
            s.config_path,
            f"${s.initial_equity:,.0f}",
            f"[{status_color}]{s.status}[/]",
            s.created_at[:10],
        )

    _console.print()
    _console.print(tbl)
    _console.print()


# ------------------------------------------------------------------ #
# Run summary
# ------------------------------------------------------------------ #

def print_backtest_results(results: list[dict], output_dir: str | None = None, show_trades: bool = True) -> None:
    """Print equity curve comparison, summary table, and per-strategy trade logs."""
    import csv
    import os

    if not results:
        _console.print("[dim]No backtest results.[/]")
        return

    # ── Summary table ──────────────────────────────────────────────────────
    sum_tbl = Table(
        box=box.SIMPLE_HEAD,
        show_header=True,
        header_style="bold yellow",
        padding=(0, 1),
        title="[bold cyan]Summary[/]",
        title_justify="left",
    )
    sum_tbl.add_column("Strategy", style="bold", no_wrap=True)
    sum_tbl.add_column("Return", justify="right")
    sum_tbl.add_column("MaxDD", justify="right")
    sum_tbl.add_column("Trades", justify="right")
    sum_tbl.add_column("WinRate", justify="right")
    sum_tbl.add_column("Sharpe", justify="right")

    for r in results:
        s = r["summary"]
        ret_color = "green" if s["return_pct"] >= 0 else "red"
        name = r["session_name"]
        sum_tbl.add_row(
            name,
            f"[{ret_color}]{s['return_pct']:+.2f}%[/{ret_color}]",
            f"[red]-{s['max_dd_pct']:.2f}%[/]",
            str(s["trade_count"]),
            f"{s['win_rate']:.0f}%" if s.get("win_rate") else "-",
            f"{s['sharpe']:+.2f}",
        )

    _console.print(sum_tbl)

    # ── Per-strategy trade logs ────────────────────────────────────────────
    if not show_trades:
        if output_dir:
            import csv as _csv
            os.makedirs(output_dir, exist_ok=True)
            for r in results:
                name = r["session_name"]
                eq_path = os.path.join(output_dir, f"{name}_equity.csv")
                with open(eq_path, "w", newline="") as f:
                    w = _csv.DictWriter(f, fieldnames=["date", "equity"])
                    w.writeheader()
                    for row in r["equity_curve"]:
                        w.writerow({"date": row["date"].isoformat(), "equity": row["equity"]})
                trades_path = os.path.join(output_dir, f"{name}_trades.csv")
                if r.get("trades"):
                    with open(trades_path, "w", newline="") as f:
                        fieldnames = ["symbol", "event_type", "score", "entry_date", "exit_date", "shares", "entry_price", "exit_price", "pnl", "reason"]
                        w = _csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
                        w.writeheader()
                        w.writerows(r["trades"])
            _console.print(f"\n[dim]Results saved to {output_dir}[/]")
        return

    for r in results:
        trades = r.get("trades", [])
        if not trades:
            _console.print(f"  [dim]{r['session_name']}: no completed trades[/]")
            continue

        tbl = Table(
            box=box.SIMPLE_HEAD,
            show_header=True,
            header_style="bold yellow",
            padding=(0, 1),
            title=f"[bold cyan]Trades — {r['session_name']}[/]",
            title_justify="left",
        )
        tbl.add_column("Symbol", style="bold", no_wrap=True)
        tbl.add_column("EventType", no_wrap=True)
        tbl.add_column("Score", justify="right")
        tbl.add_column("Entry", no_wrap=True)
        tbl.add_column("Exit", no_wrap=True)
        tbl.add_column("Days", justify="right")
        tbl.add_column("Shares", justify="right")
        tbl.add_column("EntryPx", justify="right")
        tbl.add_column("ExitPx", justify="right")
        tbl.add_column("P&L", justify="right")
        tbl.add_column("Reason", style="dim")

        for t in sorted(trades, key=lambda x: x.get("entry_date", "")):
            pnl = t.get("pnl", 0.0)
            color = _pnl_color(pnl)

            entry_d = t.get("entry_date", "")
            exit_d = t.get("exit_date", "")
            try:
                import datetime as _dt
                holding_days = (_dt.date.fromisoformat(str(exit_d)) - _dt.date.fromisoformat(str(entry_d))).days if entry_d and exit_d else "-"
            except Exception:
                holding_days = "-"

            score = t.get("score")
            score_str = f"{score:.2f}" if score is not None else "-"
            entry_px = t.get("entry_price")
            exit_px = t.get("exit_price")
            entry_px_str = f"${entry_px:.2f}" if entry_px is not None else "-"
            exit_px_str = f"${exit_px:.2f}" if exit_px is not None else "-"

            tbl.add_row(
                t.get("symbol", ""),
                t.get("event_type", "-"),
                score_str,
                str(entry_d) if entry_d else "-",
                str(exit_d) if exit_d else "-",
                str(holding_days),
                str(t.get("shares", "-")),
                entry_px_str,
                exit_px_str,
                f"[{color}]{_fmt_pnl(pnl)}[/{color}]",
                t.get("reason", "-"),
            )
        _console.print(tbl)

    # ── Optional CSV save ──────────────────────────────────────────────────
    if output_dir:
        os.makedirs(output_dir, exist_ok=True)
        for r in results:
            name = r["session_name"]

            eq_path = os.path.join(output_dir, f"{name}_equity.csv")
            with open(eq_path, "w", newline="") as f:
                w = csv.DictWriter(f, fieldnames=["date", "equity"])
                w.writeheader()
                for row in r["equity_curve"]:
                    w.writerow({"date": row["date"].isoformat(), "equity": row["equity"]})

            trades_path = os.path.join(output_dir, f"{name}_trades.csv")
            if r.get("trades"):
                with open(trades_path, "w", newline="") as f:
                    fieldnames = ["symbol", "event_type", "score", "entry_date", "exit_date", "shares", "entry_price", "exit_price", "pnl", "reason"]
                    w = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
                    w.writeheader()
                    w.writerows(r["trades"])

        _console.print(f"\n[dim]Results saved to {output_dir}[/]")


def print_run_summary(summary: dict) -> None:
    date = summary.get("date", "-")
    status = summary.get("status", "-")

    if status in ("already_processed", "non_trading_day"):
        _console.print(f"[dim]{date}: {status}[/]")
        return

    if status == "kill_switch_active":
        _console.print(f"[bold red]{date}: KILL SWITCH ACTIVE — all trading halted[/]")
        return

    _console.print(f"\nProcessing [bold]{date}[/]...")

    # Reconciliation report
    recon = summary.get("reconciliation")
    if recon and recon.has_issues:
        _console.print("  [bold yellow]RECONCILIATION:[/]")
        for sym in recon.orphaned_alpaca:
            _console.print(f"    [yellow]ORPHANED[/] {sym} — on Alpaca but no local state")
        for sym in recon.ghost_local:
            _console.print(f"    [yellow]GHOST[/] {sym} — local state but no Alpaca position (auto-closed)")
        if recon.stale_orders_cancelled:
            _console.print(f"    [dim]Stale orders cancelled: {len(recon.stale_orders_cancelled)}[/]")

    exits = summary.get("exits", [])
    entries = summary.get("entries", [])
    rejected = summary.get("rejected", [])
    candidates_detected = summary.get("candidates_detected", None)
    account = summary.get("account", {})

    # Exits
    if exits:
        _console.print("  [bold]EXITS:[/]")
        for e in exits:
            pnl = e.get("pnl", 0.0)
            r = e.get("r_multiple", 0.0)
            color = _pnl_color(pnl)
            _console.print(
                f"    [{color}]{e['symbol']}[/] — {e['reason']}  "
                f"P&L: [{color}]{_fmt_pnl(pnl)}[/]  R: [{color}]{r:+.2f}R[/]"
            )
    else:
        _console.print("  [dim]EXITS: none[/]")

    # Candidates detected
    if candidates_detected is not None:
        if candidates_detected == 0:
            _console.print("  [dim]CANDIDATES: 0 events in DB for this date[/]")
        else:
            _console.print(f"  [dim]CANDIDATES: {candidates_detected} events detected from DB[/]")

    # Entries
    if entries:
        _console.print("  [bold]ENTRIES:[/]")
        for e in entries:
            _console.print(
                f"    [green]{e['symbol']}[/] ({e['event_type']}, score={e['score']:.2f}) "
                f"-> BUY {e['shares']} shares  stop=${e['stop']:.2f}  target=${e['target']:.2f}"
            )
    else:
        _console.print("  [dim]ENTRIES: none[/]")

    # Rejected (only if non-zero)
    if rejected:
        _console.print(f"  [dim]REJECTED: {len(rejected)} candidates[/]")

    # Account summary
    if account:
        pnl = account.get("total_pnl", 0.0)
        color = _pnl_color(pnl)
        _console.print(
            f"\n  Equity: [bold]${account.get('equity', 0):,.2f}[/]  "
            f"Total P&L: [{color}]{_fmt_pnl(pnl)}[/]  "
            f"Drawdown: {account.get('drawdown_pct', 0):.2f}%"
        )
    _console.print()
