"""SQLite state management for TGTC paper trading."""
from __future__ import annotations

import os
import sqlite3
import uuid
from datetime import datetime, timezone
from pathlib import Path

from apps.tgtc_trader.models import (
    CREATE_TABLES_SQL,
    TGTCCandidateRow,
    TGTCDailySnapshotRow,
    TGTCPositionRow,
    TGTCSessionRow,
    TGTCSnapshotRow,
    TGTCTradeRow,
)

_DEFAULT_DB = "data/paper/tgtc.db"


class TGTCStateManager:
    """SQLite CRUD layer for TGTC paper trading state."""

    def __init__(self, db_path: str | None = None) -> None:
        self._db_path = db_path or os.environ.get("TGTC_TRADER_DB", _DEFAULT_DB)
        Path(self._db_path).parent.mkdir(parents=True, exist_ok=True)
        self._init_db()

    def _connect(self) -> sqlite3.Connection:
        conn = sqlite3.connect(self._db_path)
        conn.row_factory = sqlite3.Row
        conn.execute("PRAGMA journal_mode=WAL")
        conn.execute("PRAGMA foreign_keys=ON")
        return conn

    def _init_db(self) -> None:
        with self._connect() as conn:
            conn.executescript(CREATE_TABLES_SQL)

    # ── Sessions ──────────────────────────────────────────────────────────────

    def create_session(self, name: str, config_path: str, initial_equity: float) -> str:
        session_id = str(uuid.uuid4())[:8]
        now = datetime.now(timezone.utc).isoformat()
        with self._connect() as conn:
            conn.execute(
                """INSERT INTO tgtc_sessions (session_id, session_name, config_path, initial_equity, created_at)
                   VALUES (?, ?, ?, ?, ?)""",
                (session_id, name, config_path, initial_equity, now),
            )
        return session_id

    def get_session(self, name_or_id: str) -> TGTCSessionRow | None:
        with self._connect() as conn:
            row = conn.execute(
                "SELECT * FROM tgtc_sessions WHERE session_id=? OR session_name=?",
                (name_or_id, name_or_id),
            ).fetchone()
        return TGTCSessionRow(**dict(row)) if row else None

    def list_sessions(self) -> list[TGTCSessionRow]:
        with self._connect() as conn:
            rows = conn.execute("SELECT * FROM tgtc_sessions ORDER BY created_at DESC").fetchall()
        return [TGTCSessionRow(**dict(r)) for r in rows]

    def set_session_status(self, session_id: str, status: str) -> None:
        with self._connect() as conn:
            conn.execute("UPDATE tgtc_sessions SET status=? WHERE session_id=?", (status, session_id))

    def delete_session(self, session_id: str) -> None:
        with self._connect() as conn:
            for tbl in ("tgtc_snapshots", "tgtc_candidates", "tgtc_positions",
                        "tgtc_trades", "tgtc_daily_snapshots"):
                conn.execute(f"DELETE FROM {tbl} WHERE session_id=?", (session_id,))
            conn.execute("DELETE FROM tgtc_sessions WHERE session_id=?", (session_id,))

    def get_equity(self, session_id: str) -> float | None:
        with self._connect() as conn:
            row = conn.execute(
                """SELECT equity FROM tgtc_daily_snapshots
                   WHERE session_id=? ORDER BY date DESC LIMIT 1""",
                (session_id,),
            ).fetchone()
        return float(row["equity"]) if row else None

    # ── Snapshots ─────────────────────────────────────────────────────────────

    def save_snapshot_batch(self, rows: list[TGTCSnapshotRow]) -> None:
        with self._connect() as conn:
            conn.executemany(
                """INSERT OR IGNORE INTO tgtc_snapshots
                   (session_id, date, captured_at, symbol, rank, price, pct_change, volume, market_cap)
                   VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                [
                    (r.session_id, r.date, r.captured_at, r.symbol, r.rank,
                     r.price, r.pct_change, r.volume, r.market_cap)
                    for r in rows
                ],
            )

    def get_latest_snapshots(self, session_id: str, date: str,
                             limit_per_tick: int = 100) -> list[dict]:
        """Return latest tick snapshots sorted by rank."""
        with self._connect() as conn:
            latest_tick = conn.execute(
                """SELECT MAX(captured_at) FROM tgtc_snapshots
                   WHERE session_id=? AND date=?""",
                (session_id, date),
            ).fetchone()
            if not latest_tick or not latest_tick[0]:
                return []
            rows = conn.execute(
                """SELECT * FROM tgtc_snapshots
                   WHERE session_id=? AND date=? AND captured_at=?
                   ORDER BY rank ASC LIMIT ?""",
                (session_id, date, latest_tick[0], limit_per_tick),
            ).fetchall()
        return [dict(r) for r in rows]

    # ── Candidates ────────────────────────────────────────────────────────────

    def save_candidates(self, rows: list[TGTCCandidateRow]) -> None:
        with self._connect() as conn:
            conn.executemany(
                """INSERT OR REPLACE INTO tgtc_candidates
                   (session_id, date, symbol, score, rank_persistence, rank_velocity,
                    price_structure, volume_quality, relative_strength,
                    pct_change_at_10, price_at_10, vwap_at_10, above_vwap, decided_at, status)
                   VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                [
                    (r.session_id, r.date, r.symbol, r.score, r.rank_persistence,
                     r.rank_velocity, r.price_structure, r.volume_quality,
                     r.relative_strength, r.pct_change_at_10, r.price_at_10,
                     r.vwap_at_10, int(r.above_vwap), r.decided_at, r.status)
                    for r in rows
                ],
            )

    def get_candidates(self, session_id: str, date: str) -> list[dict]:
        with self._connect() as conn:
            rows = conn.execute(
                """SELECT * FROM tgtc_candidates
                   WHERE session_id=? AND date=? ORDER BY score DESC""",
                (session_id, date),
            ).fetchall()
        return [dict(r) for r in rows]

    def update_candidate_status(self, session_id: str, date: str,
                                symbol: str, status: str) -> None:
        with self._connect() as conn:
            conn.execute(
                """UPDATE tgtc_candidates SET status=?
                   WHERE session_id=? AND date=? AND symbol=?""",
                (status, session_id, date, symbol),
            )

    # ── Positions ─────────────────────────────────────────────────────────────

    def save_position(self, pos: TGTCPositionRow) -> None:
        with self._connect() as conn:
            conn.execute(
                """INSERT OR REPLACE INTO tgtc_positions
                   (session_id, date, symbol, entry_signal, entry_price, stop_price,
                    current_stop, shares, entered_at, peak_price, partial_taken,
                    be_stop_active, exit_price, exit_reason, exited_at, pnl, r_multiple,
                    is_dry_run, status)
                   VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                (
                    pos.session_id, pos.date, pos.symbol, pos.entry_signal,
                    pos.entry_price, pos.stop_price, pos.current_stop, pos.shares,
                    pos.entered_at, pos.peak_price, int(pos.partial_taken),
                    int(pos.be_stop_active), pos.exit_price, pos.exit_reason,
                    pos.exited_at, pos.pnl, pos.r_multiple, int(pos.is_dry_run),
                    pos.status,
                ),
            )

    def get_open_positions(self, session_id: str, date: str) -> list[TGTCPositionRow]:
        with self._connect() as conn:
            rows = conn.execute(
                """SELECT * FROM tgtc_positions
                   WHERE session_id=? AND date=? AND status='open'""",
                (session_id, date),
            ).fetchall()
        return [_row_to_position(r) for r in rows]

    def get_all_positions(self, session_id: str) -> list[dict]:
        with self._connect() as conn:
            rows = conn.execute(
                """SELECT * FROM tgtc_positions WHERE session_id=?
                   ORDER BY entered_at DESC LIMIT 200""",
                (session_id,),
            ).fetchall()
        return [dict(r) for r in rows]

    # ── Trades ────────────────────────────────────────────────────────────────

    def save_trade(self, trade: TGTCTradeRow) -> None:
        with self._connect() as conn:
            conn.execute(
                """INSERT OR IGNORE INTO tgtc_trades
                   (trade_id, session_id, date, symbol, entry_signal, entry_price,
                    exit_price, entered_at, exited_at, shares, pnl, r_multiple,
                    exit_reason, is_dry_run)
                   VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                (
                    trade.trade_id, trade.session_id, trade.date, trade.symbol,
                    trade.entry_signal, trade.entry_price, trade.exit_price,
                    trade.entered_at, trade.exited_at, trade.shares, trade.pnl,
                    trade.r_multiple, trade.exit_reason, int(trade.is_dry_run),
                ),
            )

    def get_trades(self, session_id: str, limit: int = 200) -> list[dict]:
        with self._connect() as conn:
            rows = conn.execute(
                """SELECT * FROM tgtc_trades WHERE session_id=?
                   ORDER BY exited_at DESC LIMIT ?""",
                (session_id, limit),
            ).fetchall()
        return [dict(r) for r in rows]

    # ── Daily snapshots ───────────────────────────────────────────────────────

    def save_daily_snapshot(self, snap: TGTCDailySnapshotRow) -> None:
        with self._connect() as conn:
            conn.execute(
                """INSERT OR REPLACE INTO tgtc_daily_snapshots
                   (session_id, date, equity, daily_pnl, total_pnl, trades_taken, phase)
                   VALUES (?, ?, ?, ?, ?, ?, ?)""",
                (snap.session_id, snap.date, snap.equity, snap.daily_pnl,
                 snap.total_pnl, snap.trades_taken, snap.phase),
            )

    def get_daily_snapshots(self, session_id: str, limit: int = 100) -> list[dict]:
        with self._connect() as conn:
            rows = conn.execute(
                """SELECT * FROM tgtc_daily_snapshots WHERE session_id=?
                   ORDER BY date ASC LIMIT ?""",
                (session_id, limit),
            ).fetchall()
        return [dict(r) for r in rows]

    def get_daily_state(self, session_id: str, date: str) -> TGTCDailySnapshotRow | None:
        with self._connect() as conn:
            row = conn.execute(
                "SELECT * FROM tgtc_daily_snapshots WHERE session_id=? AND date=?",
                (session_id, date),
            ).fetchone()
        if row is None:
            return None
        return TGTCDailySnapshotRow(**dict(row))

    def update_phase(self, session_id: str, date: str, phase: str) -> None:
        now_eq = self.get_equity(session_id) or 0.0
        with self._connect() as conn:
            conn.execute(
                """INSERT INTO tgtc_daily_snapshots (session_id, date, equity, phase)
                   VALUES (?, ?, ?, ?)
                   ON CONFLICT(session_id, date) DO UPDATE SET phase=excluded.phase""",
                (session_id, date, now_eq, phase),
            )


def _row_to_position(row: sqlite3.Row) -> TGTCPositionRow:
    d = dict(row)
    d["partial_taken"] = bool(d.get("partial_taken", 0))
    d["be_stop_active"] = bool(d.get("be_stop_active", 0))
    d["is_dry_run"] = bool(d.get("is_dry_run", 1))
    d.pop("id", None)
    return TGTCPositionRow(**d)
