"""SQLite persistence for dental compliance data, sterilization logs, and radiation logs."""

from __future__ import annotations

import json
import sqlite3
import datetime
from pathlib import Path
from typing import Any

from .types import (
    ComplianceFinding,
    ComplianceStatus,
    Severity,
    ScanResult,
    SterilizationRecord,
    RadiationRecord,
)


_SCHEMA = """
CREATE TABLE IF NOT EXISTS compliance_status (
    id TEXT PRIMARY KEY,
    framework TEXT NOT NULL,
    check_id TEXT NOT NULL,
    status TEXT DEFAULT 'unknown',
    evidence TEXT,
    last_checked TIMESTAMP,
    next_due TIMESTAMP,
    corrective_action TEXT,
    responsible_person TEXT,
    notes TEXT
);

CREATE TABLE IF NOT EXISTS sterilization_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    cycle_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    autoclave_id TEXT,
    cycle_type TEXT,
    temperature REAL,
    pressure REAL,
    duration_minutes REAL,
    biological_indicator TEXT,
    chemical_indicator TEXT,
    load_contents TEXT,
    operator TEXT,
    notes TEXT
);

CREATE TABLE IF NOT EXISTS radiation_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    log_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    equipment_id TEXT,
    log_type TEXT,
    details TEXT,
    result TEXT,
    operator TEXT
);

CREATE TABLE IF NOT EXISTS training_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    employee TEXT,
    training_type TEXT,
    completion_date TIMESTAMP,
    expiration_date TIMESTAMP,
    certificate TEXT,
    notes TEXT
);

CREATE TABLE IF NOT EXISTS scans (
    scan_id TEXT PRIMARY KEY,
    framework TEXT NOT NULL,
    practice_type TEXT NOT NULL,
    state TEXT NOT NULL DEFAULT '',
    timestamp TEXT NOT NULL,
    total_checks INTEGER DEFAULT 0,
    compliant INTEGER DEFAULT 0,
    non_compliant INTEGER DEFAULT 0,
    partial INTEGER DEFAULT 0,
    unknown INTEGER DEFAULT 0,
    score REAL DEFAULT 0.0
);

CREATE TABLE IF NOT EXISTS scan_findings (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    scan_id TEXT NOT NULL,
    framework TEXT NOT NULL,
    check_id TEXT NOT NULL,
    title TEXT NOT NULL,
    status TEXT NOT NULL DEFAULT 'unknown',
    severity TEXT NOT NULL DEFAULT 'medium',
    detail TEXT DEFAULT '',
    evidence TEXT DEFAULT '',
    corrective_action TEXT DEFAULT '',
    responsible_person TEXT DEFAULT '',
    last_checked TEXT,
    next_due TEXT,
    notes TEXT DEFAULT '',
    FOREIGN KEY (scan_id) REFERENCES scans(scan_id)
);

CREATE INDEX IF NOT EXISTS idx_compliance_framework ON compliance_status(framework);
CREATE INDEX IF NOT EXISTS idx_compliance_status ON compliance_status(status);
CREATE INDEX IF NOT EXISTS idx_scan_findings_scan ON scan_findings(scan_id);
CREATE INDEX IF NOT EXISTS idx_scan_findings_framework ON scan_findings(framework);
CREATE INDEX IF NOT EXISTS idx_sterilization_date ON sterilization_log(cycle_date);
CREATE INDEX IF NOT EXISTS idx_radiation_date ON radiation_log(log_date);
CREATE INDEX IF NOT EXISTS idx_training_employee ON training_log(employee);
"""


