"""
Unit tests for ChangeDetector.

Tests change detection, recording, change types, and no-change cases.
"""

import pytest
import tempfile
from pathlib import Path

from markitect.prompts.models import Artifact, ArtifactType, calculate_content_digest
from markitect.prompts.incremental.detector import ChangeDetector
from markitect.prompts.incremental.models import ChangeType


@pytest.fixture
def temp_db():
    """Create temporary database for testing."""
    with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
        db_path = f.name
    yield db_path
    Path(db_path).unlink(missing_ok=True)


@pytest.fixture
def detector(temp_db):
    """Create ChangeDetector instance."""
    return ChangeDetector(temp_db)


def _make_artifact(content="original content"):
    """Helper to create an in-memory artifact."""
    return Artifact.create(
        space_id="space-1",
        name="test-artifact",
        content=content,
        artifact_type=ArtifactType.CONTENT,
    )


class TestDetectChange:
    """Tests for detecting content changes."""

    def test_detect_modification(self, detector):
        """Test detecting a content modification."""
        artifact = _make_artifact("original content")
        change = detector.detect_change(artifact, "modified content")

        assert change is not None
        assert change.artifact_id == artifact.id
        assert change.old_digest == artifact.content_digest
        assert change.new_digest == calculate_content_digest("modified content")
        assert change.change_type == ChangeType.MODIFIED

    def test_no_change_returns_none(self, detector):
        """Test that identical content returns None."""
        artifact = _make_artifact("same content")
        change = detector.detect_change(artifact, "same content")

        assert change is None

    def test_detect_whitespace_change(self, detector):
        """Test detecting whitespace-only changes."""
        artifact = _make_artifact("content")
        change = detector.detect_change(artifact, "content ")

        assert change is not None
        assert change.change_type == ChangeType.MODIFIED

    def test_detect_empty_to_content(self, detector):
        """Test detecting change from empty to content."""
        artifact = _make_artifact("")
        change = detector.detect_change(artifact, "new content")

        assert change is not None
        assert change.change_type == ChangeType.MODIFIED


class TestDetectCreation:
    """Tests for recording artifact creation."""

    def test_detect_creation(self, detector):
        """Test creation change record."""
        change = detector.detect_creation("artifact-123", "new content")

        assert change.artifact_id == "artifact-123"
        assert change.old_digest is None
        assert change.new_digest == calculate_content_digest("new content")
        assert change.change_type == ChangeType.CREATED

    def test_creation_has_unique_id(self, detector):
        """Test that each creation gets a unique ID."""
        change1 = detector.detect_creation("art-1", "content")
        change2 = detector.detect_creation("art-2", "content")

        assert change1.id != change2.id


class TestDetectDeletion:
    """Tests for recording artifact deletion."""

    def test_detect_deletion(self, detector):
        """Test deletion change record."""
        artifact = _make_artifact("content to delete")
        change = detector.detect_deletion(artifact)

        assert change.artifact_id == artifact.id
        assert change.old_digest == artifact.content_digest
        assert change.change_type == ChangeType.DELETED


class TestRecordChange:
    """Tests for persisting change records."""

    def test_record_and_retrieve(self, detector):
        """Test recording a change and retrieving it."""
        artifact = _make_artifact("original")
        change = detector.detect_change(artifact, "modified")
        assert change is not None

        detector.record_change(change)
        changes = detector.get_changes_for_artifact(artifact.id)

        assert len(changes) == 1
        assert changes[0].id == change.id
        assert changes[0].artifact_id == artifact.id
        assert changes[0].change_type == ChangeType.MODIFIED

    def test_record_multiple_changes(self, detector):
        """Test recording multiple changes for same artifact."""
        artifact = _make_artifact("v1")

        change1 = detector.detect_change(artifact, "v2")
        detector.record_change(change1)

        # Simulate artifact update
        artifact.update_content("v2")
        change2 = detector.detect_change(artifact, "v3")
        detector.record_change(change2)

        changes = detector.get_changes_for_artifact(artifact.id)
        assert len(changes) == 2

    def test_get_changes_by_type(self, detector):
        """Test filtering changes by type."""
        # Record a creation
        creation = detector.detect_creation("art-new", "content")
        detector.record_change(creation)

        # Record a modification
        artifact = _make_artifact("old")
        modification = detector.detect_change(artifact, "new")
        detector.record_change(modification)

        created_changes = detector.get_changes_by_type(ChangeType.CREATED)
        assert len(created_changes) == 1
        assert created_changes[0].change_type == ChangeType.CREATED

        modified_changes = detector.get_changes_by_type(ChangeType.MODIFIED)
        assert len(modified_changes) == 1
        assert modified_changes[0].change_type == ChangeType.MODIFIED

    def test_no_changes_returns_empty(self, detector):
        """Test querying changes for artifact with none recorded."""
        changes = detector.get_changes_for_artifact("nonexistent")
        assert changes == []
