from lib.utils.kodi import settings_backup


class FakeCache:
    def __init__(self, initial=None):
        self.store = dict(initial or {})

    def get(self, key):
        return self.store.get(key)

    def set(self, key, value, expires=None):
        self.store[key] = value

    def clear_list(self, key):
        self.store[key] = None


def _write_settings_xml(tmp_path, body):
    settings_path = tmp_path / "settings.xml"
    settings_path.write_text(
        "<?xml version='1.0' encoding='utf-8'?>\n"
        "<settings version='1'>\n"
        "  <section id='plugin.video.jacktook'>\n"
        f"{body}\n"
        "  </section>\n"
        "</settings>\n",
        encoding="utf-8",
    )
    return str(settings_path)


def test_build_backup_payload_skips_action_settings_and_scrubs_sensitive_data(
    monkeypatch, tmp_path
):
    settings_path = _write_settings_xml(
        tmp_path,
        """
    <category id='general'>
      <group id='backup'>
        <setting id='safe_value' type='string'><default /></setting>
        <setting id='jackett_apikey' type='string'><default /></setting>
        <setting id='stremio_email' type='string'><default /></setting>
        <setting id='is_trakt_auth' type='boolean'><default>false</default></setting>
        <setting id='some_action' type='action' />
      </group>
    </category>
    """,
    )
    values = {
        "safe_value": "safe",
        "jackett_apikey": "secret",
        "stremio_email": "user@example.com",
        "is_trakt_auth": "true",
    }

    monkeypatch.setattr(
        settings_backup.ADDON,
        "getSetting",
        lambda setting_id: values.get(setting_id, ""),
    )
    monkeypatch.setattr(settings_backup, "cache", FakeCache())
    monkeypatch.setattr(
        settings_backup,
        "get_cached_setting_property",
        lambda setting_id: {
            "trakt_token": "tok",
            "trakt_refresh": "ref",
            "trakt_expires": "123",
        }.get(setting_id, ""),
    )

    payload = settings_backup.build_backup_payload(
        strip_sensitive=True,
        settings_xml_path=settings_path,
    )

    assert payload["settings"] == {"safe_value": "safe"}
    assert payload["dynamic_settings"] == {}
    assert payload["cache"] == {}
    assert payload["strip_sensitive"] is True
    assert payload["custom_stremio_addons_included"] is False


def test_build_backup_payload_includes_custom_stremio_addons(monkeypatch, tmp_path):
    settings_path = _write_settings_xml(
        tmp_path,
        """
    <category id='general'>
      <group id='backup'>
        <setting id='safe_value' type='string'><default /></setting>
      </group>
    </category>
    """,
    )
    custom_addon = {
        "manifest": {"id": "custom.one", "name": "Custom One"},
        "transportName": "custom",
        "transportUrl": "https://example.com/manifest.json",
    }
    another_addon = {
        "manifest": {"id": "builtin.one", "name": "Builtin"},
        "transportName": "stremio-account",
        "transportUrl": "https://account.example/manifest.json",
    }
    custom_key = "custom.one|https://example.com"
    fake_cache = FakeCache(
        {
            settings_backup.STREMIO_USER_ADDONS: [custom_addon, another_addon],
            settings_backup.STREMIO_ADDONS_KEY: settings_backup.encode_selected_ids(
                [custom_key, "builtin.one|https://account.example"]
            ),
            settings_backup.STREMIO_ADDONS_CATALOGS_KEY: settings_backup.encode_selected_ids(
                [custom_key]
            ),
            settings_backup.STREMIO_TV_ADDONS_KEY: settings_backup.encode_selected_ids(
                ["builtin.one|https://account.example"]
            ),
        }
    )

    monkeypatch.setattr(settings_backup, "cache", fake_cache)
    monkeypatch.setattr(settings_backup.ADDON, "getSetting", lambda setting_id: "value")
    monkeypatch.setattr(
        settings_backup,
        "get_cached_setting_property",
        lambda setting_id: {
            "trakt_token": "tok",
            "trakt_refresh": "ref",
            "trakt_expires": "123",
        }.get(setting_id, ""),
    )

    payload = settings_backup.build_backup_payload(
        strip_sensitive=False,
        settings_xml_path=settings_path,
    )

    assert payload["settings"] == {"safe_value": "value"}
    assert payload["dynamic_settings"] == {
        "trakt_token": "tok",
        "trakt_refresh": "ref",
        "trakt_expires": "123",
    }
    assert payload["cache"]["custom_stremio_addons"] == [custom_addon]
    assert payload["cache"]["custom_stremio_selections"] == {
        "stream": [custom_key],
        "catalog": [custom_key],
        "tv": [],
    }


