"""Tests for the database layer."""

from takeseek.db import Database


class TestSchema:
    def test_creates_tables(self, tmp_db):
        tables = tmp_db.conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
        ).fetchall()
        table_names = {r["name"] for r in tables}
        assert "clips" in table_names
        assert "transcript_segments" in table_names
        assert "frame_descriptions" in table_names
        assert "tags" in table_names

    def test_creates_fts_tables(self, tmp_db):
        tables = tmp_db.conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
        ).fetchall()
        table_names = {r["name"] for r in tables}
        assert "transcript_fts" in table_names
        assert "frame_fts" in table_names

    def test_wal_mode(self, tmp_db):
        mode = tmp_db.conn.execute("PRAGMA journal_mode").fetchone()
        assert mode[0] == "wal"


class TestClipCRUD:
    def test_upsert_insert(self, tmp_db):
        clip_id = tmp_db.upsert_clip(
            file_path="/test/video.mp4",
            file_name="video.mp4",
            file_size=1000,
        )
        assert clip_id > 0

    def test_upsert_update(self, tmp_db):
        clip_id1 = tmp_db.upsert_clip(
            file_path="/test/video.mp4",
            file_name="video.mp4",
            file_size=1000,
        )
        clip_id2 = tmp_db.upsert_clip(
            file_path="/test/video.mp4",
            file_name="video.mp4",
            file_size=2000,
            duration=30.0,
        )
        assert clip_id1 == clip_id2
        clip = tmp_db.get_clip_by_id(clip_id1)
        assert clip["file_size"] == 2000
        assert clip["duration"] == 30.0

    def test_get_by_path(self, tmp_db):
        tmp_db.upsert_clip(file_path="/a/b.mp4", file_name="b.mp4", file_size=500)
        clip = tmp_db.get_clip_by_path("/a/b.mp4")
        assert clip is not None
        assert clip["file_name"] == "b.mp4"

    def test_get_by_path_not_found(self, tmp_db):
        assert tmp_db.get_clip_by_path("/nonexistent.mp4") is None

    def test_list_clips(self, tmp_db):
        tmp_db.upsert_clip(file_path="/a.mp4", file_name="a.mp4", file_size=1)
        tmp_db.upsert_clip(file_path="/b.mp4", file_name="b.mp4", file_size=2)
        clips = tmp_db.list_clips()
        assert len(clips) == 2

    def test_update_status(self, sample_clip):
        db, clip_id = sample_clip
        db.update_clip_status(clip_id, "transcribed")
        clip = db.get_clip_by_id(clip_id)
        assert clip["index_status"] == "transcribed"

    def test_clip_count(self, tmp_db):
        tmp_db.upsert_clip(file_path="/a.mp4", file_name="a.mp4", file_size=1, index_status="metadata")
        tmp_db.upsert_clip(file_path="/b.mp4", file_name="b.mp4", file_size=2, index_status="metadata")
        tmp_db.upsert_clip(file_path="/c.mp4", file_name="c.mp4", file_size=3, index_status="complete")
        counts = tmp_db.get_clip_count()
        assert counts["total"] == 3
        assert counts["metadata"] == 2
        assert counts["complete"] == 1


class TestHashMatching:
    def test_upsert_matches_by_hash(self, tmp_db):
        """When a file is renamed, upsert should match by content_hash and preserve clip_id."""
        clip_id1 = tmp_db.upsert_clip(
            file_path="/videos/original.mp4",
            file_name="original.mp4",
            file_size=1000,
            content_hash="abc123hash",
        )
        # Simulate rename: different path, same hash
        clip_id2 = tmp_db.upsert_clip(
            file_path="/videos/renamed.mp4",
            file_name="renamed.mp4",
            file_size=1000,
            content_hash="abc123hash",
        )
        assert clip_id1 == clip_id2
        clip = tmp_db.get_clip_by_id(clip_id1)
        assert clip["file_path"] == "/videos/renamed.mp4"
        assert clip["file_name"] == "renamed.mp4"

    def test_upsert_no_hash_match(self, tmp_db):
        """Different hashes should create separate clips."""
        clip_id1 = tmp_db.upsert_clip(
            file_path="/videos/a.mp4",
            file_name="a.mp4",
            file_size=1000,
            content_hash="hash_a",
        )
        clip_id2 = tmp_db.upsert_clip(
            file_path="/videos/b.mp4",
            file_name="b.mp4",
            file_size=2000,
            content_hash="hash_b",
        )
        assert clip_id1 != clip_id2

    def test_hash_match_preserves_transcripts(self, tmp_db):
        """Renamed file matched by hash should keep its transcripts."""
        clip_id = tmp_db.upsert_clip(
            file_path="/videos/original.mp4",
            file_name="original.mp4",
            file_size=1000,
            content_hash="hashXYZ",
        )
        tmp_db.insert_transcript_segments(clip_id, [
            {"start": 0.0, "end": 5.0, "text": "Important transcript"},
        ])
        # Rename
        clip_id2 = tmp_db.upsert_clip(
            file_path="/videos/renamed.mp4",
            file_name="renamed.mp4",
            file_size=1000,
            content_hash="hashXYZ",
        )
        assert clip_id == clip_id2
        transcript = tmp_db.get_transcript(clip_id)
        assert len(transcript) == 1
        assert transcript[0]["text"] == "Important transcript"

    def test_get_clip_by_hash(self, tmp_db):
        tmp_db.upsert_clip(
            file_path="/videos/test.mp4",
            file_name="test.mp4",
            file_size=500,
            content_hash="unique_hash",
        )
        clip = tmp_db.get_clip_by_hash("unique_hash")
        assert clip is not None
        assert clip["file_path"] == "/videos/test.mp4"
        assert tmp_db.get_clip_by_hash("nonexistent") is None


