"""Tests for :mod:`mgf.common.config._paths` (rule CF-09).

The cross-platform branches (Windows / macOS) cannot run end-to-end
on the Linux CI host because pathlib binds ``Path`` to a
platform-specific class at import time. We test the logic via direct
``Path.home`` patching and ``os.name`` / ``sys.platform`` monkeypatch
combinations that hit each branch deterministically.
"""

from __future__ import annotations

import sys
from pathlib import Path

import pytest

from mgf.common import _identity
from mgf.common.config import default_config_path, expand_path, platform_dir


def _simulate_windows(monkeypatch: pytest.MonkeyPatch) -> None:
    """Monkeypatch ``os.name='nt'`` to exercise the Windows branch.

    Skips on Python < 3.12: pathlib refuses to instantiate a ``WindowsPath``
    on a POSIX host before 3.12 (relaxed in 3.12), so ``platform_dir`` would
    raise ``NotImplementedError`` mid-test. Skipping BEFORE the patch also
    means a failure can't leave ``os.name='nt'`` leaked into pytest's report
    machinery (which would crash the whole session — see tests/conftest.py)."""
    if sys.version_info < (3, 12):
        pytest.skip("WindowsPath cannot be instantiated on POSIX before Python 3.12")
    monkeypatch.setattr("os.name", "nt")


def test_xdg_override_wins_when_set(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """XDG env vars apply on every platform — power users keep
    control of their layout."""
    monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "custom"))
    assert platform_dir("config") == tmp_path / "custom"


