"""SQLite local state for Mesh agent — config, trades, device identity."""

import json
import sqlite3
import os
from pathlib import Path
from typing import Optional


DATA_DIR = Path.home() / ".tinkclaw"
DB_PATH = DATA_DIR / "mesh.db"


def _ensure_dir() -> None:
    DATA_DIR.mkdir(parents=True, exist_ok=True)


class MeshStore:
    """Local SQLite store for Mesh agent state."""

    def __init__(self, db_path: Optional[str] = None):
        _ensure_dir()
        self.db_path = db_path or str(DB_PATH)
        self._init_db()

    def _init_db(self) -> None:
        with self._conn() as conn:
            conn.executescript("""
                CREATE TABLE IF NOT EXISTS config (
                    key TEXT PRIMARY KEY,
                    value TEXT NOT NULL
                );
                CREATE TABLE IF NOT EXISTS trades (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    signal_id TEXT NOT NULL,
                    symbol TEXT NOT NULL,
                    direction TEXT NOT NULL,
                    confidence REAL NOT NULL,
                    price REAL,
                    rules_json TEXT,
                    executed_at TEXT NOT NULL,
                    synced INTEGER DEFAULT 0
                );
                CREATE TABLE IF NOT EXISTS signals (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    signal_id TEXT NOT NULL,
                    symbol TEXT NOT NULL,
                    direction TEXT NOT NULL,
                    confidence REAL NOT NULL,
                    accepted INTEGER NOT NULL,
                    reason TEXT,
                    received_at TEXT NOT NULL
                );
                CREATE INDEX IF NOT EXISTS idx_trades_synced ON trades(synced);
                CREATE INDEX IF NOT EXISTS idx_signals_received ON signals(received_at);
            """)

    def _conn(self) -> sqlite3.Connection:
        return sqlite3.connect(self.db_path)

    # ── Config ──

    def get(self, key: str, default: Optional[str] = None) -> Optional[str]:
        with self._conn() as conn:
            row = conn.execute(
                "SELECT value FROM config WHERE key = ?", (key,)
            ).fetchone()
            return row[0] if row else default

    def set(self, key: str, value: str) -> None:
        with self._conn() as conn:
            conn.execute(
                "INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)",
                (key, value),
            )

    def get_json(self, key: str, default=None):
        raw = self.get(key)
        if raw is None:
            return default
        return json.loads(raw)

    def set_json(self, key: str, value) -> None:
        self.set(key, json.dumps(value))

    # ── Device Identity ──

    @property
    def device_id(self) -> Optional[str]:
        return self.get("device_id")

    @device_id.setter
    def device_id(self, value: str) -> None:
        self.set("device_id", value)

    @property
    def auth_token(self) -> Optional[str]:
        return self.get("auth_token")

    @auth_token.setter
    def auth_token(self, value: str) -> None:
        self.set("auth_token", value)

    @property
    def api_url(self) -> str:
        return self.get("api_url", "https://api.tinkclaw.com")

    @api_url.setter
    def api_url(self, value: str) -> None:
        self.set("api_url", value)

    @property
    def stream_url(self) -> str:
        return self.get("stream_url", "wss://stream.tinkclaw.com")

    @stream_url.setter
    def stream_url(self, value: str) -> None:
        self.set("stream_url", value)

    # ── Mesh Config ──

    def get_mesh_config(self) -> dict:
        return self.get_json("mesh_config", {
            "strategy": "balanced",
            "confidence": 0.8,
            "risk_pct": 0.1,
            "symbols": [],
            "guardian_enabled": True,
        })

    def save_mesh_config(self, config: dict) -> None:
        self.set_json("mesh_config", config)

    # ── Trades ──

    def add_trade(
        self,
        signal_id: str,
        symbol: str,
        direction: str,
        confidence: float,
        price: Optional[float],
        rules: Optional[list],
        executed_at: str,
    ) -> int:
        with self._conn() as conn:
            cursor = conn.execute(
                """INSERT INTO trades
                   (signal_id, symbol, direction, confidence, price, rules_json, executed_at)
                   VALUES (?, ?, ?, ?, ?, ?, ?)""",
                (signal_id, symbol, direction, confidence, price,
                 json.dumps(rules) if rules else None, executed_at),
            )
            return cursor.lastrowid

    def get_unsynced_trades(self) -> list[dict]:
        with self._conn() as conn:
            conn.row_factory = sqlite3.Row
            rows = conn.execute(
                "SELECT * FROM trades WHERE synced = 0 ORDER BY executed_at"
            ).fetchall()
            return [dict(r) for r in rows]

    def mark_synced(self, trade_id: int) -> None:
        with self._conn() as conn:
            conn.execute("UPDATE trades SET synced = 1 WHERE id = ?", (trade_id,))

    def trade_count(self) -> int:
        with self._conn() as conn:
            row = conn.execute("SELECT COUNT(*) FROM trades").fetchone()
            return row[0]

    # ── Signal Log ──

    def log_signal(
        self,
        signal_id: str,
        symbol: str,
        direction: str,
        confidence: float,
        accepted: bool,
        reason: str,
        received_at: str,
    ) -> None:
        with self._conn() as conn:
            conn.execute(
                """INSERT INTO signals
                   (signal_id, symbol, direction, confidence, accepted, reason, received_at)
                   VALUES (?, ?, ?, ?, ?, ?, ?)""",
                (signal_id, symbol, direction, confidence,
                 1 if accepted else 0, reason, received_at),
            )

    # ── Cleanup ──

    def clear(self) -> None:
        with self._conn() as conn:
            conn.executescript("""
                DELETE FROM config;
                DELETE FROM trades;
                DELETE FROM signals;
            """)

    def is_activated(self) -> bool:
        return self.device_id is not None and self.auth_token is not None
