"""Compliance audit log persistence (SQLite, append-only)."""

from __future__ import annotations

import json
import logging
import sqlite3
import threading
from typing import Any

from horizon.compliance._types import (
    ApprovalRequest, ApprovalStatus, AuditEvent, AuditEventType,
    SurveillanceAlert,
)

logger = logging.getLogger("horizon.compliance.store")

_SCHEMA = """
CREATE TABLE IF NOT EXISTS audit_events (
    event_id TEXT PRIMARY KEY,
    event_type TEXT NOT NULL,
    timestamp REAL NOT NULL,
    actor TEXT NOT NULL,
    market_id TEXT,
    order_id TEXT,
    exchange TEXT,
    details_json TEXT NOT NULL,
    prev_hash TEXT,
    event_hash TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_audit_ts ON audit_events(timestamp);
CREATE INDEX IF NOT EXISTS idx_audit_type ON audit_events(event_type);
CREATE INDEX IF NOT EXISTS idx_audit_market ON audit_events(market_id);

CREATE TABLE IF NOT EXISTS surveillance_alerts (
    alert_id TEXT PRIMARY KEY,
    alert_type TEXT NOT NULL,
    severity TEXT NOT NULL,
    timestamp REAL NOT NULL,
    market_id TEXT NOT NULL,
    description TEXT NOT NULL,
    evidence_json TEXT NOT NULL,
    resolved INTEGER NOT NULL DEFAULT 0,
    resolution_notes TEXT DEFAULT '',
    resolved_by TEXT,
    resolved_at REAL
);
CREATE INDEX IF NOT EXISTS idx_alert_ts ON surveillance_alerts(timestamp);

CREATE TABLE IF NOT EXISTS approval_requests (
    request_id TEXT PRIMARY KEY,
    order_json TEXT NOT NULL,
    market_id TEXT NOT NULL,
    requester TEXT NOT NULL,
    reason TEXT NOT NULL,
    timestamp REAL NOT NULL,
    expires_at REAL NOT NULL,
    status TEXT NOT NULL DEFAULT 'pending',
    reviewer TEXT,
    review_timestamp REAL,
    review_notes TEXT DEFAULT ''
);

CREATE TABLE IF NOT EXISTS compliance_meta (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL
);
"""


