"""SQLite storage layer for file tracking."""

import os
import sqlite3
import hashlib
from pathlib import Path
from dataclasses import dataclass
from typing import Optional
import time

from nogic.storage.schema import (
    create_schema,
    get_schema_version,
    set_schema_version,
    migrate_schema,
    SCHEMA_VERSION,
)


@dataclass
class FileRecord:
    id: int
    path: str
    content_hash: str
    last_modified: float
    synced_at: Optional[float]
    status: str  # 'active' or 'deleted'


class FileStorage:
    def __init__(self, db_path: Path):
        self.db_path = db_path
        self._conn: Optional[sqlite3.Connection] = None
        self._init_db()

    def _init_db(self):
        conn = self._connect()
        current_version = get_schema_version(conn)

        if current_version < SCHEMA_VERSION:
            create_schema(conn)
            set_schema_version(conn, SCHEMA_VERSION)
            conn.commit()

        # Restrict database file permissions
        os.chmod(self.db_path, 0o600)

    def _connect(self) -> sqlite3.Connection:
        """Return the shared connection, creating it if needed."""
        if self._conn is None:
            self._conn = sqlite3.connect(self.db_path)
            self._conn.row_factory = sqlite3.Row
        return self._conn

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

    @staticmethod
    def compute_hash(content: str) -> str:
        return hashlib.sha256(content.encode()).hexdigest()

    def get_file(self, path: str) -> Optional[FileRecord]:
        conn = self._connect()
        row = conn.execute(
            "SELECT * FROM files WHERE path = ?", (path,)
        ).fetchone()
        if row:
            return FileRecord(**dict(row))
        return None

    def upsert_file(
        self, path: str, content_hash: str, last_modified: float
    ) -> tuple[int, str]:
        """Insert or update file. Returns (file_id, event_type)."""
        existing = self.get_file(path)
        now = time.time()

        conn = self._connect()
        if existing is None:
            cursor = conn.execute(
                """INSERT INTO files (path, content_hash, last_modified, status)
                   VALUES (?, ?, ?, 'active')""",
                (path, content_hash, last_modified),
            )
            file_id = cursor.lastrowid
            event_type = "created"
        elif existing.status == "deleted":
            conn.execute(
                """UPDATE files
                   SET content_hash = ?, last_modified = ?, status = 'active'
                   WHERE id = ?""",
                (content_hash, last_modified, existing.id),
            )
            file_id = existing.id
            event_type = "created"
        elif existing.content_hash != content_hash:
            conn.execute(
                """UPDATE files
                   SET content_hash = ?, last_modified = ?
                   WHERE id = ?""",
                (content_hash, last_modified, existing.id),
            )
            file_id = existing.id
            event_type = "modified"
        else:
            return existing.id, "unchanged"

        conn.execute(
            """INSERT INTO sync_events (file_id, event_type, content_hash, timestamp)
               VALUES (?, ?, ?, ?)""",
            (file_id, event_type, content_hash, now),
        )
        conn.commit()
        return file_id, event_type

    def mark_deleted(self, path: str) -> Optional[int]:
        """Mark file as deleted. Returns file_id if found."""
        existing = self.get_file(path)
        if existing and existing.status == "active":
            now = time.time()
            conn = self._connect()
            conn.execute(
                "UPDATE files SET status = 'deleted' WHERE id = ?",
                (existing.id,),
            )
            conn.execute(
                """INSERT INTO sync_events (file_id, event_type, content_hash, timestamp)
                   VALUES (?, 'deleted', ?, ?)""",
                (existing.id, existing.content_hash, now),
            )
            conn.commit()
            return existing.id
        return None

    def mark_synced(self, file_id: int):
        """Mark file as synced."""
        conn = self._connect()
        conn.execute(
            "UPDATE files SET synced_at = ? WHERE id = ?",
            (time.time(), file_id),
        )
        conn.commit()

    def get_pending_sync(self) -> list[FileRecord]:
        """Get files that need syncing (modified after last sync)."""
        conn = self._connect()
        rows = conn.execute(
            """SELECT * FROM files
               WHERE status = 'active'
               AND (synced_at IS NULL OR last_modified > synced_at)"""
        ).fetchall()
        return [FileRecord(**dict(row)) for row in rows]

    def get_all_active(self) -> list[FileRecord]:
        """Get all active files."""
        conn = self._connect()
        rows = conn.execute(
            "SELECT * FROM files WHERE status = 'active'"
        ).fetchall()
        return [FileRecord(**dict(row)) for row in rows]

    def get_stats(self) -> dict:
        """Get storage statistics."""
        conn = self._connect()
        total = conn.execute(
            "SELECT COUNT(*) FROM files WHERE status = 'active'"
        ).fetchone()[0]
        synced = conn.execute(
            "SELECT COUNT(*) FROM files WHERE status = 'active' AND synced_at IS NOT NULL"
        ).fetchone()[0]
        return {
            "total_files": total,
            "synced_files": synced,
            "pending_files": total - synced,
        }

    def clear_all(self):
        """Clear all file records (for reindexing)."""
        conn = self._connect()
        conn.execute("DELETE FROM sync_events")
        conn.execute("DELETE FROM files")
        conn.commit()