def test_apply_backup_payload_replaces_custom_stremio_addons(monkeypatch, tmp_path):
    settings_path = _write_settings_xml(
        tmp_path,
        """
    <category id='general'>
      <group id='backup'>
        <setting id='safe_value' type='string'><default /></setting>
      </group>
    </category>
    """,
    )
    old_custom = {
        "manifest": {"id": "custom.old", "name": "Old Custom"},
        "transportName": "custom",
        "transportUrl": "https://old.example/manifest.json",
    }
    new_custom = {
        "manifest": {"id": "custom.new", "name": "New Custom"},
        "transportName": "custom",
        "transportUrl": "https://new.example/manifest.json",
    }
    account_addon = {
        "manifest": {"id": "account.one", "name": "Account"},
        "transportName": "stremio-account",
        "transportUrl": "https://account.example/manifest.json",
    }
    old_key = "custom.old|https://old.example"
    new_key = "custom.new|https://new.example"
    account_key = "account.one|https://account.example"
    fake_cache = FakeCache(
        {
            settings_backup.STREMIO_USER_ADDONS: [old_custom, account_addon],
            settings_backup.STREMIO_ADDONS_KEY: settings_backup.encode_selected_ids(
                [old_key, account_key]
            ),
            settings_backup.STREMIO_ADDONS_CATALOGS_KEY: settings_backup.encode_selected_ids(
                [old_key]
            ),
            settings_backup.STREMIO_TV_ADDONS_KEY: settings_backup.encode_selected_ids([]),
        }
    )
    set_calls = []
    property_calls = []

    monkeypatch.setattr(settings_backup, "cache", fake_cache)
    monkeypatch.setattr(
        settings_backup,
        "set_setting",
        lambda setting_id, value: set_calls.append((setting_id, value)),
    )
    monkeypatch.setattr(
        settings_backup,
        "set_cached_setting_property",
        lambda setting_id, value: property_calls.append((setting_id, value)),
    )

    payload = {
        "settings": {"safe_value": "restored", "unknown": "ignored"},
        "dynamic_settings": {
            "trakt_token": "tok",
            "trakt_refresh": "ref",
            "trakt_expires": "123",
        },
        "cache": {
            "custom_stremio_addons": [new_custom],
            "custom_stremio_selections": {
                "stream": [new_key],
                "catalog": [new_key],
                "tv": ["invalid"],
            },
        },
        "strip_sensitive": False,
    }

    settings_backup.apply_backup_payload(payload, settings_xml_path=settings_path)

    assert ("safe_value", "restored") in set_calls
    assert ("trakt_token", "tok") in property_calls
    assert ("trakt_refresh", "ref") in property_calls
    assert ("trakt_expires", "123") in property_calls
    assert fake_cache.store[settings_backup.STREMIO_USER_ADDONS] == [
        account_addon,
        new_custom,
    ]
    assert settings_backup.decode_selected_ids(
        fake_cache.store[settings_backup.STREMIO_ADDONS_KEY]
    ) == [account_key, new_key]
    assert settings_backup.decode_selected_ids(
        fake_cache.store[settings_backup.STREMIO_ADDONS_CATALOGS_KEY]
    ) == [new_key]
    assert (
        settings_backup.decode_selected_ids(fake_cache.store[settings_backup.STREMIO_TV_ADDONS_KEY])
        == []
    )


