"""Logging and Audit Trail.

Maintains structured logs of flagged events with support for
JSON files, SQLite, and optional PostgreSQL storage.
"""

from __future__ import annotations

import json
import sqlite3
from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Literal, Optional


@dataclass
class AuditEntry:
    """A single audit log entry."""

    timestamp: str
    risk_score: float
    risk_level: str
    detected_categories: List[str]
    sanitized: bool
    blocked: bool
    prompt_preview: str = ""
    metadata: Dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> dict:
        return asdict(self)


class BaseBackend:
    """Abstract logging backend."""

    def write(self, entry: AuditEntry) -> None:
        raise NotImplementedError

    def read_all(self) -> list[dict]:
        raise NotImplementedError

    def close(self) -> None:
        pass


class JSONBackend(BaseBackend):
    """Append-only JSON-lines log file."""

    def __init__(self, path: str | Path = "contextguard_audit.jsonl") -> None:
        self.path = Path(path)
        self.path.parent.mkdir(parents=True, exist_ok=True)

    def write(self, entry: AuditEntry) -> None:
        with open(self.path, "a", encoding="utf-8") as f:
            f.write(json.dumps(entry.to_dict(), default=str) + "\n")

    def read_all(self) -> list[dict]:
        if not self.path.exists():
            return []
        entries = []
        with open(self.path, "r", encoding="utf-8") as f:
            for line in f:
                line = line.strip()
                if line:
                    entries.append(json.loads(line))
        return entries


class SQLiteBackend(BaseBackend):
    """SQLite audit log storage."""

    def __init__(self, path: str | Path = "contextguard_audit.db") -> None:
        self.path = Path(path)
        self.path.parent.mkdir(parents=True, exist_ok=True)
        self._conn = sqlite3.connect(str(self.path))
        self._conn.execute("PRAGMA journal_mode=WAL")
        self._create_table()

    def _create_table(self) -> None:
        self._conn.execute("""
            CREATE TABLE IF NOT EXISTS audit_log (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp TEXT NOT NULL,
                risk_score REAL NOT NULL,
                risk_level TEXT NOT NULL,
                detected_categories TEXT NOT NULL,
                sanitized INTEGER NOT NULL,
                blocked INTEGER NOT NULL,
                prompt_preview TEXT,
                metadata TEXT
            )
        """)
        self._conn.commit()

    def write(self, entry: AuditEntry) -> None:
        self._conn.execute(
            """INSERT INTO audit_log
               (timestamp, risk_score, risk_level, detected_categories,
                sanitized, blocked, prompt_preview, metadata)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
            (
                entry.timestamp,
                entry.risk_score,
                entry.risk_level,
                json.dumps(entry.detected_categories),
                int(entry.sanitized),
                int(entry.blocked),
                entry.prompt_preview,
                json.dumps(entry.metadata, default=str),
            ),
        )
        self._conn.commit()

    def read_all(self) -> list[dict]:
        cursor = self._conn.execute(
            "SELECT timestamp, risk_score, risk_level, detected_categories, "
            "sanitized, blocked, prompt_preview, metadata FROM audit_log ORDER BY id"
        )
        entries = []
        for row in cursor.fetchall():
            entries.append(
                {
                    "timestamp": row[0],
                    "risk_score": row[1],
                    "risk_level": row[2],
                    "detected_categories": json.loads(row[3]),
                    "sanitized": bool(row[4]),
                    "blocked": bool(row[5]),
                    "prompt_preview": row[6],
                    "metadata": json.loads(row[7]) if row[7] else {},
                }
            )
        return entries

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


class AuditLogger:
    """Structured audit logger with pluggable backends.

    Parameters
    ----------
    backend : str
        One of ``"json"``, ``"sqlite"``. Defaults to ``"json"``.
    path : str or Path, optional
        File path for the backend storage.
    block_threshold : float
        Risk score above which prompts are automatically blocked.
    """

    def __init__(
        self,
        backend: Literal["json", "sqlite"] = "json",
        path: Optional[str | Path] = None,
        block_threshold: float = 0.9,
    ) -> None:
        self.block_threshold = block_threshold
        if backend == "sqlite":
            self._backend: BaseBackend = SQLiteBackend(path or "contextguard_audit.db")
        else:
            self._backend = JSONBackend(path or "contextguard_audit.jsonl")

    def log(
        self,
        risk_score: float,
        risk_level: str,
        detected_categories: list[str],
        sanitized: bool = False,
        blocked: bool = False,
        prompt_preview: str = "",
        metadata: Optional[dict] = None,
    ) -> AuditEntry:
        """Record an audit entry."""
        entry = AuditEntry(
            timestamp=datetime.now(timezone.utc).isoformat(),
            risk_score=risk_score,
            risk_level=risk_level,
            detected_categories=detected_categories,
            sanitized=sanitized,
            blocked=blocked or risk_score >= self.block_threshold,
            prompt_preview=prompt_preview[:200],
            metadata=metadata or {},
        )
        self._backend.write(entry)
        return entry

    def get_logs(self) -> list[dict]:
        """Retrieve all audit log entries."""
        return self._backend.read_all()

    def close(self) -> None:
        self._backend.close()
