from __future__ import annotations

import sqlite3
import time
from contextlib import contextmanager
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from ocr_pipeline.utils.logging import get_logger

logger = get_logger(__name__)


@dataclass
class ProcessedFile:
    path: str
    hash: str
    timestamp: float
    run_id: int
    size: int
    mtime: float


class Database:
    def __init__(self, db_path: Path):
        self.db_path = Path(db_path).expanduser().resolve()
        self.db_path.parent.mkdir(parents=True, exist_ok=True)
        self._init_db()

    @contextmanager
    def _conn(self):
        conn = sqlite3.connect(self.db_path)
        conn.row_factory = sqlite3.Row
        try:
            yield conn
        finally:
            conn.close()

    def _init_db(self):
        with self._conn() as conn:
            conn.executescript("""
                CREATE TABLE IF NOT EXISTS processed_files (
                    hash TEXT PRIMARY KEY,
                    path TEXT NOT NULL,
                    timestamp TEXT NOT NULL,
                    ocr_engine TEXT,
                    confidence REAL,
                    chunks_created INTEGER DEFAULT 0,
                    created_at REAL DEFAULT (strftime('%s', 'now'))
                );

                CREATE TABLE IF NOT EXISTS runs (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    date TEXT NOT NULL,
                    run_number INTEGER NOT NULL,
                    total_images INTEGER DEFAULT 0,
                    successful INTEGER DEFAULT 0,
                    failed INTEGER DEFAULT 0,
                    chunks_created INTEGER DEFAULT 0,
                    started_at REAL DEFAULT (strftime('%s', 'now')),
                    completed_at REAL,
                    UNIQUE(date, run_number)
                );

                CREATE INDEX IF NOT EXISTS idx_processed_hash ON processed_files(hash);
                CREATE INDEX IF NOT EXISTS idx_runs_date ON runs(date);
            """)

    def is_processed(self, file_hash: str, path: Path | str | None = None) -> bool:
        """Check if a file is already processed by hash (and optionally path)."""
        with self._conn() as conn:
            if path:
                cursor = conn.execute(
                    "SELECT 1 FROM processed_files WHERE hash = ? AND path = ?",
                    (file_hash, str(path)),
                )
            else:
                cursor = conn.execute(
                    "SELECT 1 FROM processed_files WHERE hash = ?",
                    (file_hash,),
                )
            return cursor.fetchone() is not None

    def get_processed(self, file_hash: str) -> dict[str, Any] | None:
        with self._conn() as conn:
            cursor = conn.execute("SELECT * FROM processed_files WHERE hash = ?", (file_hash,))
            row = cursor.fetchone()
            return dict(row) if row else None

    def mark_processed(
        self,
        file_hash: str,
        path: str,
        timestamp: float | str,
        engine: str = "",
        confidence: float = 0.0,
        chunks: int = 0,
        run_id: int = 0,
    ):
        with self._conn() as conn:
            conn.execute(
                """INSERT OR REPLACE INTO processed_files
                   (hash, path, timestamp, ocr_engine, confidence, chunks_created)
                   VALUES (?, ?, ?, ?, ?, ?)""",
                (file_hash, path, str(timestamp), engine, confidence, chunks),
            )

    def get_stats(self, days: int = 30) -> dict[str, Any]:
        with self._conn() as conn:
            cursor = conn.execute("""
                SELECT
                    COUNT(*) as total_processed,
                    SUM(chunks_created) as total_chunks,
                    AVG(confidence) as avg_confidence
                FROM processed_files
            """)
            row = cursor.fetchone()
            return dict(row) if row else {}

    def start_run(self, date: str | None = None) -> int:
        if date is None:
            date = time.strftime("%Y-%m-%d")
        with self._conn() as conn:
            cursor = conn.execute(
                "SELECT COALESCE(MAX(run_number), 0) + 1 FROM runs WHERE date = ?", (date,)
            )
            run_number = cursor.fetchone()[0]
            conn.execute(
                "INSERT INTO runs (date, run_number, total_images) VALUES (?, ?, 0)",
                (date, run_number),
            )
            return run_number

    def complete_run(self, date: str, run_number: int, stats: dict[str, int]):
        with self._conn() as conn:
            conn.execute(
                """UPDATE runs SET
                    total_images = ?,
                    successful = ?,
                    failed = ?,
                    chunks_created = ?,
                    completed_at = ?
                WHERE date = ? AND run_number = ?""",
                (
                    stats.get("total", 0),
                    stats.get("successful", 0),
                    stats.get("failed", 0),
                    stats.get("chunks", 0),
                    time.time(),
                    date,
                    run_number,
                ),
            )


_db_instance: Database | None = None


def get_db() -> Database:
    global _db_instance
    if _db_instance is None:
        from ocr_pipeline.config import settings

        _db_instance = Database(settings.watch_db_path)
    return _db_instance
