"""Unit tests for NfoRepairService — Task 1."""

import shutil
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock

import pytest

from src.core.services.nfo_repair_service import (
    REQUIRED_TAGS,
    NfoRepairService,
    find_missing_tags,
    nfo_needs_repair,
    parse_nfo_tags,
)

REPO_ROOT = Path(__file__).parents[2]
BAD_NFO = REPO_ROOT / "tvshow.nfo.bad"
GOOD_NFO = REPO_ROOT / "tvshow.nfo.good"

# Tags known to be absent/empty in tvshow.nfo.bad
EXPECTED_MISSING_FROM_BAD = {
    "originaltitle", "year", "plot", "runtime", "premiered",
    "status", "imdbid", "genre", "studio", "country", "actor/name", "watched",
}


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture()
def bad_nfo(tmp_path: Path) -> Path:
    """Copy tvshow.nfo.bad into a temp dir and return path to the copy."""
    dest = tmp_path / "tvshow.nfo"
    shutil.copy(BAD_NFO, dest)
    return dest


@pytest.fixture()
def good_nfo(tmp_path: Path) -> Path:
    """Copy tvshow.nfo.good into a temp dir and return path to the copy."""
    dest = tmp_path / "tvshow.nfo"
    shutil.copy(GOOD_NFO, dest)
    return dest


@pytest.fixture()
def mock_nfo_service() -> MagicMock:
    """Return a MagicMock NFOService with an async update_tvshow_nfo."""
    svc = MagicMock()
    svc.update_tvshow_nfo = AsyncMock(return_value=Path("/fake/tvshow.nfo"))
    return svc


# ---------------------------------------------------------------------------
# find_missing_tags
# ---------------------------------------------------------------------------


def test_find_missing_tags_with_bad_nfo(bad_nfo: Path) -> None:
    """Bad NFO must report all 12 incomplete/missing tags."""
    missing = find_missing_tags(bad_nfo)
    assert set(missing) == EXPECTED_MISSING_FROM_BAD, (
        f"Unexpected missing set: {set(missing)}"
    )


def test_find_missing_tags_with_good_nfo(good_nfo: Path) -> None:
    """Good NFO must report no missing tags."""
    missing = find_missing_tags(good_nfo)
    assert missing == []


# ---------------------------------------------------------------------------
# nfo_needs_repair
# ---------------------------------------------------------------------------


def test_nfo_needs_repair_returns_true_for_bad_nfo(bad_nfo: Path) -> None:
    assert nfo_needs_repair(bad_nfo) is True


def test_nfo_needs_repair_returns_false_for_good_nfo(good_nfo: Path) -> None:
    assert nfo_needs_repair(good_nfo) is False


# ---------------------------------------------------------------------------
# NfoRepairService.repair_series
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_repair_series_calls_update_when_nfo_needs_repair(
    tmp_path: Path, mock_nfo_service: MagicMock
) -> None:
    """repair_series must call update_tvshow_nfo exactly once for a bad NFO."""
    shutil.copy(BAD_NFO, tmp_path / "tvshow.nfo")
    service = NfoRepairService(mock_nfo_service)

    result = await service.repair_series(tmp_path, "Test Series")

    assert result is True
    mock_nfo_service.update_tvshow_nfo.assert_called_once_with(
        "Test Series", download_media=False
    )


@pytest.mark.asyncio
async def test_repair_series_skips_when_nfo_is_complete(
    tmp_path: Path, mock_nfo_service: MagicMock
) -> None:
    """repair_series must NOT call update_tvshow_nfo for a complete NFO."""
    shutil.copy(GOOD_NFO, tmp_path / "tvshow.nfo")
    service = NfoRepairService(mock_nfo_service)

    result = await service.repair_series(tmp_path, "Test Series")

    assert result is False
    mock_nfo_service.update_tvshow_nfo.assert_not_called()


# ---------------------------------------------------------------------------
# parse_nfo_tags edge cases
# ---------------------------------------------------------------------------


def test_parse_nfo_tags_handles_missing_file_gracefully() -> None:
    """parse_nfo_tags must return empty dict for non-existent path."""
    result = parse_nfo_tags(Path("/nonexistent/dir/tvshow.nfo"))
    assert result == {}


def test_parse_nfo_tags_handles_malformed_xml_gracefully(tmp_path: Path) -> None:
    """parse_nfo_tags must return empty dict for malformed XML."""
    bad_xml = tmp_path / "tvshow.nfo"
    bad_xml.write_text("<<< not valid xml >>>", encoding="utf-8")
    result = parse_nfo_tags(bad_xml)
    assert result == {}