class TestTranscripts:
    def test_insert_and_get(self, sample_clip):
        db, clip_id = sample_clip
        segments = [
            {"start": 0.0, "end": 2.5, "text": "Hello world"},
            {"start": 2.5, "end": 5.0, "text": "Testing transcripts"},
        ]
        db.insert_transcript_segments(clip_id, segments)
        result = db.get_transcript(clip_id)
        assert len(result) == 2
        assert result[0]["text"] == "Hello world"
        assert result[1]["start_time"] == 2.5

    def test_idempotent_reinsert(self, sample_clip):
        db, clip_id = sample_clip
        db.insert_transcript_segments(clip_id, [{"start": 0.0, "end": 1.0, "text": "First"}])
        db.insert_transcript_segments(clip_id, [{"start": 0.0, "end": 1.0, "text": "Second"}])
        result = db.get_transcript(clip_id)
        assert len(result) == 1
        assert result[0]["text"] == "Second"


class TestFrameDescriptions:
    def test_insert_and_get(self, sample_clip):
        db, clip_id = sample_clip
        descriptions = [
            {"timestamp": 0.0, "description": "A person walking"},
            {"timestamp": 30.0, "description": "A sunset over the ocean"},
        ]
        db.insert_frame_descriptions(clip_id, descriptions)
        result = db.get_frame_descriptions(clip_id)
        assert len(result) == 2
        assert "sunset" in result[1]["description"]

    def test_idempotent_reinsert(self, sample_clip):
        db, clip_id = sample_clip
        db.insert_frame_descriptions(clip_id, [{"timestamp": 0.0, "description": "Old"}])
        db.insert_frame_descriptions(clip_id, [{"timestamp": 0.0, "description": "New"}])
        result = db.get_frame_descriptions(clip_id)
        assert len(result) == 1
        assert result[0]["description"] == "New"


class TestTags:
    def test_add_and_get(self, sample_clip):
        db, clip_id = sample_clip
        db.add_tag(clip_id, "Nature")
        db.add_tag(clip_id, "4K")
        tags = db.get_tags(clip_id)
        assert "nature" in tags
        assert "4k" in tags

    def test_duplicate_tag_ignored(self, sample_clip):
        db, clip_id = sample_clip
        db.add_tag(clip_id, "test")
        db.add_tag(clip_id, "test")
        tags = db.get_tags(clip_id)
        assert tags.count("test") == 1

    def test_list_all_tags(self, sample_clip):
        db, clip_id = sample_clip
        db.add_tag(clip_id, "nature")
        db.add_tag(clip_id, "sunset")
        all_tags = db.list_all_tags()
        assert len(all_tags) == 2


class TestFTSSync:
    def test_transcript_fts_sync(self, sample_clip):
        db, clip_id = sample_clip
        db.insert_transcript_segments(clip_id, [
            {"start": 0.0, "end": 5.0, "text": "The quick brown fox jumps"},
        ])
        results = db.search_transcripts("fox")
        assert len(results) == 1
        assert results[0]["clip_id"] == clip_id

    def test_transcript_fts_delete_sync(self, sample_clip):
        db, clip_id = sample_clip
        db.insert_transcript_segments(clip_id, [
            {"start": 0.0, "end": 5.0, "text": "unique_word_alpha"},
        ])
        # Re-insert with different text (delete + insert)
        db.insert_transcript_segments(clip_id, [
            {"start": 0.0, "end": 5.0, "text": "unique_word_beta"},
        ])
        assert len(db.search_transcripts("unique_word_alpha")) == 0
        assert len(db.search_transcripts("unique_word_beta")) == 1

    def test_frame_fts_sync(self, sample_clip):
        db, clip_id = sample_clip
        db.insert_frame_descriptions(clip_id, [
            {"timestamp": 0.0, "description": "A magnificent waterfall in the mountains"},
        ])
        results = db.search_frames("waterfall")
        assert len(results) == 1
        assert results[0]["clip_id"] == clip_id


class TestSubdirectories:
    def test_list_subdirectories(self, tmp_db):
        tmp_db.upsert_clip(file_path="/videos/nature/a.mp4", file_name="a.mp4", file_size=1)
        tmp_db.upsert_clip(file_path="/videos/nature/b.mp4", file_name="b.mp4", file_size=2)
        tmp_db.upsert_clip(file_path="/videos/urban/c.mp4", file_name="c.mp4", file_size=3)
        dirs = tmp_db.list_subdirectories()
        assert len(dirs) == 2
        dir_names = [d["directory"] for d in dirs]
        assert "/videos/nature" in dir_names
        assert "/videos/urban" in dir_names
