"""Unit tests for Widevine CDM hint-file seeding (cloakbrowser/widevine.py)."""

import json

import pytest

from cloakbrowser import widevine
from cloakbrowser.widevine import resolve_widevine_cdm_dir, seed_widevine_hint

_HINT = "WidevineCdm/latest-component-updated-widevine-cdm"


@pytest.fixture(autouse=True)
def _force_linux(monkeypatch, tmp_path):
    """Run as if on Linux unless a test overrides it (seeding is Linux-only)."""
    monkeypatch.setattr(widevine.platform, "system", lambda: "Linux")
    monkeypatch.delenv("CLOAKBROWSER_WIDEVINE", raising=False)
    monkeypatch.delenv("CLOAKBROWSER_WIDEVINE_CDM", raising=False)
    # Isolate the cache-root fallback from any real ~/.cloakbrowser on the host.
    monkeypatch.setenv("CLOAKBROWSER_CACHE_DIR", str(tmp_path / "_isolated_cache"))


def _make_cdm(dirpath):
    """Create a fake WidevineCdm dir with a manifest.json."""
    dirpath.mkdir(parents=True, exist_ok=True)
    (dirpath / "manifest.json").write_text('{"version": "4.10.3050.0"}')
    return dirpath


def _binary(tmp_path):
    """Return a fake chrome binary path inside its own dir."""
    bdir = tmp_path / "bin"
    bdir.mkdir(parents=True, exist_ok=True)
    return bdir / "chrome"


def test_seeds_hint_next_to_binary(tmp_path):
    """CDM in <binary dir>/WidevineCdm -> hint file written with abs Path."""
    binary = _binary(tmp_path)
    cdm = _make_cdm(binary.parent / "WidevineCdm")

    profile = tmp_path / "profile"
    seed_widevine_hint(profile, binary)

    hint = profile / _HINT
    assert hint.is_file()
    assert json.loads(hint.read_text())["Path"] == str(cdm.resolve())


def test_seeds_hint_from_env_var(tmp_path, monkeypatch):
    """CLOAKBROWSER_WIDEVINE_CDM takes priority and is used as the Path."""
    cdm = _make_cdm(tmp_path / "custom_cdm")
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE_CDM", str(cdm))

    profile = tmp_path / "profile"
    seed_widevine_hint(profile, _binary(tmp_path))

    assert json.loads((profile / _HINT).read_text())["Path"] == str(cdm.resolve())


def test_no_cdm_no_file(tmp_path):
    """No CDM present -> nothing written, no exception."""
    profile = tmp_path / "profile"
    seed_widevine_hint(profile, _binary(tmp_path))
    assert not (profile / _HINT).exists()


def test_kill_switch_disables(tmp_path, monkeypatch):
    """CLOAKBROWSER_WIDEVINE=0 disables seeding even when a CDM exists."""
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE_CDM", str(_make_cdm(tmp_path / "custom_cdm")))
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE", "0")

    profile = tmp_path / "profile"
    seed_widevine_hint(profile, _binary(tmp_path))
    assert not (profile / _HINT).exists()


def test_idempotent(tmp_path, monkeypatch):
    """Seeding twice leaves the same correct content and doesn't error."""
    cdm = _make_cdm(tmp_path / "custom_cdm")
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE_CDM", str(cdm))

    profile = tmp_path / "profile"
    binary = _binary(tmp_path)
    seed_widevine_hint(profile, binary)
    seed_widevine_hint(profile, binary)
    assert json.loads((profile / _HINT).read_text())["Path"] == str(cdm.resolve())


def test_noop_on_non_linux(tmp_path, monkeypatch):
    """On non-Linux, seeding is a no-op even with a CDM present."""
    monkeypatch.setattr(widevine.platform, "system", lambda: "Windows")
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE_CDM", str(_make_cdm(tmp_path / "cdm")))

    profile = tmp_path / "profile"
    seed_widevine_hint(profile, _binary(tmp_path))
    assert not (profile / _HINT).exists()


def test_resolve_requires_manifest(tmp_path, monkeypatch):
    """A WidevineCdm dir without manifest.json is not treated as a CDM."""
    bogus = tmp_path / "custom_cdm"
    bogus.mkdir()
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE_CDM", str(bogus))
    assert resolve_widevine_cdm_dir(_binary(tmp_path)) is None


def test_env_var_is_exclusive(tmp_path, monkeypatch):
    """An invalid CLOAKBROWSER_WIDEVINE_CDM skips seeding — no fallback to binary dir."""
    binary = _binary(tmp_path)
    _make_cdm(binary.parent / "WidevineCdm")  # valid CDM next to binary
    bogus = tmp_path / "bogus"
    bogus.mkdir()  # set but no manifest.json
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE_CDM", str(bogus))
    assert resolve_widevine_cdm_dir(binary) is None


