"""
Change detector for artifact content changes.

Implements FR-7.1: Change detection with persistent records.

Detects changes by comparing content digests and persists
ArtifactChange records to the artifact_changes table.
"""

import sqlite3
from pathlib import Path
from typing import List, Optional

from markitect.prompts.models import Artifact, calculate_content_digest
from markitect.prompts.incremental.models import ArtifactChange, ChangeType


# SQL schema for artifact_changes table
CHANGES_TABLE_SQL = """
CREATE TABLE IF NOT EXISTS artifact_changes (
    id TEXT PRIMARY KEY,
    artifact_id TEXT NOT NULL,
    old_digest TEXT,
    new_digest TEXT NOT NULL,
    change_type TEXT NOT NULL,
    detected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_changes_artifact ON artifact_changes(artifact_id);
CREATE INDEX IF NOT EXISTS idx_changes_type ON artifact_changes(change_type);
"""


class ChangeDetector:
    """
    Detects and records artifact content changes.

    Uses digest comparison to detect changes and persists
    ArtifactChange records to SQLite for auditing and
    incremental recomputation triggering.
    """

    def __init__(self, db_path: str):
        """
        Initialize with database path and ensure tables exist.

        Args:
            db_path: Path to SQLite database file
        """
        self.db_path = db_path
        self._initialize_tables()

    def _initialize_tables(self) -> None:
        """Initialize artifact_changes table if not exists."""
        db_dir = Path(self.db_path).parent
        if db_dir and not db_dir.exists():
            db_dir.mkdir(parents=True, exist_ok=True)

        conn = sqlite3.connect(self.db_path)
        try:
            conn.executescript(CHANGES_TABLE_SQL)
            conn.commit()
        finally:
            conn.close()

    def _get_connection(self) -> sqlite3.Connection:
        """Get a database connection."""
        conn = sqlite3.connect(self.db_path)
        conn.row_factory = sqlite3.Row
        return conn

    def detect_change(
        self,
        artifact: Artifact,
        new_content: str,
    ) -> Optional[ArtifactChange]:
        """
        Detect if artifact content has changed.

        Compares the artifact's stored digest against the new content digest.
        If changed, creates and returns an ArtifactChange record (not yet persisted).

        Args:
            artifact: Existing artifact to check
            new_content: New content to compare against

        Returns:
            ArtifactChange if content changed, None otherwise
        """
        new_digest = calculate_content_digest(new_content)

        if not artifact.has_changed(new_digest):
            return None

        return ArtifactChange.create(
            artifact_id=artifact.id,
            old_digest=artifact.content_digest,
            new_digest=new_digest,
            change_type=ChangeType.MODIFIED,
        )

    def detect_creation(
        self,
        artifact_id: str,
        content: str,
    ) -> ArtifactChange:
        """
        Record creation of a new artifact.

        Args:
            artifact_id: ID of newly created artifact
            content: Content of new artifact

        Returns:
            ArtifactChange record for the creation
        """
        new_digest = calculate_content_digest(content)
        return ArtifactChange.create(
            artifact_id=artifact_id,
            old_digest=None,
            new_digest=new_digest,
            change_type=ChangeType.CREATED,
        )

    def detect_deletion(
        self,
        artifact: Artifact,
    ) -> ArtifactChange:
        """
        Record deletion of an artifact.

        Args:
            artifact: Artifact being deleted

        Returns:
            ArtifactChange record for the deletion
        """
        return ArtifactChange.create(
            artifact_id=artifact.id,
            old_digest=artifact.content_digest,
            new_digest=artifact.content_digest,
            change_type=ChangeType.DELETED,
        )

    def record_change(self, change: ArtifactChange) -> ArtifactChange:
        """
        Persist an ArtifactChange record to the database.

        Args:
            change: Change record to persist

        Returns:
            Persisted change record
        """
        conn = self._get_connection()
        try:
            conn.execute(
                """
                INSERT INTO artifact_changes (
                    id, artifact_id, old_digest, new_digest,
                    change_type, detected_at
                ) VALUES (?, ?, ?, ?, ?, ?)
                """,
                (
                    change.id,
                    change.artifact_id,
                    change.old_digest,
                    change.new_digest,
                    change.change_type.value,
                    change.detected_at.isoformat(),
                ),
            )
            conn.commit()
            return change
        finally:
            conn.close()

    def get_changes_for_artifact(self, artifact_id: str) -> List[ArtifactChange]:
        """
        Get all recorded changes for an artifact.

        Args:
            artifact_id: Artifact identifier

        Returns:
            List of change records, ordered by detection time
        """
        conn = self._get_connection()
        try:
            cursor = conn.execute(
                """
                SELECT * FROM artifact_changes
                WHERE artifact_id = ?
                ORDER BY detected_at
                """,
                (artifact_id,),
            )
            return [self._row_to_change(row) for row in cursor.fetchall()]
        finally:
            conn.close()

    def get_changes_by_type(self, change_type: ChangeType) -> List[ArtifactChange]:
        """
        Get all recorded changes of a specific type.

        Args:
            change_type: Type of change to filter by

        Returns:
            List of change records
        """
        conn = self._get_connection()
        try:
            cursor = conn.execute(
                "SELECT * FROM artifact_changes WHERE change_type = ?",
                (change_type.value,),
            )
            return [self._row_to_change(row) for row in cursor.fetchall()]
        finally:
            conn.close()

    def _row_to_change(self, row: sqlite3.Row) -> ArtifactChange:
        """Convert database row to ArtifactChange instance."""
        from datetime import datetime

        return ArtifactChange(
            id=row["id"],
            artifact_id=row["artifact_id"],
            old_digest=row["old_digest"],
            new_digest=row["new_digest"],
            change_type=ChangeType(row["change_type"]),
            detected_at=datetime.fromisoformat(row["detected_at"]),
        )
