"""Integration test: add an anime and verify NFO contains required information.

This test adds 'Sacrificial Princess And The King Of Beasts' and verifies
that the generated tvshow.nfo contains all required tags including plot,
outline, title, year, etc.
"""

from pathlib import Path
from unittest.mock import AsyncMock, patch

import pytest
from lxml import etree

from src.core.services.nfo_service import NFOService

# ---------------------------------------------------------------------------
# Mock TMDB data for "Sacrificial Princess And The King Of Beasts"
# ---------------------------------------------------------------------------
MOCK_TMDB_DATA = {
    "id": 222093,
    "name": "Sacrificial Princess and the King of Beasts",
    "original_name": "贄姫と獣の王",
    "overview": (
        "A girl is offered as a sacrifice to a beastly king, "
        "but instead of being eaten, she becomes his bride."
    ),
    "tagline": "A tale of love between a sacrifice and a beast king.",
    "first_air_date": "2023-04-20",
    "vote_average": 7.5,
    "vote_count": 150,
    "status": "Ended",
    "episode_run_time": [24],
    "genres": [
        {"id": 16, "name": "Animation"},
        {"id": 10749, "name": "Romance"},
    ],
    "networks": [{"id": 1, "name": "TBS"}],
    "origin_country": ["JP"],
    "poster_path": "/poster.jpg",
    "backdrop_path": "/backdrop.jpg",
    "external_ids": {"imdb_id": "tt19896734", "tvdb_id": 421737},
    "credits": {
        "cast": [
            {
                "id": 1,
                "name": "Test Actor",
                "character": "Sariphi",
                "profile_path": "/actor.jpg",
            }
        ]
    },
    "images": {"logos": [{"file_path": "/logo.png"}]},
    "seasons": [{"season_number": 1, "name": "Season 1"}],
}

MOCK_CONTENT_RATINGS = {
    "results": [
        {"iso_3166_1": "DE", "rating": "12"},
        {"iso_3166_1": "US", "rating": "TV-14"},
    ]
}

# ---------------------------------------------------------------------------
# Required XML tags that must exist and be non-empty after creation
# ---------------------------------------------------------------------------
REQUIRED_SINGLE_TAGS = [
    "title",
    "originaltitle",
    "sorttitle",
    "year",
    "plot",
    "outline",
    "runtime",
    "premiered",
    "status",
    "tmdbid",
    "imdbid",
    "tvdbid",
    "dateadded",
    "watched",
    "mpaa",
    "tagline",
]

REQUIRED_MULTI_TAGS = [
    "genre",
    "studio",
    "country",
]


@pytest.fixture
def anime_dir(tmp_path: Path) -> Path:
    """Temporary anime root directory."""
    d = tmp_path / "anime"
    d.mkdir()
    return d


@pytest.fixture
def nfo_service(anime_dir: Path) -> NFOService:
    """NFOService pointing at the temp directory."""
    return NFOService(
        tmdb_api_key="test_api_key",
        anime_directory=str(anime_dir),
        image_size="w500",
        auto_create=True,
    )


