# src/paper/state_store.py
"""
State Store (SQLite)

Responsabilidades:
- Persistir estado del paper trading (broker snapshot, equity curve)
- Persistir trades ejecutados
- Ser la source of truth para la API (read-only)
"""

import sqlite3
import json
from pathlib import Path
from datetime import datetime
from typing import List, Dict, Any, Optional


class StateStore:
    def __init__(self, db_path: str | Path):
        self.db_path = Path(db_path)
        self.db_path.parent.mkdir(parents=True, exist_ok=True)

        self.conn = sqlite3.connect(self.db_path, check_same_thread=False, timeout=30.0)
        self.conn.row_factory = sqlite3.Row

        self.conn.execute("PRAGMA journal_mode=WAL;")
        self.conn.execute("PRAGMA synchronous=NORMAL;")
        self.conn.execute("PRAGMA busy_timeout = 5000;")

        self._init_schema()

    # --------------------------------------------------
    # Schema
    # --------------------------------------------------

    def _init_schema(self):
        cur = self.conn.cursor()

        # Último snapshot del broker (histórico simple)
        cur.execute("""
        CREATE TABLE IF NOT EXISTS broker_snapshot (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            ts TEXT NOT NULL,
            data TEXT NOT NULL
        )
        """)

        # Trades ejecutados
        cur.execute("""
        CREATE TABLE IF NOT EXISTS trades (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            ts TEXT NOT NULL,
            data TEXT NOT NULL
        )
        """)

        # Equity curve (serie temporal)
        cur.execute("""
        CREATE TABLE IF NOT EXISTS equity_curve (
            ts TEXT PRIMARY KEY,
            equity REAL NOT NULL,
            cash REAL NOT NULL
        )
        """)

        # Loop state (estado del bot)
        cur.execute("""
        CREATE TABLE IF NOT EXISTS loop_state (
            key TEXT PRIMARY KEY,
            value TEXT NOT NULL,
            updated_at TEXT NOT NULL
        )
        """)

        self.conn.commit()

    # --------------------------------------------------
    # Broker snapshot
    # --------------------------------------------------

    def save_broker_snapshot(self, snapshot: Dict[str, Any]) -> None:
        """
        Guarda un snapshot completo del broker (JSON).
        """
        self.conn.execute(
            """
            INSERT INTO broker_snapshot (ts, data)
            VALUES (?, ?)
            """,
            (
                datetime.utcnow().isoformat(),
                json.dumps(snapshot),
            ),
        )
        self.conn.commit()

    def load_broker_snapshot(self) -> Optional[Dict[str, Any]]:
        cur = self.conn.execute(
            """
            SELECT data
            FROM broker_snapshot
            WHERE data IS NOT NULL
            ORDER BY ts DESC
            LIMIT 1
            """
        )
        row = cur.fetchone()

        if row is None or row["data"] is None:
            return None

        try:
            return json.loads(row["data"])
        except Exception:
            return None


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

    def append_trade(self, trade: Dict[str, Any]) -> None:
        """
        Guarda un tade ejecutado (JSON)
        """
        self.conn.execute(
            """
            INSERT INTO trades (ts, data)
            VALUES (?, ?)
            """,
            (
                datetime.utcnow().isoformat(),
                json.dumps(trade)
            )
        )
        self.conn.commit()

    def load_trades(self, limit: int = 100) -> List[Dict[str, Any]]:
        cur = self.conn.execute(
            """
            SELECT data
            FROM trades
            ORDER BY ts DESC
            LIMIT ?
            """,
            (limit,),
        )
        rows = cur.fetchall()

        return [json.loads(r["data"]) for r in rows[::-1]]

    # --------------------------------------------------
    # Equity curve
    # --------------------------------------------------

    def append_equity_point(
        self,
        ts: datetime,
        equity: float,
        cash: float,
    ) -> None:
        """
        Añade un punto a la equity curve.
        """
        self.conn.execute(
            """
            INSERT OR REPLACE INTO equity_curve (ts, equity, cash)
            VALUES (?, ?, ?)
            """,
            (ts.isoformat(), float(equity), float(cash)),
        )
        self.conn.commit()

    def load_equity_curve(
        self,
        from_ts: Optional[datetime] = None,
    ) -> Dict[str, List]:
        """
        Carga la equity curve completa o desde un timestamp dado.
        """
        if from_ts:
            cur = self.conn.execute(
                """
                SELECT ts, equity, cash
                FROM equity_curve
                WHERE ts >= ?
                ORDER BY ts ASC
                """,
                (from_ts.isoformat(),),
            )
        else:
            cur = self.conn.execute(
                """
                SELECT ts, equity, cash
                FROM equity_curve
                ORDER BY ts ASC
                """
            )

        rows = cur.fetchall()

        return {
            "timestamps": [r["ts"] for r in rows],
            "equity": [r["equity"] for r in rows],
            "cash": [r["cash"] for r in rows],
        }


    # --------------------------------------------------
    # Loop state
    # --------------------------------------------------

    def save_loop_state(self, state: str) -> None:
        """
        Guarda el estado actual del loop (RUNNING, PAUSED, ERROR, etc).
        """
        self.conn.execute(
            """
            INSERT OR REPLACE INTO loop_state (key, value, updated_at)
            VALUES ('state', ?, ?)
            """,
            (
                state,
                datetime.utcnow().isoformat(),
            ),
        )
        self.conn.commit()

    def load_loop_state(self) -> dict:
        """
        Devuelve el estado actual del loop.
        """
        cur = self.conn.execute(
            """
            SELECT value, updated_at
            FROM loop_state
            WHERE key = 'state'
            """
        )
        row = cur.fetchone()

        if row is None:
            return {
                "state": "INIT",
                "updated_at": None,
            }

        return {
            "state": row["value"],
            "updated_at": row["updated_at"],
        }