import importlib
import json
import sys
from unittest.mock import MagicMock, patch


def _load_torrserver_utils():
    if "lib.utils.torrent.torrserver_utils" in sys.modules:
        return importlib.reload(sys.modules["lib.utils.torrent.torrserver_utils"])
    return importlib.import_module("lib.utils.torrent.torrserver_utils")


class TestDownloadTorrentSubtitles:
    def test_calls_subtitle_manager_with_imdb_id(self):
        module = _load_torrserver_utils()
        params = {
            "hash": "abc123",
            "meta": json.dumps({"title": "Test Movie", "ids": {"imdb_id": "tt123"}}),
        }

        with patch.object(module, "SubtitleManager") as MockManager, patch.object(
            module, "notification"
        ) as mock_notification, patch.object(
            module, "ADDON_PROFILE_PATH", "/addon/profile"
        ), patch.object(module.os.path, "exists", return_value=True), patch.object(
            module, "get_torrserver_api"
        ) as mock_api, patch.object(module.xbmcgui, "ListItem") as MockListItem, patch.object(
            module.xbmc, "Player"
        ) as MockPlayer:
            mock_manager = MagicMock()
            mock_manager.fetch_subtitles.return_value = ["/path/to/sub.srt"]
            mock_manager.last_fetch_status = None
            MockManager.return_value = mock_manager
            mock_api.return_value.get_torrent_info.return_value = {
                "file_stats": [{"path": "movie.mkv", "id": "1"}]
            }
            mock_listitem = MagicMock()
            MockListItem.return_value = mock_listitem
            mock_player = MagicMock()
            MockPlayer.return_value = mock_player

            module.download_torrent_subtitles(params)

            MockManager.assert_called_once_with(
                data={
                    "title": "Test Movie",
                    "mode": "movies",
                    "ids": {
                        "imdb_id": "tt123",
                        "tmdb_id": "",
                        "tvdb_id": "",
                        "original_id": "",
                    },
                    "tv_data": {},
                },
                notification=module.notification,
            )
            mock_manager.fetch_subtitles.assert_called_once_with(
                auto_select=False, folder_path="/addon/profile/subtitles/abc123"
            )
            mock_notification.assert_called_once_with("Subtitles downloaded successfully")
            playback_url = "plugin://plugin.video.jacktorr/buffer_and_play?info_hash=abc123&file_id=1&path=movie.mkv"
            MockListItem.assert_called_once_with(label="Test Movie", path=playback_url)
            mock_listitem.setSubtitles.assert_called_once_with(["/path/to/sub.srt"])
            mock_player.play.assert_called_once_with(playback_url, mock_listitem)

    def test_notifies_when_no_subtitles_found(self):
        module = _load_torrserver_utils()
        params = {
            "hash": "abc123",
            "meta": json.dumps({"title": "Test Movie", "ids": {"imdb_id": "tt123"}}),
        }

        with patch.object(module, "SubtitleManager") as MockManager, patch.object(
            module, "notification"
        ) as mock_notification:
            mock_manager = MagicMock()
            mock_manager.fetch_subtitles.return_value = None
            mock_manager.last_fetch_status = "not_found"
            MockManager.return_value = mock_manager

            module.download_torrent_subtitles(params)

            mock_notification.assert_called_once_with(module.translation(90252))

    def test_title_fallback_used_when_no_imdb_id(self):
        module = _load_torrserver_utils()
        params = {
            "hash": "abc123",
            "meta": json.dumps({"title": "Test Movie"}),
        }

        with patch.object(module, "SubtitleManager") as MockManager, patch.object(
            module, "notification"
        ) as mock_notification, patch.object(
            module, "ADDON_PROFILE_PATH", "/addon/profile"
        ), patch.object(module.os.path, "exists", return_value=True):
            mock_manager = MagicMock()
            mock_manager.fetch_subtitles.return_value = None
            mock_manager.last_fetch_status = "no_imdb"
            MockManager.return_value = mock_manager

            module.download_torrent_subtitles(params)

            MockManager.assert_called_once_with(
                data={
                    "title": "Test Movie",
                    "mode": "movies",
                    "ids": {
                        "tmdb_id": "",
                        "tvdb_id": "",
                        "imdb_id": "",
                        "original_id": "",
                    },
                    "tv_data": {},
                },
                notification=module.notification,
            )
            mock_manager.fetch_subtitles.assert_called_once_with(
                auto_select=False, folder_path="/addon/profile/subtitles/abc123"
            )
            mock_notification.assert_called_once_with(module.translation(90299))

    def test_aborts_when_no_imdb_id_and_no_title(self):
        module = _load_torrserver_utils()
        params = {
            "hash": "abc123",
            "meta": json.dumps({}),
        }

        with patch.object(module, "SubtitleManager") as MockManager, patch.object(
            module, "notification"
        ) as mock_notification:
            module.download_torrent_subtitles(params)

            MockManager.assert_not_called()
            mock_notification.assert_called_once_with("Insufficient metadata")