class ComplianceStore:
    """SQLite-backed compliance persistence layer.

    Uses WAL mode for concurrent reads, parameterized queries,
    and append-only semantics for audit events.
    """

    def __init__(self, db_path: str = "./compliance.db") -> None:
        self._db_path = db_path
        self._lock = threading.Lock()
        self._conn = sqlite3.connect(db_path, check_same_thread=False)
        self._conn.execute("PRAGMA journal_mode=WAL")
        self._conn.execute("PRAGMA synchronous=NORMAL")
        self._conn.executescript(_SCHEMA)
        self._conn.commit()

    def close(self) -> None:
        with self._lock:
            self._conn.close()

    # ----- Audit events -----

    def insert_audit_event(self, event: AuditEvent) -> None:
        with self._lock:
            self._conn.execute(
                "INSERT OR IGNORE INTO audit_events "
                "(event_id, event_type, timestamp, actor, market_id, order_id, "
                "exchange, details_json, prev_hash, event_hash) "
                "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
                (
                    event.event_id, event.event_type.value, event.timestamp,
                    event.actor, event.market_id, event.order_id, event.exchange,
                    json.dumps(event.details, separators=(",", ":")),
                    event.prev_hash, event.event_hash,
                ),
            )
            self._conn.commit()

    def query_audit_events(
        self,
        start: float | None = None,
        end: float | None = None,
        event_type: str | None = None,
        market_id: str | None = None,
        limit: int = 1000,
    ) -> list[AuditEvent]:
        clauses = []
        params: list[Any] = []
        if start is not None:
            clauses.append("timestamp >= ?")
            params.append(start)
        if end is not None:
            clauses.append("timestamp <= ?")
            params.append(end)
        if event_type is not None:
            clauses.append("event_type = ?")
            params.append(event_type)
        if market_id is not None:
            clauses.append("market_id = ?")
            params.append(market_id)

        where = " AND ".join(clauses) if clauses else "1=1"
        sql = f"SELECT * FROM audit_events WHERE {where} ORDER BY timestamp ASC LIMIT ?"
        params.append(limit)

        with self._lock:
            rows = self._conn.execute(sql, params).fetchall()

        return [
            AuditEvent(
                event_id=r[0],
                event_type=AuditEventType(r[1]),
                timestamp=r[2],
                actor=r[3],
                market_id=r[4],
                order_id=r[5],
                exchange=r[6],
                details=json.loads(r[7]),
                prev_hash=r[8],
                event_hash=r[9],
            )
            for r in rows
        ]

    def last_event_hash(self) -> str | None:
        with self._lock:
            row = self._conn.execute(
                "SELECT event_hash FROM audit_events ORDER BY rowid DESC LIMIT 1"
            ).fetchone()
        return row[0] if row else None

    def event_count(self) -> int:
        with self._lock:
            row = self._conn.execute("SELECT COUNT(*) FROM audit_events").fetchone()
        return row[0] if row else 0

    # ----- Surveillance alerts -----

    def insert_alert(self, alert: SurveillanceAlert) -> None:
        with self._lock:
            self._conn.execute(
                "INSERT OR IGNORE INTO surveillance_alerts "
                "(alert_id, alert_type, severity, timestamp, market_id, "
                "description, evidence_json, resolved, resolution_notes) "
                "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
                (
                    alert.alert_id, alert.alert_type.value, alert.severity,
                    alert.timestamp, alert.market_id, alert.description,
                    json.dumps(alert.evidence, separators=(",", ":")),
                    int(alert.resolved), alert.resolution_notes,
                ),
            )
            self._conn.commit()

    def resolve_alert(self, alert_id: str, resolved_by: str, notes: str = "") -> None:
        import time
        with self._lock:
            self._conn.execute(
                "UPDATE surveillance_alerts SET resolved=1, resolved_by=?, "
                "resolution_notes=?, resolved_at=? WHERE alert_id=?",
                (resolved_by, notes, time.time(), alert_id),
            )
            self._conn.commit()

    def query_alerts(
        self,
        start: float | None = None,
        end: float | None = None,
        severity: str | None = None,
        resolved: bool | None = None,
        limit: int = 100,
    ) -> list[SurveillanceAlert]:
        clauses = []
        params: list[Any] = []
        if start is not None:
            clauses.append("timestamp >= ?")
            params.append(start)
        if end is not None:
            clauses.append("timestamp <= ?")
            params.append(end)
        if severity is not None:
            clauses.append("severity = ?")
            params.append(severity)
        if resolved is not None:
            clauses.append("resolved = ?")
            params.append(int(resolved))

        where = " AND ".join(clauses) if clauses else "1=1"
        sql = f"SELECT * FROM surveillance_alerts WHERE {where} ORDER BY timestamp DESC LIMIT ?"
        params.append(limit)

        with self._lock:
            rows = self._conn.execute(sql, params).fetchall()

        return [
            SurveillanceAlert(
                alert_id=r[0], alert_type=r[1], severity=r[2],
                timestamp=r[3], market_id=r[4], description=r[5],
                evidence=json.loads(r[6]), resolved=bool(r[7]),
                resolution_notes=r[8] or "",
            )
            for r in rows
        ]

    # ----- Approval requests -----

    def insert_approval(self, req: ApprovalRequest) -> None:
        with self._lock:
            self._conn.execute(
                "INSERT OR IGNORE INTO approval_requests "
                "(request_id, order_json, market_id, requester, reason, "
                "timestamp, expires_at, status, reviewer, review_timestamp, review_notes) "
                "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
                (
                    req.request_id, json.dumps(req.order_data, separators=(",", ":")),
                    req.market_id, req.requester, req.reason,
                    req.timestamp, req.expires_at, req.status.value,
                    req.reviewer, req.review_timestamp, req.review_notes,
                ),
            )
            self._conn.commit()

    def update_approval_status(
        self, request_id: str, status: ApprovalStatus,
        reviewer: str, notes: str = "",
    ) -> None:
        import time
        with self._lock:
            self._conn.execute(
                "UPDATE approval_requests SET status=?, reviewer=?, "
                "review_timestamp=?, review_notes=? WHERE request_id=?",
                (status.value, reviewer, time.time(), notes, request_id),
            )
            self._conn.commit()

    def get_pending_approvals(self) -> list[ApprovalRequest]:
        with self._lock:
            rows = self._conn.execute(
                "SELECT * FROM approval_requests WHERE status='pending' "
                "ORDER BY timestamp ASC"
            ).fetchall()
        return [self._row_to_approval(r) for r in rows]

    def get_approval(self, request_id: str) -> ApprovalRequest | None:
        with self._lock:
            row = self._conn.execute(
                "SELECT * FROM approval_requests WHERE request_id=?",
                (request_id,),
            ).fetchone()
        return self._row_to_approval(row) if row else None

    def _row_to_approval(self, r: tuple) -> ApprovalRequest:
        return ApprovalRequest(
            request_id=r[0], order_data=json.loads(r[1]), market_id=r[2],
            requester=r[3], reason=r[4], timestamp=r[5], expires_at=r[6],
            status=ApprovalStatus(r[7]), reviewer=r[8],
            review_timestamp=r[9], review_notes=r[10] or "",
        )

    # ----- Metadata -----

    def get_meta(self, key: str) -> str | None:
        with self._lock:
            row = self._conn.execute(
                "SELECT value FROM compliance_meta WHERE key=?", (key,)
            ).fetchone()
        return row[0] if row else None

    def set_meta(self, key: str, value: str) -> None:
        with self._lock:
            self._conn.execute(
                "INSERT OR REPLACE INTO compliance_meta (key, value) VALUES (?, ?)",
                (key, value),
            )
            self._conn.commit()

    # ----- Retention -----

    def purge_before(self, cutoff_ts: float) -> int:
        with self._lock:
            cursor = self._conn.execute(
                "DELETE FROM audit_events WHERE timestamp < ?", (cutoff_ts,)
            )
            count = cursor.rowcount
            self._conn.execute(
                "DELETE FROM surveillance_alerts WHERE timestamp < ?", (cutoff_ts,)
            )
            self._conn.execute(
                "DELETE FROM approval_requests WHERE timestamp < ?", (cutoff_ts,)
            )
            self._conn.commit()
        return count

    def alert_count(self, start: float | None = None, end: float | None = None) -> int:
        clauses = []
        params: list[Any] = []
        if start is not None:
            clauses.append("timestamp >= ?")
            params.append(start)
        if end is not None:
            clauses.append("timestamp <= ?")
            params.append(end)
        where = " AND ".join(clauses) if clauses else "1=1"
        with self._lock:
            row = self._conn.execute(
                f"SELECT COUNT(*) FROM surveillance_alerts WHERE {where}", params
            ).fetchone()
        return row[0] if row else 0
