"""Offline wheel and resource-integrity contracts for the diagnostic policy."""

from __future__ import annotations

import hashlib
import os
import shutil
import subprocess
import sys
import zipfile
from pathlib import Path

import pytest

from checkin_cli.policies import (
    DIAGNOSTIC_PROMOTION_POLICY_SHA256,
    POLICY_RESOURCE_NAME,
    PromotionPolicyError,
    load_policy,
    load_policy_bytes,
)

PROJECT_ROOT = Path(
    os.environ.get("DUALCOACH_PROFILE_PACKAGE", Path(__file__).resolve().parents[1])
).resolve()
POLICY_PATH = PROJECT_ROOT / "checkin_cli" / "policies" / POLICY_RESOURCE_NAME
POLICY_MEMBER = f"checkin_cli/policies/{POLICY_RESOURCE_NAME}"

# This literal is intentionally independent of the source-tree read. It is the
# release contract for the exact bytes served by importlib.resources.
EXPECTED_POLICY_SHA256 = "0e5cc387f83810b58998666b8341ad1f32f85fa59d97b67a57c5ebff5a0ffb5a"

_POLICY_PROBE = r"""
import hashlib
from importlib import resources
from checkin_cli.policies import (
    DIAGNOSTIC_PROMOTION_POLICY_SHA256,
    load_policy,
    load_policy_bytes,
)

raw = resources.files("checkin_cli.policies").joinpath("diagnostic-promotion-policy.json").read_bytes()
actual = hashlib.sha256(raw).hexdigest()
assert actual == __import__("sys").argv[1]
assert actual == DIAGNOSTIC_PROMOTION_POLICY_SHA256
assert raw == load_policy_bytes()
load_policy()
print(actual)
"""

_REJECTION_PROBE = r"""
from checkin_cli.policies import PromotionPolicyError, load_policy

try:
    load_policy()
except PromotionPolicyError as exc:
    print(type(exc).__name__)
else:
    raise SystemExit("tampered or missing policy unexpectedly loaded")
"""


def _offline_env(target: Path) -> dict[str, str]:
    env = os.environ.copy()
    base_site = (
        Path(sys.base_prefix)
        / "lib"
        / f"python{sys.version_info.major}.{sys.version_info.minor}"
        / "site-packages"
    )
    python_paths = [str(target)]
    if base_site.is_dir():
        python_paths.append(str(base_site))
    env["PYTHONPATH"] = os.pathsep.join(python_paths)
    env["PYTHONNOUSERSITE"] = "1"
    env["PIP_NO_INDEX"] = "1"
    env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
    return env


def _build_wheel(tmp_path: Path) -> Path:
    source = tmp_path / "source"
    shutil.copytree(
        PROJECT_ROOT,
        source,
        ignore=shutil.ignore_patterns(
            ".git",
            ".pytest_cache",
            ".venv",
            "__pycache__",
            "*.egg-info",
            "build",
            "dist",
        ),
    )
    for directory in (source, *(path for path in source.rglob("*") if path.is_dir())):
        directory.chmod(directory.stat().st_mode | 0o700)
    wheel_dir = tmp_path / "wheel"
    wheel_dir.mkdir()
    subprocess.run(
        [
            sys.executable,
            "-m",
            "pip",
            "wheel",
            "--no-build-isolation",
            "--no-cache-dir",
            "--no-deps",
            "--no-index",
            "--wheel-dir",
            str(wheel_dir),
            str(source),
        ],
        cwd=source,
        env=_offline_env(source),
        check=True,
    )
    wheels = sorted(wheel_dir.glob("*.whl"))
    assert len(wheels) == 1
    return wheels[0]


def _extract_wheel(wheel: Path, target: Path) -> None:
    target.mkdir()
    with zipfile.ZipFile(wheel) as archive:
        archive.extractall(target)


def _run_resource_probe(target: Path, expected_digest: str) -> subprocess.CompletedProcess[str]:
    return subprocess.run(
        [sys.executable, "-c", _POLICY_PROBE, expected_digest],
        cwd=target,
        env=_offline_env(target),
        text=True,
        capture_output=True,
        check=False,
    )


def _assert_loader_rejects(target: Path) -> None:
    result = subprocess.run(
        [sys.executable, "-c", _REJECTION_PROBE],
        cwd=target,
        env=_offline_env(target),
        text=True,
        capture_output=True,
        check=False,
    )
    assert result.returncode == 0, result.stdout + result.stderr
    assert result.stdout.strip() == "PromotionPolicyError"


def test_source_policy_is_digest_verified_before_parsing() -> None:
    raw = load_policy_bytes()
    assert raw == POLICY_PATH.read_bytes()
    assert hashlib.sha256(raw).hexdigest() == EXPECTED_POLICY_SHA256
    assert DIAGNOSTIC_PROMOTION_POLICY_SHA256 == EXPECTED_POLICY_SHA256

    policy = load_policy()
    assert set(policy) == {
        "schema_version",
        "allowed_artifact_kinds",
        "forbidden_artifact_kinds",
        "profile_markers",
        "hermes_markers",
        "allowed_suffixes",
        "migration_allowlist",
        "config_allowlist",
        "requires",
        "deployment_allowed",
    }
    assert policy["deployment_allowed"] is False
    assert "runtime_data" in policy["forbidden_artifact_kinds"]
    assert "deployment" in policy["forbidden_artifact_kinds"]


def test_wheel_contains_exact_policy_and_installed_resource_bytes(tmp_path: Path) -> None:
    raw = POLICY_PATH.read_bytes()
    assert hashlib.sha256(raw).hexdigest() == EXPECTED_POLICY_SHA256

    wheel = _build_wheel(tmp_path)
    with zipfile.ZipFile(wheel) as archive:
        members = [name for name in archive.namelist() if name == POLICY_MEMBER]
        assert members == [POLICY_MEMBER]
        assert archive.read(POLICY_MEMBER) == raw

    target = tmp_path / "installed"
    _extract_wheel(wheel, target)
    result = _run_resource_probe(target, EXPECTED_POLICY_SHA256)
    assert result.returncode == 0, result.stdout + result.stderr
    assert result.stdout.strip() == EXPECTED_POLICY_SHA256


def test_installed_resource_loader_rejects_missing_and_mismatched_bytes(tmp_path: Path) -> None:
    wheel = _build_wheel(tmp_path)
    installed = tmp_path / "installed"
    _extract_wheel(wheel, installed)

    missing = tmp_path / "missing"
    shutil.copytree(installed, missing)
    (missing / POLICY_MEMBER).unlink()
    _assert_loader_rejects(missing)

    mismatched = tmp_path / "mismatched"
    shutil.copytree(installed, mismatched)
    (mismatched / POLICY_MEMBER).write_bytes(b"{}\n")
    _assert_loader_rejects(mismatched)


@pytest.mark.parametrize(
    "bad_bytes",
    [b"{}", b"not-json\n"],
    ids=["wrong-schema", "malformed-json"],
)
def test_source_loader_does_not_accept_a_digest_mismatch(monkeypatch, bad_bytes: bytes) -> None:
    from checkin_cli import policies

    monkeypatch.setattr(
        policies,
        "_read_policy_resource",
        lambda: bad_bytes,
    )
    with pytest.raises(PromotionPolicyError, match="digest mismatch"):
        policies.load_policy()