def test_apply_backup_payload_clears_scrubbed_fields_and_custom_addons(monkeypatch, tmp_path):
    settings_path = _write_settings_xml(
        tmp_path,
        """
    <category id='general'>
      <group id='backup'>
        <setting id='safe_value' type='string'><default /></setting>
        <setting id='jackett_apikey' type='string'><default /></setting>
        <setting id='stremio_email' type='string'><default /></setting>
        <setting id='is_trakt_auth' type='boolean'><default>false</default></setting>
        <setting id='trakt_user' type='string'><default /></setting>
      </group>
    </category>
    """,
    )
    old_custom = {
        "manifest": {"id": "custom.old", "name": "Old Custom"},
        "transportName": "custom",
        "transportUrl": "https://old.example/manifest.json",
    }
    account_addon = {
        "manifest": {"id": "account.one", "name": "Account"},
        "transportName": "stremio-account",
        "transportUrl": "https://account.example/manifest.json",
    }
    old_key = "custom.old|https://old.example"
    account_key = "account.one|https://account.example"
    fake_cache = FakeCache(
        {
            settings_backup.STREMIO_USER_ADDONS: [old_custom, account_addon],
            settings_backup.STREMIO_ADDONS_KEY: settings_backup.encode_selected_ids(
                [old_key, account_key]
            ),
            settings_backup.STREMIO_ADDONS_CATALOGS_KEY: settings_backup.encode_selected_ids(
                [old_key]
            ),
            settings_backup.STREMIO_TV_ADDONS_KEY: settings_backup.encode_selected_ids([]),
        }
    )
    set_calls = []
    property_calls = []

    monkeypatch.setattr(settings_backup, "cache", fake_cache)
    monkeypatch.setattr(
        settings_backup,
        "set_setting",
        lambda setting_id, value: set_calls.append((setting_id, value)),
    )
    monkeypatch.setattr(
        settings_backup,
        "set_cached_setting_property",
        lambda setting_id, value: property_calls.append((setting_id, value)),
    )

    settings_backup.apply_backup_payload(
        {
            "settings": {"safe_value": "restored"},
            "dynamic_settings": {"trakt_token": "tok"},
            "cache": {},
            "strip_sensitive": True,
        },
        settings_xml_path=settings_path,
    )

    assert ("safe_value", "restored") in set_calls
    assert ("jackett_apikey", "") in set_calls
    assert ("stremio_email", "") in set_calls
    assert ("is_trakt_auth", "false") in set_calls
    assert ("trakt_user", "unknown_user") in set_calls
    assert ("trakt_token", "") in property_calls
    assert ("trakt_refresh", "") in property_calls
    assert ("trakt_expires", "") in property_calls
    assert fake_cache.store[settings_backup.STREMIO_USER_ADDONS] == [account_addon]
    assert settings_backup.decode_selected_ids(
        fake_cache.store[settings_backup.STREMIO_ADDONS_KEY]
    ) == [account_key]
    assert (
        settings_backup.decode_selected_ids(
            fake_cache.store[settings_backup.STREMIO_ADDONS_CATALOGS_KEY]
        )
        == []
    )


def test_reset_all_settings_restores_defaults_and_clears_custom_addons(monkeypatch, tmp_path):
    settings_path = _write_settings_xml(
        tmp_path,
        """
    <category id='general'>
      <group id='backup'>
        <setting id='safe_value' type='string'><default>default-safe</default></setting>
        <setting id='is_trakt_auth' type='boolean'><default>false</default></setting>
        <setting id='trakt_user' type='string'><default /></setting>
      </group>
    </category>
    """,
    )
    old_custom = {
        "manifest": {"id": "custom.old", "name": "Old Custom"},
        "transportName": "custom",
        "transportUrl": "https://old.example/manifest.json",
    }
    account_addon = {
        "manifest": {"id": "account.one", "name": "Account"},
        "transportName": "stremio-account",
        "transportUrl": "https://account.example/manifest.json",
    }
    old_key = "custom.old|https://old.example"
    account_key = "account.one|https://account.example"
    fake_cache = FakeCache(
        {
            settings_backup.STREMIO_USER_ADDONS: [old_custom, account_addon],
            settings_backup.STREMIO_ADDONS_KEY: settings_backup.encode_selected_ids(
                [old_key, account_key]
            ),
            settings_backup.STREMIO_ADDONS_CATALOGS_KEY: settings_backup.encode_selected_ids(
                [old_key]
            ),
            settings_backup.STREMIO_TV_ADDONS_KEY: settings_backup.encode_selected_ids([]),
        }
    )
    set_calls = []
    property_calls = []

    monkeypatch.setattr(settings_backup, "cache", fake_cache)
    monkeypatch.setattr(
        settings_backup,
        "set_setting",
        lambda setting_id, value: set_calls.append((setting_id, value)),
    )
    monkeypatch.setattr(
        settings_backup,
        "set_cached_setting_property",
        lambda setting_id, value: property_calls.append((setting_id, value)),
    )

    settings_backup.reset_all_settings(settings_xml_path=settings_path)

    assert ("safe_value", "default-safe") in set_calls
    assert ("is_trakt_auth", "false") in set_calls
    assert ("trakt_user", "unknown_user") in set_calls
    assert ("trakt_token", "") in property_calls
    assert ("trakt_refresh", "") in property_calls
    assert ("trakt_expires", "") in property_calls
    assert fake_cache.store[settings_backup.STREMIO_USER_ADDONS] == [account_addon]
    assert settings_backup.decode_selected_ids(
        fake_cache.store[settings_backup.STREMIO_ADDONS_KEY]
    ) == [account_key]
    assert (
        settings_backup.decode_selected_ids(
            fake_cache.store[settings_backup.STREMIO_ADDONS_CATALOGS_KEY]
        )
        == []
    )