class TestAddAnimeNFOContent:
    """Test that adding an anime produces an NFO with required information."""

    @pytest.mark.asyncio
    async def test_add_anime_nfo_contains_required_tags(
        self,
        nfo_service: NFOService,
        anime_dir: Path,
    ) -> None:
        """Add 'Sacrificial Princess And The King Of Beasts' and verify NFO.

        Steps:
        1. Create the series folder on disk.
        2. Mock TMDB API responses.
        3. Call create_tvshow_nfo to generate the NFO.
        4. Parse the resulting XML and assert every required tag is present
           and non-empty.
        """
        series_key = "sacrificial-princess-and-the-king-of-beasts"
        series_name = "Sacrificial Princess And The King Of Beasts"
        series_folder = f"{series_name} (2023)"

        # Step 1: Create series folder
        series_path = anime_dir / series_folder
        series_path.mkdir()

        # Step 2: Mock TMDB API calls
        with patch.object(
            nfo_service.tmdb_client,
            "search_tv_show",
            new_callable=AsyncMock,
        ) as mock_search, patch.object(
            nfo_service.tmdb_client,
            "get_tv_show_details",
            new_callable=AsyncMock,
        ) as mock_details, patch.object(
            nfo_service.tmdb_client,
            "get_tv_show_content_ratings",
            new_callable=AsyncMock,
        ) as mock_ratings, patch.object(
            nfo_service.image_downloader,
            "download_all_media",
            new_callable=AsyncMock,
        ) as mock_download:

            mock_search.return_value = {
                "results": [
                    {
                        "id": 222093,
                        "name": series_name,
                        "first_air_date": "2023-04-20",
                        "overview": (
                            "A girl is offered as a sacrifice to a beastly king..."
                        ),
                    }
                ]
            }
            mock_details.return_value = MOCK_TMDB_DATA
            mock_ratings.return_value = MOCK_CONTENT_RATINGS
            mock_download.return_value = {
                "poster": True,
                "logo": True,
                "fanart": True,
            }

            # Step 3: Create NFO
            nfo_path = await nfo_service.create_tvshow_nfo(
                serie_name=series_name,
                serie_folder=series_folder,
                year=2023,
                download_poster=True,
                download_logo=True,
                download_fanart=True,
            )

        # Verify NFO was created
        assert nfo_path.exists(), f"NFO file not created at {nfo_path}"
        assert nfo_path.name == "tvshow.nfo"

        # Step 4: Parse NFO XML and verify required tags
        nfo_content = nfo_path.read_text(encoding="utf-8")
        root = etree.fromstring(nfo_content.encode("utf-8"))

        missing: list[str] = []
        for tag in REQUIRED_SINGLE_TAGS:
            elem = root.find(f".//{tag}")
            if elem is None or not (elem.text or "").strip():
                missing.append(tag)

        for tag in REQUIRED_MULTI_TAGS:
            elems = root.findall(f".//{tag}")
            if not elems or not any((e.text or "").strip() for e in elems):
                missing.append(tag)

        # At least one actor must be present
        actors = root.findall(".//actor/name")
        if not actors or not any((a.text or "").strip() for a in actors):
            missing.append("actor/name")

        assert not missing, (
            f"Missing or empty required tags in NFO for '{series_name}':\n  "
            + "\n  ".join(missing)
            + f"\n\nFull NFO content:\n{nfo_content}"
        )

        # Verify specific values for the requested anime
        assert root.findtext(".//title") == "Sacrificial Princess and the King of Beasts"
        assert root.findtext(".//year") == "2023"
        assert root.findtext(".//status") == "Ended"
        assert root.findtext(".//watched") == "false"
        assert root.findtext(".//tmdbid") == "222093"
        assert root.findtext(".//imdbid") == "tt19896734"
        assert root.findtext(".//tvdbid") == "421737"

        # Plot and outline must be non-trivial
        plot = root.findtext(".//plot") or ""
        outline = root.findtext(".//outline") or ""
        assert len(plot) >= 10, f"plot too short: {plot!r}"
        assert len(outline) >= 10, f"outline too short: {outline!r}"

        # Verify multi-value fields
        genres = [g.text for g in root.findall(".//genre") if g.text]
        assert "Animation" in genres
        assert "Romance" in genres

        studios = [s.text for s in root.findall(".//studio") if s.text]
        assert "TBS" in studios

        countries = [c.text for c in root.findall(".//country") if c.text]
        assert "JP" in countries

    @pytest.mark.asyncio
    async def test_add_anime_nfo_has_plot_and_outline(
        self,
        nfo_service: NFOService,
        anime_dir: Path,
    ) -> None:
        """Specifically verify that plot and outline tags are populated.

        This is a focused regression test ensuring the NFO always contains
        meaningful plot and outline data.
        """
        series_name = "Sacrificial Princess And The King Of Beasts"
        series_folder = f"{series_name} (2023)"
        series_path = anime_dir / series_folder
        series_path.mkdir()

        with patch.object(
            nfo_service.tmdb_client,
            "search_tv_show",
            new_callable=AsyncMock,
        ) as mock_search, patch.object(
            nfo_service.tmdb_client,
            "get_tv_show_details",
            new_callable=AsyncMock,
        ) as mock_details, patch.object(
            nfo_service.tmdb_client,
            "get_tv_show_content_ratings",
            new_callable=AsyncMock,
        ) as mock_ratings, patch.object(
            nfo_service.image_downloader,
            "download_all_media",
            new_callable=AsyncMock,
        ) as mock_download:

            mock_search.return_value = {
                "results": [
                    {
                        "id": 222093,
                        "name": series_name,
                        "first_air_date": "2023-04-20",
                    }
                ]
            }
            mock_details.return_value = MOCK_TMDB_DATA
            mock_ratings.return_value = MOCK_CONTENT_RATINGS
            mock_download.return_value = {"poster": False, "logo": False, "fanart": False}

            nfo_path = await nfo_service.create_tvshow_nfo(
                serie_name=series_name,
                serie_folder=series_folder,
                year=2023,
                download_poster=False,
                download_logo=False,
                download_fanart=False,
            )

        assert nfo_path.exists()
        root = etree.parse(str(nfo_path)).getroot()

        plot_elem = root.find(".//plot")
        outline_elem = root.find(".//outline")

        assert plot_elem is not None, "<plot> tag missing from NFO"
        assert outline_elem is not None, "<outline> tag missing from NFO"

        plot_text = (plot_elem.text or "").strip()
        outline_text = (outline_elem.text or "").strip()

        assert plot_text, "<plot> tag is empty"
        assert outline_text, "<outline> tag is empty"
        assert "sacrifice" in plot_text.lower() or "beast" in plot_text.lower(), (
            f"plot does not contain expected content: {plot_text!r}"
        )
