"""Unit tests for key_resolution_service."""
from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from src.server.services.scheduler.key_resolution_service import (
    _extract_key_from_link,
    _extract_year_from_folder,
    _normalize_for_comparison,
    _strip_year_from_folder,
    resolve_key_for_folder,
)


class TestStripYearFromFolder:
    """Tests for _strip_year_from_folder."""

    def test_removes_year_suffix(self):
        assert _strip_year_from_folder("Rent-A-Girlfriend (2020)") == "Rent-A-Girlfriend"

    def test_removes_year_suffix_with_spaces(self):
        assert _strip_year_from_folder("Attack on Titan (2013)") == "Attack on Titan"

    def test_no_year_returns_original(self):
        assert _strip_year_from_folder("Naruto") == "Naruto"

    def test_year_in_middle_not_stripped(self):
        assert _strip_year_from_folder("2024 Anime (2024)") == "2024 Anime"

    def test_empty_string(self):
        assert _strip_year_from_folder("") == ""

    def test_only_year(self):
        assert _strip_year_from_folder("(2020)") == ""


class TestExtractYearFromFolder:
    """Tests for _extract_year_from_folder."""

    def test_extracts_year(self):
        assert _extract_year_from_folder("Rent-A-Girlfriend (2020)") == 2020

    def test_no_year_returns_none(self):
        assert _extract_year_from_folder("Naruto") is None

    def test_year_in_middle_not_extracted(self):
        # Only trailing year is extracted
        assert _extract_year_from_folder("2024 Anime") is None


class TestExtractKeyFromLink:
    """Tests for _extract_key_from_link."""

    def test_relative_link(self):
        assert _extract_key_from_link("/anime/stream/rent-a-girlfriend") == "rent-a-girlfriend"

    def test_full_url(self):
        assert (
            _extract_key_from_link("https://aniworld.to/anime/stream/attack-on-titan")
            == "attack-on-titan"
        )

    def test_link_with_trailing_slash(self):
        assert _extract_key_from_link("/anime/stream/naruto/") == "naruto"

    def test_empty_link(self):
        assert _extract_key_from_link("") is None

    def test_none_link(self):
        assert _extract_key_from_link(None) is None

    def test_slug_only(self):
        assert _extract_key_from_link("one-piece") == "one-piece"


class TestNormalizeForComparison:
    """Tests for _normalize_for_comparison."""

    def test_case_insensitive(self):
        assert _normalize_for_comparison("Rent-A-Girlfriend") == _normalize_for_comparison(
            "rent-a-girlfriend"
        )

    def test_strips_whitespace(self):
        assert _normalize_for_comparison("  Naruto  ") == "naruto"

    def test_normalizes_dashes(self):
        assert _normalize_for_comparison("Rent-A-Girlfriend") == "rent a girlfriend"

    def test_collapses_spaces(self):
        assert _normalize_for_comparison("Attack  on   Titan") == "attack on titan"


class TestResolveKeyForFolder:
    """Tests for resolve_key_for_folder."""

    @pytest.mark.asyncio
    async def test_single_exact_match_returns_key(self):
        """When provider returns exactly one exact-name match, key is resolved."""
        search_results = [
            {"link": "/anime/stream/rent-a-girlfriend", "title": "Rent-A-Girlfriend"},
        ]

        with patch(
            "src.server.services.scheduler.key_resolution_service._search_provider",
            return_value=search_results,
        ):
            key = await resolve_key_for_folder("Rent-A-Girlfriend (2020)")
            assert key == "rent-a-girlfriend"

    @pytest.mark.asyncio
    async def test_no_results_returns_none(self):
        """When provider returns no results, returns None."""
        with patch(
            "src.server.services.scheduler.key_resolution_service._search_provider",
            return_value=[],
        ):
            key = await resolve_key_for_folder("Unknown Anime (2020)")
            assert key is None

    @pytest.mark.asyncio
    async def test_multiple_exact_matches_returns_none(self):
        """When multiple results match the same name exactly, returns None."""
        search_results = [
            {"link": "/anime/stream/my-anime", "title": "My Anime"},
            {"link": "/anime/stream/my-anime-2", "title": "My Anime"},
        ]

        with patch(
            "src.server.services.scheduler.key_resolution_service._search_provider",
            return_value=search_results,
        ):
            key = await resolve_key_for_folder("My Anime (2022)")
            assert key is None

    @pytest.mark.asyncio
    async def test_no_exact_match_returns_none(self):
        """When results exist but none match the folder name, returns None."""
        search_results = [
            {"link": "/anime/stream/rent-a-girlfriend-2", "title": "Rent-A-Girlfriend 2nd Season"},
            {"link": "/anime/stream/rent-a-girlfriend-3", "title": "Rent-A-Girlfriend 3rd Season"},
        ]

        with patch(
            "src.server.services.scheduler.key_resolution_service._search_provider",
            return_value=search_results,
        ):
            key = await resolve_key_for_folder("Rent-A-Girlfriend (2020)")
            assert key is None

    @pytest.mark.asyncio
    async def test_case_insensitive_match(self):
        """Matching is case-insensitive."""
        search_results = [
            {"link": "/anime/stream/naruto", "title": "NARUTO"},
        ]

        with patch(
            "src.server.services.scheduler.key_resolution_service._search_provider",
            return_value=search_results,
        ):
            key = await resolve_key_for_folder("Naruto (2002)")
            assert key == "naruto"

    @pytest.mark.asyncio
    async def test_provider_error_returns_none(self):
        """When provider search raises an exception, returns None gracefully."""
        with patch(
            "src.server.services.scheduler.key_resolution_service._search_provider",
            side_effect=RuntimeError("Network error"),
        ):
            key = await resolve_key_for_folder("Some Anime (2020)")
            assert key is None

    @pytest.mark.asyncio
    async def test_result_with_name_field_instead_of_title(self):
        """Search results using 'name' field instead of 'title' work."""
        search_results = [
            {"link": "/anime/stream/one-piece", "name": "One Piece"},
        ]

        with patch(
            "src.server.services.scheduler.key_resolution_service._search_provider",
            return_value=search_results,
        ):
            key = await resolve_key_for_folder("One Piece (1999)")
            assert key == "one-piece"

    @pytest.mark.asyncio
    async def test_folder_without_year(self):
        """Folders without year suffix still work."""
        search_results = [
            {"link": "/anime/stream/naruto", "title": "Naruto"},
        ]

        with patch(
            "src.server.services.scheduler.key_resolution_service._search_provider",
            return_value=search_results,
        ):
            key = await resolve_key_for_folder("Naruto")
            assert key == "naruto"

    @pytest.mark.asyncio
    async def test_exact_match_among_partial_matches(self):
        """Only exact matches count, partial matches are ignored."""
        search_results = [
            {"link": "/anime/stream/dororo", "title": "Dororo"},
            {"link": "/anime/stream/dororo-to-hyakkimaru", "title": "Dororo to Hyakkimaru"},
        ]

        with patch(
            "src.server.services.scheduler.key_resolution_service._search_provider",
            return_value=search_results,
        ):
            key = await resolve_key_for_folder("Dororo (2019)")
            assert key == "dororo"