class DentalComplianceStore:
    """SQLite-backed store for dental compliance data."""

    def __init__(self, db_path: str | Path | None = None) -> None:
        if db_path is None:
            db_path = Path.home() / ".dentalcomplianceos" / "dental_compliance.db"
        self.db_path = Path(db_path)
        self.db_path.parent.mkdir(parents=True, exist_ok=True)
        self._conn: sqlite3.Connection | None = None

    @property
    def conn(self) -> sqlite3.Connection:
        """Get or create database connection."""
        if self._conn is None:
            self._conn = sqlite3.connect(str(self.db_path))
            self._conn.row_factory = sqlite3.Row
            self._conn.executescript(_SCHEMA)
        return self._conn

    def close(self) -> None:
        """Close the database connection."""
        if self._conn:
            self._conn.close()
            self._conn = None

    # -- Scans --

    def save_scan(self, result: ScanResult) -> None:
        """Persist a scan result and its findings."""
        self.conn.execute(
            "INSERT OR REPLACE INTO scans (scan_id, framework, practice_type, state, "
            "timestamp, total_checks, compliant, non_compliant, partial, unknown, score) "
            "VALUES (?,?,?,?,?,?,?,?,?,?,?)",
            (
                result.scan_id,
                result.framework,
                result.practice_type,
                result.state,
                result.timestamp,
                result.total_checks,
                result.compliant,
                result.non_compliant,
                result.partial,
                result.unknown,
                result.score,
            ),
        )
        for f in result.findings:
            self.conn.execute(
                "INSERT INTO scan_findings (scan_id, framework, check_id, title, "
                "status, severity, detail, evidence, corrective_action, "
                "responsible_person, last_checked, next_due, notes) "
                "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
                (
                    result.scan_id,
                    f.framework,
                    f.check_id,
                    f.title,
                    f.status.value,
                    f.severity.value,
                    f.detail,
                    f.evidence,
                    f.corrective_action,
                    f.responsible_person,
                    f.last_checked,
                    f.next_due,
                    f.notes,
                ),
            )
        self.conn.commit()

    def get_latest_scan(self, framework: str) -> dict[str, Any] | None:
        """Get the latest scan result for a framework."""
        row = self.conn.execute(
            "SELECT * FROM scans WHERE framework = ? ORDER BY timestamp DESC LIMIT 1",
            (framework,),
        ).fetchone()
        if not row:
            return None
        d = dict(row)
        findings = self.conn.execute(
            "SELECT * FROM scan_findings WHERE scan_id = ?",
            (d["scan_id"],),
        ).fetchall()
        d["findings"] = [dict(f) for f in findings]
        return d

    def get_all_scan_summaries(self) -> list[dict[str, Any]]:
        """Get latest scan summary for each framework."""
        rows = self.conn.execute(
            "SELECT framework, MAX(timestamp) as last_scan, score, "
            "total_checks, compliant, non_compliant "
            "FROM scans GROUP BY framework ORDER BY framework"
        ).fetchall()
        return [dict(r) for r in rows]

    def get_non_compliant_findings(
        self, framework: str | None = None
    ) -> list[dict[str, Any]]:
        """Get all non-compliant findings, optionally filtered by framework."""
        if framework:
            rows = self.conn.execute(
                "SELECT * FROM scan_findings WHERE status = 'non_compliant' "
                "AND framework = ? ORDER BY CASE severity "
                "WHEN 'critical' THEN 0 WHEN 'high' THEN 1 "
                "WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END",
                (framework,),
            ).fetchall()
        else:
            rows = self.conn.execute(
                "SELECT * FROM scan_findings WHERE status = 'non_compliant' "
                "ORDER BY CASE severity "
                "WHEN 'critical' THEN 0 WHEN 'high' THEN 1 "
                "WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END"
            ).fetchall()
        return [dict(r) for r in rows]

    # -- Compliance Status --

    def update_compliance_status(
        self,
        framework: str,
        check_id: str,
        status: str,
        evidence: str = "",
        corrective_action: str = "",
        responsible_person: str = "",
        notes: str = "",
        next_due: str = "",
    ) -> None:
        """Update or insert a compliance status record."""
        record_id = f"{framework}:{check_id}"
        now = datetime.datetime.utcnow().isoformat()
        self.conn.execute(
            "INSERT OR REPLACE INTO compliance_status "
            "(id, framework, check_id, status, evidence, last_checked, "
            "next_due, corrective_action, responsible_person, notes) "
            "VALUES (?,?,?,?,?,?,?,?,?,?)",
            (
                record_id,
                framework,
                check_id,
                status,
                evidence,
                now,
                next_due,
                corrective_action,
                responsible_person,
                notes,
            ),
        )
        self.conn.commit()

    def get_compliance_status(
        self, framework: str | None = None
    ) -> list[dict[str, Any]]:
        """Get all compliance status records, optionally filtered by framework."""
        if framework:
            rows = self.conn.execute(
                "SELECT * FROM compliance_status WHERE framework = ? "
                "ORDER BY check_id",
                (framework,),
            ).fetchall()
        else:
            rows = self.conn.execute(
                "SELECT * FROM compliance_status ORDER BY framework, check_id"
            ).fetchall()
        return [dict(r) for r in rows]

    # -- Sterilization Log --

    def log_sterilization(self, record: SterilizationRecord) -> int:
        """Log a sterilization cycle."""
        cur = self.conn.execute(
            "INSERT INTO sterilization_log (autoclave_id, cycle_type, temperature, "
            "pressure, duration_minutes, biological_indicator, chemical_indicator, "
            "load_contents, operator, notes) VALUES (?,?,?,?,?,?,?,?,?,?)",
            (
                record.autoclave_id,
                record.cycle_type,
                record.temperature,
                record.pressure,
                record.duration_minutes,
                record.biological_indicator,
                record.chemical_indicator,
                record.load_contents,
                record.operator,
                record.notes,
            ),
        )
        self.conn.commit()
        return cur.lastrowid  # type: ignore[return-value]

    def get_sterilization_log(
        self, days: int = 30, autoclave_id: str | None = None
    ) -> list[dict[str, Any]]:
        """Get sterilization log entries."""
        cutoff = (
            datetime.datetime.utcnow() - datetime.timedelta(days=days)
        ).isoformat()
        if autoclave_id:
            rows = self.conn.execute(
                "SELECT * FROM sterilization_log WHERE cycle_date >= ? "
                "AND autoclave_id = ? ORDER BY cycle_date DESC",
                (cutoff, autoclave_id),
            ).fetchall()
        else:
            rows = self.conn.execute(
                "SELECT * FROM sterilization_log WHERE cycle_date >= ? "
                "ORDER BY cycle_date DESC",
                (cutoff,),
            ).fetchall()
        return [dict(r) for r in rows]

    def get_last_spore_test(self, autoclave_id: str) -> dict[str, Any] | None:
        """Get the most recent biological indicator (spore test) result."""
        row = self.conn.execute(
            "SELECT * FROM sterilization_log WHERE autoclave_id = ? "
            "AND biological_indicator IS NOT NULL AND biological_indicator != '' "
            "ORDER BY cycle_date DESC LIMIT 1",
            (autoclave_id,),
        ).fetchone()
        return dict(row) if row else None

    # -- Radiation Log --

    def log_radiation(self, record: RadiationRecord) -> int:
        """Log a radiation event."""
        cur = self.conn.execute(
            "INSERT INTO radiation_log (equipment_id, log_type, details, result, operator) "
            "VALUES (?,?,?,?,?)",
            (
                record.equipment_id,
                record.log_type,
                record.details,
                record.result,
                record.operator,
            ),
        )
        self.conn.commit()
        return cur.lastrowid  # type: ignore[return-value]

    def get_radiation_log(
        self, days: int = 90, equipment_id: str | None = None
    ) -> list[dict[str, Any]]:
        """Get radiation log entries."""
        cutoff = (
            datetime.datetime.utcnow() - datetime.timedelta(days=days)
        ).isoformat()
        if equipment_id:
            rows = self.conn.execute(
                "SELECT * FROM radiation_log WHERE log_date >= ? "
                "AND equipment_id = ? ORDER BY log_date DESC",
                (cutoff, equipment_id),
            ).fetchall()
        else:
            rows = self.conn.execute(
                "SELECT * FROM radiation_log WHERE log_date >= ? "
                "ORDER BY log_date DESC",
                (cutoff,),
            ).fetchall()
        return [dict(r) for r in rows]

    def get_last_lead_apron_check(self) -> dict[str, Any] | None:
        """Get the most recent lead apron inspection."""
        row = self.conn.execute(
            "SELECT * FROM radiation_log WHERE log_type = 'lead_apron_check' "
            "ORDER BY log_date DESC LIMIT 1"
        ).fetchone()
        return dict(row) if row else None

    # -- Training Log --

    def log_training(
        self,
        employee: str,
        training_type: str,
        completion_date: str,
        expiration_date: str = "",
        certificate: str = "",
        notes: str = "",
    ) -> int:
        """Log a training completion."""
        cur = self.conn.execute(
            "INSERT INTO training_log (employee, training_type, completion_date, "
            "expiration_date, certificate, notes) VALUES (?,?,?,?,?,?)",
            (employee, training_type, completion_date, expiration_date, certificate, notes),
        )
        self.conn.commit()
        return cur.lastrowid  # type: ignore[return-value]

    def get_training_log(self, employee: str | None = None) -> list[dict[str, Any]]:
        """Get training log entries."""
        if employee:
            rows = self.conn.execute(
                "SELECT * FROM training_log WHERE employee = ? "
                "ORDER BY completion_date DESC",
                (employee,),
            ).fetchall()
        else:
            rows = self.conn.execute(
                "SELECT * FROM training_log ORDER BY completion_date DESC"
            ).fetchall()
        return [dict(r) for r in rows]

    def get_expiring_training(self, days: int = 30, days_ahead: int | None = None) -> list[dict[str, Any]]:
        """Get training records expiring within the given number of days."""
        effective_days = days_ahead if days_ahead is not None else days
        cutoff = (
            datetime.datetime.utcnow() + datetime.timedelta(days=effective_days)
        ).isoformat()
        rows = self.conn.execute(
            "SELECT * FROM training_log WHERE expiration_date != '' "
            "AND expiration_date <= ? ORDER BY expiration_date ASC",
            (cutoff,),
        ).fetchall()
        return [dict(r) for r in rows]