def test_factory_reset_clears_settings_caches_and_local_database(monkeypatch, tmp_path):
    settings_path = _write_settings_xml(
        tmp_path,
        """
    <category id='general'>
      <group id='backup'>
        <setting id='safe_value' type='string'><default>default-safe</default></setting>
      </group>
    </category>
    """,
    )
    fake_cache = FakeCache()
    set_calls = []
    helper_calls = []
    property_calls = []

    class FakePickleDatabase:
        def __init__(self):
            self.calls = []

        def set_key(self, key, value, commit=True):
            self.calls.append((key, value, commit))

        def commit(self):
            self.calls.append(("commit", None, None))

    fake_pickle_db = FakePickleDatabase()

    monkeypatch.setattr(settings_backup, "cache", fake_cache)
    monkeypatch.setattr(
        settings_backup,
        "set_setting",
        lambda setting_id, value: set_calls.append((setting_id, value)),
    )
    monkeypatch.setattr(
        settings_backup,
        "set_cached_setting_property",
        lambda setting_id, value: property_calls.append((setting_id, value)),
    )
    monkeypatch.setattr(settings_backup, "clear_all_cache", lambda: helper_calls.append("all"))
    monkeypatch.setattr(
        settings_backup, "clear_trakt_db_cache", lambda: helper_calls.append("trakt")
    )
    monkeypatch.setattr(settings_backup, "clear_tmdb_cache", lambda: helper_calls.append("tmdb"))
    monkeypatch.setattr(
        settings_backup, "clear_stremio_cache", lambda: helper_calls.append("stremio")
    )
    monkeypatch.setattr(
        settings_backup, "clear_debrid_cache", lambda: helper_calls.append("debrid")
    )
    monkeypatch.setattr(
        settings_backup, "clear_mdblist_cache", lambda: helper_calls.append("mdblist")
    )
    monkeypatch.setattr(settings_backup, "PickleDatabase", lambda: fake_pickle_db)

    settings_backup.factory_reset(settings_xml_path=settings_path)

    assert ("safe_value", "default-safe") in set_calls
    assert ("trakt_token", "") in property_calls
    assert ("trakt_refresh", "") in property_calls
    assert ("trakt_expires", "") in property_calls
    assert helper_calls == ["all", "trakt", "tmdb", "stremio", "debrid", "mdblist"]
    assert fake_cache.store["multi"] is None
    assert fake_cache.store["direct"] is None
    assert ("jt:watch", {}, False) in fake_pickle_db.calls
    assert ("jt:lth", {}, False) in fake_pickle_db.calls
    assert ("jt:lfh", {}, False) in fake_pickle_db.calls
    assert ("jt:lib", {}, False) in fake_pickle_db.calls
    assert ("search_query", "", False) in fake_pickle_db.calls
    assert ("search_catalog_query", "", False) in fake_pickle_db.calls
    assert ("anime_query", "", False) in fake_pickle_db.calls
    assert ("collection_search_query", "", False) in fake_pickle_db.calls
    assert ("commit", None, None) in fake_pickle_db.calls


def test_export_settings_backup_returns_silently_when_dialog_is_cancelled(monkeypatch):
    notifications = []
    build_calls = []

    monkeypatch.setattr(settings_backup, "_get_export_path", lambda: None)
    monkeypatch.setattr(
        settings_backup,
        "notification",
        lambda message: notifications.append(message),
    )
    monkeypatch.setattr(
        settings_backup,
        "build_backup_payload",
        lambda *args, **kwargs: build_calls.append(True),
    )

    settings_backup.export_settings_backup()

    assert notifications == []
    assert build_calls == []