def test_linux_config_dir(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    monkeypatch.setattr("os.name", "posix")
    monkeypatch.setattr("sys.platform", "linux")
    monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
    monkeypatch.setattr(Path, "home", classmethod(lambda _cls: tmp_path / "home"))
    assert platform_dir("config") == tmp_path / "home" / ".config"


def test_linux_state_dir(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    monkeypatch.setattr("os.name", "posix")
    monkeypatch.setattr("sys.platform", "linux")
    monkeypatch.delenv("XDG_STATE_HOME", raising=False)
    monkeypatch.setattr(Path, "home", classmethod(lambda _cls: tmp_path / "home"))
    assert platform_dir("state") == tmp_path / "home" / ".local" / "state"


def test_linux_data_dir(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    monkeypatch.setattr("os.name", "posix")
    monkeypatch.setattr("sys.platform", "linux")
    monkeypatch.delenv("XDG_DATA_HOME", raising=False)
    monkeypatch.setattr(Path, "home", classmethod(lambda _cls: tmp_path / "home"))
    assert platform_dir("data") == tmp_path / "home" / ".local" / "share"


def test_macos_uses_application_support(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    monkeypatch.setattr("os.name", "posix")
    monkeypatch.setattr("sys.platform", "darwin")
    monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
    monkeypatch.setattr(Path, "home", classmethod(lambda _cls: tmp_path / "home"))
    assert platform_dir("config") == tmp_path / "home" / "Library" / "Application Support"


def test_macos_state_uses_application_support(
    monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
    monkeypatch.setattr("os.name", "posix")
    monkeypatch.setattr("sys.platform", "darwin")
    monkeypatch.delenv("XDG_STATE_HOME", raising=False)
    monkeypatch.setattr(Path, "home", classmethod(lambda _cls: tmp_path / "home"))
    assert platform_dir("state") == tmp_path / "home" / "Library" / "Application Support"


def _norm(p: Path) -> str:
    """Normalise to forward slashes — the Windows branch returns a
    WindowsPath when ``os.name`` is monkeypatched on a Linux host,
    which then renders with backslashes. Comparing slash-normalised
    strings asserts the intent without taking a hard dependency on
    the host platform."""
    return str(p).replace("\\", "/")


def test_windows_config_uses_appdata(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    _simulate_windows(monkeypatch)
    monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
    monkeypatch.setenv("APPDATA", str(tmp_path / "Roaming"))
    assert _norm(platform_dir("config")) == _norm(tmp_path / "Roaming")


def test_windows_state_uses_localappdata(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    _simulate_windows(monkeypatch)
    monkeypatch.delenv("XDG_STATE_HOME", raising=False)
    monkeypatch.setenv("LOCALAPPDATA", str(tmp_path / "Local"))
    assert _norm(platform_dir("state")) == _norm(tmp_path / "Local")


def test_windows_falls_back_to_userprofile(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    """When neither APPDATA nor LOCALAPPDATA is set (rare, but happens
    on stripped-down Windows containers), fall back to the canonical
    AppData/Roaming or AppData/Local path under HOME."""
    _simulate_windows(monkeypatch)
    monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
    monkeypatch.delenv("XDG_STATE_HOME", raising=False)
    monkeypatch.delenv("APPDATA", raising=False)
    monkeypatch.delenv("LOCALAPPDATA", raising=False)
    monkeypatch.setattr(Path, "home", classmethod(lambda _cls: tmp_path / "user"))
    assert _norm(platform_dir("config")) == _norm(tmp_path / "user" / "AppData" / "Roaming")
    assert _norm(platform_dir("state")) == _norm(tmp_path / "user" / "AppData" / "Local")


# ---------------------------------------------------------------------------
# RA-14: additional Windows-branch coverage
# ---------------------------------------------------------------------------


def test_windows_data_uses_localappdata(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    """RA-14: the ``data`` kind on Windows resolves to LOCALAPPDATA
    same as ``state`` (Windows doesn't distinguish state/data the
    way XDG does; both flow through ``%LOCALAPPDATA%``)."""
    _simulate_windows(monkeypatch)
    monkeypatch.delenv("XDG_DATA_HOME", raising=False)
    monkeypatch.setenv("LOCALAPPDATA", str(tmp_path / "Local"))
    assert _norm(platform_dir("data")) == _norm(tmp_path / "Local")


def test_windows_data_falls_back_to_userprofile_appdata_local(
    monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
    """RA-14: data without LOCALAPPDATA falls through to
    ``<home>/AppData/Local`` — same shape as the state fallback."""
    _simulate_windows(monkeypatch)
    monkeypatch.delenv("XDG_DATA_HOME", raising=False)
    monkeypatch.delenv("LOCALAPPDATA", raising=False)
    monkeypatch.setattr(Path, "home", classmethod(lambda _cls: tmp_path / "user"))
    assert _norm(platform_dir("data")) == _norm(tmp_path / "user" / "AppData" / "Local")


def test_windows_xdg_override_wins_for_every_kind(
    monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
    """RA-14: an XDG override set on Windows takes precedence over
    APPDATA / LOCALAPPDATA — rule CF-09 (power users keep control
    across all platforms)."""
    _simulate_windows(monkeypatch)
    monkeypatch.setenv("APPDATA", str(tmp_path / "AppDataRoaming"))
    monkeypatch.setenv("LOCALAPPDATA", str(tmp_path / "AppDataLocal"))
    monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "custom_config"))
    monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path / "custom_state"))
    monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "custom_data"))
    assert _norm(platform_dir("config")) == _norm(tmp_path / "custom_config")
    assert _norm(platform_dir("state")) == _norm(tmp_path / "custom_state")
    assert _norm(platform_dir("data")) == _norm(tmp_path / "custom_data")


def test_windows_path_strings_roundtrip_via_pure_windows_path(
    monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
    """RA-14 sanity: the path STRING we hand back for Windows can
    be parsed by ``PureWindowsPath`` without throwing — i.e., the
    helpers don't produce a path with forbidden Windows chars
    (``:`` in a non-drive position, ``*``, ``?``, etc.).

    We can't easily test the chained ``platform_dir / app /
    'config.yaml'`` composition on a non-Windows host because
    Python's pathlib refuses to instantiate ``WindowsPath`` on
    Linux — the ``/`` operator under monkeypatched ``os.name='nt'``
    tries to construct a fresh WindowsPath and crashes. So the
    round-trip checks the FIRST stage (``platform_dir`` output)
    at the string level via ``PureWindowsPath`` only.
    """
    from pathlib import PureWindowsPath

    _simulate_windows(monkeypatch)
    monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
    monkeypatch.setattr(Path, "home", classmethod(lambda _cls: tmp_path / "user"))
    monkeypatch.delenv("APPDATA", raising=False)
    base = platform_dir("config")  # uses the home-fallback branch
    # PureWindowsPath validates the string shape against Windows
    # rules; raises on illegal chars. We assert the round-trip
    # works (no exception) and the path-shape invariants survive.
    pwp = PureWindowsPath(_norm(base))
    assert pwp.parts, "platform_dir produced an empty path on Windows"
    # The expected suffix for the config branch's fallback is
    # AppData/Roaming.
    assert pwp.parts[-2:] == ("AppData", "Roaming")


# ---------------------------------------------------------------------------
# default_config_path
# ---------------------------------------------------------------------------


def test_default_config_path_explicit_app(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "x"))
    assert default_config_path("vmanager") == tmp_path / "x" / "vmanager" / "config.yaml"


def test_default_config_path_falls_back_to_identity(
    monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
    """When ``app`` is omitted, reads from :func:`mgf.common.app_name`."""
    monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "x"))
    prior_name = _identity._APP_NAME
    _identity._set_identity("custom-app", "0.0.0")
    try:
        assert default_config_path() == tmp_path / "x" / "custom-app" / "config.yaml"
    finally:
        _identity._APP_NAME = prior_name


# ---------------------------------------------------------------------------
# expand_path
# ---------------------------------------------------------------------------


def test_expand_path_expands_tilde(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("HOME", "/home/tester")
    assert expand_path("~/foo") == Path("/home/tester/foo")


def test_expand_path_expands_envvars(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("MGF_TEST_ROOT", "/opt/x")
    assert expand_path("$MGF_TEST_ROOT/bar") == Path("/opt/x/bar")


def test_expand_path_passes_through_non_path_values() -> None:
    assert expand_path(42) == 42
    assert expand_path(None) is None