class TestDownloadAndPlaySubtitles:
    def test_plays_video_with_subtitles(self):
        module = _load_torrserver_utils()
        params = {
            "hash": "abc123",
            "file_id": "1",
            "path": "movie.mkv",
            "meta": json.dumps({"title": "Test Movie", "ids": {"imdb_id": "tt123"}}),
        }

        with patch.object(module, "SubtitleManager") as MockManager, patch.object(
            module, "get_setting", return_value=False
        ), patch.object(module.xbmcgui, "ListItem") as MockListItem, patch.object(
            module.xbmc, "Player"
        ) as MockPlayer, patch.object(module, "notification") as mock_notification:
            mock_manager = MagicMock()
            mock_manager.fetch_subtitles.return_value = ["/path/to/sub.srt"]
            mock_manager.last_fetch_status = None
            MockManager.return_value = mock_manager

            mock_player = MagicMock()
            MockPlayer.return_value = mock_player
            mock_listitem = MagicMock()
            MockListItem.return_value = mock_listitem

            module.download_and_play_subtitles(params)

            playback_url = "plugin://plugin.video.jacktorr/buffer_and_play?info_hash=abc123&file_id=1&path=movie.mkv"
            MockListItem.assert_called_once_with(label="Test Movie", path=playback_url)
            mock_listitem.setSubtitles.assert_called_once_with(["/path/to/sub.srt"])
            mock_player.play.assert_called_once_with(playback_url, mock_listitem)
            mock_notification.assert_not_called()

    def test_uses_subtitle_paths_returned_by_manager_without_second_translation_prompt(
        self,
    ):
        module = _load_torrserver_utils()
        params = {
            "hash": "abc123",
            "file_id": "1",
            "path": "movie.mkv",
            "meta": json.dumps({"title": "Test Movie", "ids": {"imdb_id": "tt123"}}),
        }

        with patch.object(module, "SubtitleManager") as MockManager, patch.object(
            module, "DeepLTranslator"
        ) as MockTranslator, patch.object(module, "get_setting", return_value=True), patch.object(
            module.xbmcgui.Dialog, "yesno", return_value=True
        ), patch.object(module.xbmcgui, "ListItem") as MockListItem, patch.object(
            module.xbmc, "Player"
        ) as MockPlayer, patch.object(module, "notification"):
            mock_manager = MagicMock()
            mock_manager.fetch_subtitles.return_value = ["/path/to/translated.srt"]
            MockManager.return_value = mock_manager

            mock_player = MagicMock()
            MockPlayer.return_value = mock_player
            mock_listitem = MagicMock()
            MockListItem.return_value = mock_listitem

            module.download_and_play_subtitles(params)

            mock_manager.fetch_subtitles.assert_called_once_with(auto_select=False)
            MockTranslator.assert_not_called()
            mock_listitem.setSubtitles.assert_called_once_with(["/path/to/translated.srt"])

    def test_deepL_no_uses_original_subtitles(self):
        module = _load_torrserver_utils()
        params = {
            "hash": "abc123",
            "file_id": "1",
            "path": "movie.mkv",
            "meta": json.dumps({"title": "Test Movie", "ids": {"imdb_id": "tt123"}}),
        }

        with patch.object(module, "SubtitleManager") as MockManager, patch.object(
            module, "DeepLTranslator"
        ) as MockTranslator, patch.object(module, "get_setting", return_value=True), patch.object(
            module.xbmcgui.Dialog, "yesno", return_value=False
        ), patch.object(module.xbmcgui, "ListItem") as MockListItem, patch.object(
            module.xbmc, "Player"
        ) as MockPlayer, patch.object(module, "notification"):
            mock_manager = MagicMock()
            mock_manager.fetch_subtitles.return_value = ["/path/to/sub.srt"]
            MockManager.return_value = mock_manager

            mock_player = MagicMock()
            MockPlayer.return_value = mock_player
            mock_listitem = MagicMock()
            MockListItem.return_value = mock_listitem

            module.download_and_play_subtitles(params)

            MockTranslator.assert_not_called()
            mock_listitem.setSubtitles.assert_called_once_with(["/path/to/sub.srt"])

    def test_no_results_notifies_and_does_not_play(self):
        module = _load_torrserver_utils()
        params = {
            "hash": "abc123",
            "file_id": "1",
            "path": "movie.mkv",
            "meta": json.dumps({"title": "Test Movie", "ids": {"imdb_id": "tt123"}}),
        }

        with patch.object(module, "SubtitleManager") as MockManager, patch.object(
            module, "get_torrserver_api"
        ), patch.object(module.xbmcgui, "ListItem") as MockListItem, patch.object(
            module.xbmc, "Player"
        ) as MockPlayer, patch.object(module, "notification") as mock_notification:
            mock_manager = MagicMock()
            mock_manager.fetch_subtitles.return_value = None
            mock_manager.last_fetch_status = "not_found"
            MockManager.return_value = mock_manager

            module.download_and_play_subtitles(params)

            MockListItem.assert_not_called()
            MockPlayer.assert_not_called()
            mock_notification.assert_called_once_with(module.translation(90252))

    def test_does_not_run_deepl_translation_twice_after_manager_fetch(self):
        module = _load_torrserver_utils()
        params = {
            "hash": "abc123",
            "file_id": "1",
            "path": "movie.mkv",
            "meta": json.dumps({"title": "Test Movie", "ids": {"imdb_id": "tt123"}}),
        }

        with patch.object(module, "SubtitleManager") as MockManager, patch.object(
            module, "DeepLTranslator"
        ) as MockTranslator, patch.object(module, "get_setting", return_value=True), patch.object(
            module.xbmcgui.Dialog, "yesno", return_value=True
        ) as yesno, patch.object(module.xbmcgui, "ListItem") as MockListItem, patch.object(
            module.xbmc, "Player"
        ) as MockPlayer, patch.object(module, "notification") as mock_notification, patch.object(
            module, "kodilog"
        ):
            mock_manager = MagicMock()
            mock_manager.fetch_subtitles.return_value = ["/path/to/sub.srt"]
            MockManager.return_value = mock_manager

            mock_player = MagicMock()
            MockPlayer.return_value = mock_player
            mock_listitem = MagicMock()
            MockListItem.return_value = mock_listitem

            module.download_and_play_subtitles(params)

            mock_listitem.setSubtitles.assert_called_once_with(["/path/to/sub.srt"])
            MockTranslator.assert_not_called()
            yesno.assert_not_called()
            mock_notification.assert_not_called()


class TestTorrentMetadataCache:
    def test_save_and_get_torrent_meta_normalizes_hash_case(self):
        module = _load_torrserver_utils()
        meta = {"title": "Test Movie", "ids": {"imdb_id": "tt123"}}

        stored = {}

        def fake_set(key, value, expires=None):
            stored[key] = value

        def fake_get(key):
            return stored.get(key)

        with patch.object(module.cache, "set", side_effect=fake_set), patch.object(
            module.cache, "get", side_effect=fake_get
        ), patch.object(module, "kodilog"):
            module.save_torrent_meta("ABC123", meta)
            result = module.get_torrent_meta("abc123")

        assert result == meta
        assert stored == {"torrent_meta:abc123": meta}

    def test_get_torrent_meta_strips_hash_whitespace(self):
        module = _load_torrserver_utils()
        meta = {"title": "Test Movie", "ids": {"imdb_id": "tt123"}}

        with patch.object(module.cache, "get", return_value=meta) as mock_get, patch.object(
            module, "kodilog"
        ):
            result = module.get_torrent_meta("  ABC123  ")

        assert result == meta
        mock_get.assert_called_once_with("torrent_meta:abc123")
