"""Unit tests for content.archive.indexer."""

from __future__ import annotations

from unittest.mock import patch

import pytest

from content.archive.indexer import (
    EpisodeDocument,
    IndexResult,
    _meilisearch_available,
    index_episode,
    search_episodes,
)

# ── _meilisearch_available ────────────────────────────────────────────────────


class TestMeilisearchAvailable:
    def test_returns_bool(self):
        assert isinstance(_meilisearch_available(), bool)

    def test_false_when_spec_missing(self):
        with patch("importlib.util.find_spec", return_value=None):
            assert _meilisearch_available() is False


# ── EpisodeDocument ───────────────────────────────────────────────────────────


class TestEpisodeDocument:
    def test_to_dict_contains_id(self):
        doc = EpisodeDocument(id="ep-001", title="Test")
        d = doc.to_dict()
        assert d["id"] == "ep-001"

    def test_to_dict_contains_title(self):
        doc = EpisodeDocument(id="ep-001", title="My Episode")
        assert doc.to_dict()["title"] == "My Episode"

    def test_to_dict_defaults(self):
        doc = EpisodeDocument(id="ep-001", title="T")
        d = doc.to_dict()
        assert d["tags"] == []
        assert d["highlight_ids"] == []
        assert d["duration"] == 0.0
        assert d["clip_count"] == 0

    def test_to_dict_preserves_tags(self):
        doc = EpisodeDocument(id="ep-001", title="T", tags=["gaming", "highlights"])
        assert doc.to_dict()["tags"] == ["gaming", "highlights"]

    def test_to_dict_all_fields(self):
        doc = EpisodeDocument(
            id="ep-002",
            title="Full",
            description="Desc",
            tags=["t"],
            published_at="2026-03-23T00:00:00Z",
            youtube_url="https://yt.com/x",
            blossom_url="https://blossom.io/x",
            duration=180.0,
            clip_count=5,
            highlight_ids=["h1", "h2"],
        )
        d = doc.to_dict()
        assert d["description"] == "Desc"
        assert d["youtube_url"] == "https://yt.com/x"
        assert d["duration"] == 180.0
        assert d["highlight_ids"] == ["h1", "h2"]


# ── index_episode ─────────────────────────────────────────────────────────────


class TestIndexEpisode:
    @pytest.mark.asyncio
    async def test_empty_id_returns_failure(self):
        result = await index_episode("", "Title")
        assert result.success is False
        assert "episode_id" in result.error

    @pytest.mark.asyncio
    async def test_whitespace_id_returns_failure(self):
        result = await index_episode("   ", "Title")
        assert result.success is False

    @pytest.mark.asyncio
    async def test_returns_failure_when_meilisearch_missing(self):
        with patch("content.archive.indexer._meilisearch_available", return_value=False):
            result = await index_episode("ep-001", "Title")
        assert result.success is False
        assert "meilisearch" in result.error.lower()

    @pytest.mark.asyncio
    async def test_successful_indexing(self):
        fake_result = IndexResult(success=True, document_id="ep-001")
        with (
            patch("content.archive.indexer._meilisearch_available", return_value=True),
            patch("asyncio.to_thread", return_value=fake_result),
        ):
            result = await index_episode(
                "ep-001",
                "Test Episode",
                description="A test",
                tags=["gaming"],
                published_at="2026-03-23T00:00:00Z",
                youtube_url="https://yt.com/abc",
                duration=120.0,
                clip_count=3,
                highlight_ids=["h1", "h2", "h3"],
            )

        assert result.success is True
        assert result.document_id == "ep-001"

    @pytest.mark.asyncio
    async def test_exception_from_thread_returns_failure(self):
        with (
            patch("content.archive.indexer._meilisearch_available", return_value=True),
            patch("asyncio.to_thread", side_effect=RuntimeError("connection refused")),
        ):
            result = await index_episode("ep-001", "Title")

        assert result.success is False
        assert "connection refused" in result.error


# ── search_episodes ───────────────────────────────────────────────────────────


class TestSearchEpisodes:
    @pytest.mark.asyncio
    async def test_returns_empty_when_library_missing(self):
        with patch("content.archive.indexer._meilisearch_available", return_value=False):
            results = await search_episodes("highlights")
        assert results == []

    @pytest.mark.asyncio
    async def test_returns_hits_on_success(self):
        fake_hits = [{"id": "ep-001", "title": "Gaming Highlights"}]
        with (
            patch("content.archive.indexer._meilisearch_available", return_value=True),
            patch("asyncio.to_thread", return_value=fake_hits),
        ):
            results = await search_episodes("gaming")

        assert len(results) == 1
        assert results[0]["id"] == "ep-001"

    @pytest.mark.asyncio
    async def test_returns_empty_on_exception(self):
        with (
            patch("content.archive.indexer._meilisearch_available", return_value=True),
            patch("asyncio.to_thread", side_effect=RuntimeError("timeout")),
        ):
            results = await search_episodes("query")

        assert results == []

    @pytest.mark.asyncio
    async def test_empty_list_when_no_results(self):
        with (
            patch("content.archive.indexer._meilisearch_available", return_value=True),
            patch("asyncio.to_thread", return_value=[]),
        ):
            results = await search_episodes("nothing matches")

        assert results == []
