"""Telemetry emitter: local SQLite sink, optional cloud drain."""

from __future__ import annotations

import logging
import sqlite3
import threading
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from llmhosts.telemetry.events import PerformanceEvent, RoutingEvent

logger = logging.getLogger(__name__)

_DEFAULT_DB = Path.home() / ".llmhosts" / "telemetry.db"
DEFAULT_TELEMETRY_DB: Path = _DEFAULT_DB  # Public alias for _DEFAULT_DB
_SCHEMA = """
CREATE TABLE IF NOT EXISTS routing_events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    query_hash TEXT NOT NULL,
    tier_used TEXT NOT NULL,
    model TEXT NOT NULL,
    latency_ms REAL,
    input_tokens INTEGER,
    output_tokens INTEGER,
    cache_hit INTEGER,
    privacy_level TEXT,
    node_id TEXT,
    timestamp TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_routing_events_timestamp ON routing_events (timestamp);
CREATE TABLE IF NOT EXISTS performance_events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    model_id TEXT NOT NULL,
    quantization TEXT NOT NULL DEFAULT 'unknown',
    gpu_model TEXT NOT NULL,
    vram_total_mb INTEGER NOT NULL DEFAULT 0,
    vram_used_mb INTEGER NOT NULL DEFAULT 0,
    tokens_per_second REAL NOT NULL,
    first_token_latency_ms REAL NOT NULL,
    context_length_tokens INTEGER NOT NULL DEFAULT 0,
    success INTEGER NOT NULL DEFAULT 1,
    created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_performance_events_gpu_model ON performance_events (gpu_model);
CREATE INDEX IF NOT EXISTS idx_performance_events_model_id ON performance_events (model_id);
"""


class TelemetryEmitter:
    """Thread-safe telemetry emitter with local SQLite storage."""

    def __init__(self, db_path: Path | None = None, enabled: bool = True) -> None:
        self._enabled = enabled
        self._db_path = db_path or _DEFAULT_DB
        self._lock = threading.Lock()
        if self._enabled:
            try:
                self._init_db()
            except Exception as exc:
                # If DB setup fails (e.g. bad path, permissions), silently disable
                logger.debug("Telemetry DB init failed; disabling telemetry: %s", exc)
                self._enabled = False

    def _init_db(self) -> None:
        self._db_path.parent.mkdir(parents=True, exist_ok=True)
        with sqlite3.connect(self._db_path) as conn:
            conn.executescript(_SCHEMA)

    def emit(self, event: RoutingEvent) -> None:
        """Store a routing event. Non-blocking; silently swallows errors."""
        if not self._enabled:
            return
        try:
            with self._lock, sqlite3.connect(self._db_path) as conn:
                conn.execute(
                    """INSERT INTO routing_events
                        (query_hash, tier_used, model, latency_ms, input_tokens,
                         output_tokens, cache_hit, privacy_level, node_id, timestamp)
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                    (
                        event.query_hash,
                        event.tier_used.value if hasattr(event.tier_used, "value") else event.tier_used,
                        event.model,
                        event.latency_ms,
                        event.input_tokens,
                        event.output_tokens,
                        int(event.cache_hit),
                        event.privacy_level,
                        event.node_id,
                        event.timestamp,
                    ),
                )
        except Exception as exc:
            logger.debug("Telemetry emit failed (non-fatal): %s", exc)

    def emit_performance(self, event: PerformanceEvent) -> None:
        """Store a GPU performance event. Non-blocking; silently swallows errors."""
        if not self._enabled:
            return
        try:
            with self._lock, sqlite3.connect(self._db_path) as conn:
                conn.execute(
                    """INSERT INTO performance_events
                        (model_id, quantization, gpu_model, vram_total_mb, vram_used_mb,
                         tokens_per_second, first_token_latency_ms, context_length_tokens,
                         success, created_at)
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                    (
                        event.model_id,
                        event.quantization,
                        event.gpu_model,
                        event.vram_total_mb,
                        event.vram_used_mb,
                        event.tokens_per_second,
                        event.first_token_latency_ms,
                        event.context_length_tokens,
                        int(event.success),
                        event.timestamp.isoformat(),
                    ),
                )
        except Exception as exc:
            logger.debug("Performance telemetry emit failed (non-fatal): %s", exc)

    def status(self) -> dict:
        """Return current telemetry stats."""
        if not self._enabled:
            return {"enabled": False, "total_events": 0}
        try:
            with sqlite3.connect(self._db_path) as conn:
                row = conn.execute("SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM routing_events").fetchone()
                return {
                    "enabled": True,
                    "total_events": row[0],
                    "oldest_event": row[1],
                    "newest_event": row[2],
                    "db_path": str(self._db_path),
                }
        except Exception:
            return {"enabled": True, "total_events": 0, "error": "db_unavailable"}

    def recent_events(self, limit: int = 20) -> list[dict]:
        """Return most recent events (for CLI status display)."""
        if not self._enabled:
            return []
        try:
            with sqlite3.connect(self._db_path) as conn:
                conn.row_factory = sqlite3.Row
                rows = conn.execute("SELECT * FROM routing_events ORDER BY timestamp DESC LIMIT ?", (limit,)).fetchall()
                return [dict(row) for row in rows]
        except Exception:
            return []