def test_resolve_falls_back_to_cache_root(tmp_path, monkeypatch):
    """No CDM next to the binary -> auto-detect falls back to <cache>/WidevineCdm.

    Simulates the Pro case: a Pro binary sits in its own chromium-<ver>-pro dir
    with no adjacent CDM, while the Docker auto-fetch left one at the cache root.
    """
    cache = tmp_path / "cache"
    monkeypatch.setenv("CLOAKBROWSER_CACHE_DIR", str(cache))
    cdm = _make_cdm(cache / "WidevineCdm")
    pro_binary = tmp_path / "chromium-148.0-pro" / "chrome"
    pro_binary.parent.mkdir(parents=True)  # binary dir exists, but has no CDM
    assert resolve_widevine_cdm_dir(pro_binary) == cdm.resolve()


def test_resolve_binary_dir_wins_over_cache_root(tmp_path, monkeypatch):
    """A manual sideload next to the binary takes precedence over the cache root."""
    cache = tmp_path / "cache"
    monkeypatch.setenv("CLOAKBROWSER_CACHE_DIR", str(cache))
    _make_cdm(cache / "WidevineCdm")  # cache-root CDM present...
    binary = _binary(tmp_path)
    next_to = _make_cdm(binary.parent / "WidevineCdm")  # ...but sideload wins
    assert resolve_widevine_cdm_dir(binary) == next_to.resolve()


def test_seeds_hint_from_cache_root_fallback(tmp_path, monkeypatch):
    """End-to-end: a cache-root CDM seeds the hint for a binary with none adjacent."""
    cache = tmp_path / "cache"
    monkeypatch.setenv("CLOAKBROWSER_CACHE_DIR", str(cache))
    cdm = _make_cdm(cache / "WidevineCdm")
    profile = tmp_path / "profile"
    seed_widevine_hint(profile, _binary(tmp_path))  # binary has no adjacent CDM
    assert json.loads((profile / _HINT).read_text())["Path"] == str(cdm.resolve())


def test_empty_env_var_is_exclusive(tmp_path, monkeypatch):
    """An empty (but set) CLOAKBROWSER_WIDEVINE_CDM resolves to None — and must NOT
    pick up a stray manifest.json in the working directory (``Path("")`` -> ``.``)."""
    binary = _binary(tmp_path)
    _make_cdm(binary.parent / "WidevineCdm")  # valid CDM next to binary
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE_CDM", "")
    cwd = tmp_path / "cwd"
    cwd.mkdir()
    (cwd / "manifest.json").write_text("{}")  # stray manifest in CWD must be ignored
    monkeypatch.chdir(cwd)
    assert resolve_widevine_cdm_dir(binary) is None


def test_empty_user_data_dir_skips(tmp_path, monkeypatch):
    """Empty user_data_dir (ephemeral profile) -> no CWD pollution, no seeding."""
    cdm = _make_cdm(tmp_path / "custom_cdm")
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE_CDM", str(cdm))
    monkeypatch.chdir(tmp_path)
    seed_widevine_hint("", _binary(tmp_path))
    assert not (tmp_path / "WidevineCdm").exists()


def test_never_raises_on_write_failure(tmp_path, monkeypatch):
    """A write failure (hint dir path is a file) must not raise — launch must not break."""
    cdm = _make_cdm(tmp_path / "custom_cdm")
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE_CDM", str(cdm))
    profile = tmp_path / "profile"
    profile.mkdir()
    # Block mkdir of <profile>/WidevineCdm by occupying that path with a file.
    (profile / "WidevineCdm").write_text("not a dir")

    seed_widevine_hint(profile, _binary(tmp_path))  # must not raise


def test_rewrites_corrupt_existing_hint(tmp_path, monkeypatch):
    """A non-UTF8 / mismatched existing hint is overwritten, without raising."""
    cdm = _make_cdm(tmp_path / "custom_cdm")
    monkeypatch.setenv("CLOAKBROWSER_WIDEVINE_CDM", str(cdm))
    profile = tmp_path / "profile"
    hint = profile / "WidevineCdm" / _HINT.split("/")[-1]
    hint.parent.mkdir(parents=True)
    hint.write_bytes(b"\xff\xfe not valid utf-8")

    seed_widevine_hint(profile, _binary(tmp_path))  # must not raise

    # corrupt content replaced with a valid hint pointing at the CDM
    assert json.loads(hint.read_text())["Path"] == str(cdm.resolve())
