"""Unit tests for NFO service factory module.

Tests factory instantiation, configuration precedence, singleton pattern,
and convenience functions for creating NFOService instances.
"""

from unittest.mock import MagicMock, patch

import pytest

from src.core.services.nfo_factory import (
    NFOServiceFactory,
    create_nfo_service,
    get_nfo_factory,
)


class TestNFOServiceFactoryCreate:
    """Tests for NFOServiceFactory.create method."""

    @patch("src.core.services.nfo_factory.NFOService")
    @patch("src.core.services.nfo_factory.settings")
    def test_create_with_explicit_api_key(self, mock_settings, mock_nfo_cls):
        """Explicit API key takes priority over settings."""
        mock_settings.tmdb_api_key = "settings_key"
        mock_settings.anime_directory = "/anime"
        mock_settings.nfo_image_size = "original"
        mock_settings.nfo_auto_create = False

        factory = NFOServiceFactory()
        factory.create(tmdb_api_key="explicit_key")
        mock_nfo_cls.assert_called_once_with(
            tmdb_api_key="explicit_key",
            anime_directory="/anime",
            image_size="original",
            auto_create=False,
        )

    @patch("src.core.services.nfo_factory.NFOService")
    @patch("src.core.services.nfo_factory.settings")
    def test_create_falls_back_to_settings(self, mock_settings, mock_nfo_cls):
        """Falls back to settings when no explicit key provided."""
        mock_settings.tmdb_api_key = "settings_key"
        mock_settings.anime_directory = "/anime"
        mock_settings.nfo_image_size = "w500"
        mock_settings.nfo_auto_create = True

        factory = NFOServiceFactory()
        factory.create()
        mock_nfo_cls.assert_called_once_with(
            tmdb_api_key="settings_key",
            anime_directory="/anime",
            image_size="w500",
            auto_create=True,
        )

    @patch("src.core.services.nfo_factory.settings")
    def test_create_raises_without_api_key(self, mock_settings):
        """Raises ValueError when no API key available from any source."""
        mock_settings.tmdb_api_key = None
        factory = NFOServiceFactory()
        factory._get_api_key_from_config = MagicMock(return_value=None)
        with pytest.raises(ValueError, match="TMDB API key not configured"):
            factory.create()

    @patch("src.core.services.nfo_factory.NFOService")
    @patch("src.core.services.nfo_factory.settings")
    def test_create_with_all_custom_params(self, mock_settings, mock_nfo_cls):
        """All parameters can be overridden."""
        mock_settings.tmdb_api_key = "default"
        mock_settings.anime_directory = "/default"
        mock_settings.nfo_image_size = "original"
        mock_settings.nfo_auto_create = False

        factory = NFOServiceFactory()
        factory.create(
            tmdb_api_key="custom",
            anime_directory="/custom",
            image_size="w300",
            auto_create=True,
        )
        mock_nfo_cls.assert_called_once_with(
            tmdb_api_key="custom",
            anime_directory="/custom",
            image_size="w300",
            auto_create=True,
        )

    @patch("src.core.services.nfo_factory.NFOService")
    @patch("src.core.services.nfo_factory.settings")
    def test_create_uses_config_json_fallback(self, mock_settings, mock_nfo_cls):
        """Falls back to config.json when settings has no key."""
        mock_settings.tmdb_api_key = None
        mock_settings.anime_directory = "/anime"
        mock_settings.nfo_image_size = "original"
        mock_settings.nfo_auto_create = False

        factory = NFOServiceFactory()
        factory._get_api_key_from_config = MagicMock(return_value="config_key")
        factory.create()
        mock_nfo_cls.assert_called_once()
        call_kwargs = mock_nfo_cls.call_args[1]
        assert call_kwargs["tmdb_api_key"] == "config_key"


class TestNFOServiceFactoryCreateOptional:
    """Tests for NFOServiceFactory.create_optional method."""

    @patch("src.core.services.nfo_factory.settings")
    def test_returns_none_without_api_key(self, mock_settings):
        """Returns None instead of raising when no API key."""
        mock_settings.tmdb_api_key = None
        factory = NFOServiceFactory()
        factory._get_api_key_from_config = MagicMock(return_value=None)
        result = factory.create_optional()
        assert result is None

    @patch("src.core.services.nfo_factory.NFOService")
    @patch("src.core.services.nfo_factory.settings")
    def test_returns_service_when_configured(self, mock_settings, mock_nfo_cls):
        """Returns NFOService when configuration is available."""
        mock_settings.tmdb_api_key = "key123"
        mock_settings.anime_directory = "/anime"
        mock_settings.nfo_image_size = "original"
        mock_settings.nfo_auto_create = False

        factory = NFOServiceFactory()
        result = factory.create_optional()
        assert result is not None


class TestGetNfoFactory:
    """Tests for get_nfo_factory singleton function."""

    def test_returns_factory_instance(self):
        """Returns an NFOServiceFactory instance."""
        import src.core.services.nfo_factory as mod
        old = mod._factory_instance
        try:
            mod._factory_instance = None
            factory = get_nfo_factory()
            assert isinstance(factory, NFOServiceFactory)
        finally:
            mod._factory_instance = old

    def test_returns_same_instance(self):
        """Repeated calls return the same singleton."""
        import src.core.services.nfo_factory as mod
        old = mod._factory_instance
        try:
            mod._factory_instance = None
            f1 = get_nfo_factory()
            f2 = get_nfo_factory()
            assert f1 is f2
        finally:
            mod._factory_instance = old


class TestCreateNfoService:
    """Tests for create_nfo_service convenience function."""

    @patch("src.core.services.nfo_factory.NFOService")
    @patch("src.core.services.nfo_factory.settings")
    def test_convenience_function_creates_service(
        self, mock_settings, mock_nfo_cls
    ):
        """Convenience function delegates to factory.create()."""
        mock_settings.tmdb_api_key = "key"
        mock_settings.anime_directory = "/anime"
        mock_settings.nfo_image_size = "original"
        mock_settings.nfo_auto_create = False

        result = create_nfo_service()
        mock_nfo_cls.assert_called_once()

    @patch("src.core.services.nfo_factory.settings")
    def test_convenience_function_raises_without_key(self, mock_settings):
        """Convenience function raises ValueError without key."""
        mock_settings.tmdb_api_key = None
        import src.core.services.nfo_factory as mod
        old = mod._factory_instance
        try:
            mod._factory_instance = None
            factory = get_nfo_factory()
            factory._get_api_key_from_config = MagicMock(return_value=None)
            with pytest.raises(ValueError):
                factory.create()
        finally:
            mod._factory_instance = old
