"""Digest-verified promotion policy packaged with the profile wheel."""

from __future__ import annotations

import hashlib
import hmac
import json
from importlib import resources
from typing import Final, TypeAlias

POLICY_RESOURCE_NAME: Final = "diagnostic-promotion-policy.json"
DIAGNOSTIC_PROMOTION_POLICY_SHA256: Final = (
    "0e5cc387f83810b58998666b8341ad1f32f85fa59d97b67a57c5ebff5a0ffb5a"
)


_ALLOWED_ARTIFACT_KINDS: Final = (
    "code",
    "test",
    "documentation",
    "migration",
    "config_schema",
)
_FORBIDDEN_ARTIFACT_KINDS: Final = (
    "runtime_data",
    "capability",
    "session",
    "ledger",
    "customer_data",
    "credential",
    "token",
    "deployment",
)
_PROFILE_MARKERS: Final = (
    "pyproject.toml",
    "checkin_cli/__init__.py",
    "tests/",
)
_HERMES_MARKERS: Final = (
    "gateway/__init__.py",
    "gateway/platforms/nutrition_coaching.py",
    "tests/gateway/",
)
_ALLOWED_SUFFIXES: Final = (".html", ".md", ".py", ".pyi", ".toml")
_CONFIG_ALLOWLIST: Final = (
    "/schema_version",
    "/diagnostic_isolated",
    "/diagnostic_isolated/enabled",
    "/diagnostic_isolated/contract_version",
    "/diagnostic_isolated/max_provider_timeout_seconds",
    "/diagnostic_isolated/test_bot_digest",
    "/diagnostic_isolated/operator_destination_digest",
    "/diagnostic_isolated/customer_destination_digest",
    "/diagnostic_isolated/promotion_policy_digest",
)
_REQUIRED_APPROVALS: Final = (
    "tests_passed",
    "architect_approved",
    "qa_approved",
    "human_release_approval",
)
_POLICY_KEYS: Final = frozenset(
    {
        "schema_version",
        "allowed_artifact_kinds",
        "forbidden_artifact_kinds",
        "profile_markers",
        "hermes_markers",
        "allowed_suffixes",
        "migration_allowlist",
        "config_allowlist",
        "requires",
        "deployment_allowed",
    }
)

Policy: TypeAlias = dict[str, object]


class PromotionPolicyError(ValueError):
    """Raised when the packaged promotion policy is absent, changed, or invalid."""


def _read_policy_resource() -> bytes:
    try:
        resource = resources.files(__name__).joinpath(POLICY_RESOURCE_NAME)
        raw = resource.read_bytes()
    except (AttributeError, FileNotFoundError, OSError, TypeError) as exc:
        raise PromotionPolicyError("promotion policy resource is missing") from exc
    if not isinstance(raw, bytes):
        raise PromotionPolicyError("promotion policy resource did not return bytes")
    return raw


def load_policy_bytes() -> bytes:
    """Read the packaged policy and verify its exact raw-byte digest."""
    raw = _read_policy_resource()
    actual = hashlib.sha256(raw).hexdigest()
    if not hmac.compare_digest(actual, DIAGNOSTIC_PROMOTION_POLICY_SHA256):
        raise PromotionPolicyError("promotion policy resource digest mismatch")
    return raw


def _validate_string_list(
    value: object,
    *,
    field: str,
    expected: tuple[str, ...] | None = None,
    allow_empty: bool = False,
) -> list[str]:
    if not isinstance(value, list) or any(not isinstance(item, str) for item in value):
        raise PromotionPolicyError(f"promotion policy field {field!r} must be a string list")
    if not allow_empty and not value:
        raise PromotionPolicyError(f"promotion policy field {field!r} must not be empty")
    if len(value) != len(set(value)):
        raise PromotionPolicyError(f"promotion policy field {field!r} contains duplicates")
    if expected is not None and value != list(expected):
        raise PromotionPolicyError(f"promotion policy field {field!r} is not the approved allowlist")
    return value


def _validate_relative_paths(value: list[str], *, field: str) -> None:
    for item in value:
        if (
            not item
            or item.startswith("/")
            or "\\" in item
            or "*" in item
            or any(part == ".." for part in item.split("/"))
        ):
            raise PromotionPolicyError(f"promotion policy field {field!r} has an unsafe path")


def _validate_policy(document: object) -> Policy:
    if not isinstance(document, dict):
        raise PromotionPolicyError("promotion policy must be a JSON object")
    if set(document) != _POLICY_KEYS:
        raise PromotionPolicyError("promotion policy has missing or extra fields")
    if document["schema_version"] != "diagnostic_promotion_policy_v1":
        raise PromotionPolicyError("unsupported promotion policy schema version")

    _validate_string_list(
        document["allowed_artifact_kinds"],
        field="allowed_artifact_kinds",
        expected=_ALLOWED_ARTIFACT_KINDS,
    )
    _validate_string_list(
        document["forbidden_artifact_kinds"],
        field="forbidden_artifact_kinds",
        expected=_FORBIDDEN_ARTIFACT_KINDS,
    )
    profile_markers = _validate_string_list(
        document["profile_markers"], field="profile_markers", expected=_PROFILE_MARKERS
    )
    hermes_markers = _validate_string_list(
        document["hermes_markers"], field="hermes_markers", expected=_HERMES_MARKERS
    )
    _validate_relative_paths(profile_markers, field="profile_markers")
    _validate_relative_paths(hermes_markers, field="hermes_markers")

    suffixes = _validate_string_list(
        document["allowed_suffixes"], field="allowed_suffixes", expected=_ALLOWED_SUFFIXES
    )
    if any(not suffix.startswith(".") or "/" in suffix or "\\" in suffix for suffix in suffixes):
        raise PromotionPolicyError("promotion policy has an unsafe file suffix")

    migrations = _validate_string_list(
        document["migration_allowlist"],
        field="migration_allowlist",
        allow_empty=True,
    )
    _validate_relative_paths(migrations, field="migration_allowlist")

    config_allowlist = _validate_string_list(
        document["config_allowlist"],
        field="config_allowlist",
        expected=_CONFIG_ALLOWLIST,
    )
    if any(
        not item.startswith("/")
        or "*" in item
        or "\\" in item
        or any(part == ".." for part in item.split("/"))
        for item in config_allowlist
    ):
        raise PromotionPolicyError("promotion policy has an unsafe config schema path")

    _validate_string_list(
        document["requires"], field="requires", expected=_REQUIRED_APPROVALS
    )
    if document["deployment_allowed"] is not False:
        raise PromotionPolicyError("promotion policy must forbid deployment")

    # Return a fresh shallow copy so callers cannot mutate the parsed object held
    # by a future implementation that adds safe caching.
    return dict(document)


def load_policy() -> Policy:
    """Load and validate the policy only after its raw bytes match the digest."""
    raw = load_policy_bytes()
    try:
        document = json.loads(raw.decode("utf-8"))
    except (UnicodeDecodeError, json.JSONDecodeError) as exc:
        raise PromotionPolicyError("promotion policy is not valid UTF-8 JSON") from exc
    return _validate_policy(document)



__all__ = [
    "DIAGNOSTIC_PROMOTION_POLICY_SHA256",
    "POLICY_RESOURCE_NAME",
    "PromotionPolicyError",
    "load_policy",
    "load_policy_bytes",
]
