"""The immutable GLM-5.2 Max alpha acceptance contract.

The contract exists to answer one question that cannot be answered honestly after
the fact: *what would have counted as success?*

Its thresholds are locked before the target ever runs (DGR-017), and DGR-020 reads
them back to publish an ``alpha`` or ``stop`` verdict. The whole point is that the
gap between those two moments is where a threshold quietly becomes "0.1 tokens/sec
was always the goal". So the document carries ``contract_sha256`` over its canonical content, while the
approved v1 digest is pinned independently in code. :func:`load_alpha_contract`
recomputes the self-digest and then requires that trusted pre-execution digest.
Changing a threshold and re-sealing under the same identity is rejected; an
amendment requires a new supported contract identity under human review.

The parsed contract recursively freezes nested mappings and sequences. Thresholds
therefore cannot change between verification and use, and :meth:`AlphaContract.to_dict`
returns an isolated mutable copy for diagnostics and tests.
"""

from __future__ import annotations

import json
import re
from dataclasses import dataclass
from importlib.resources import files
from pathlib import Path
from types import MappingProxyType
from typing import Any, Mapping

from .manifest import (
    ALPHA_QUANTIZATION,
    ALPHA_SHARD_COUNT,
    ArchitectureSnapshot,
    GlmTargetError,
    TargetManifest,
    canonical_sha256,
)

ALPHA_CONTRACT_SCHEMA_VERSION = 1
ALPHA_CONTRACT_VERSION = 1
ALPHA_CONTRACT_ID = "glm-5.2-max-alpha/v1"
ALPHA_CONTRACT_V1_SHA256 = "aab23220280c053a3c14ff559df3cb5c9e1bf7f0f7188c6519e2e9d9ad036ed9"

_CONTRACT_RESOURCE = "alpha-contract.json"

DIGEST_FIELD = "contract_sha256"

VERDICT_ALPHA = "alpha"
VERDICT_STOP = "stop"

# Every section the roadmap's acceptance matrix (section 5) locks. A contract that
# omits one is not a weaker contract, it is an unreviewable one.
REQUIRED_SECTIONS: tuple[str, ...] = (
    "identity_and_fit",
    "semantic_correctness",
    "target_run",
    "performance",
    "reliability",
    "storage",
)


class AlphaContractError(GlmTargetError):
    """Raised when the alpha contract is missing, malformed, or has been mutated."""


def contract_signing_payload(document: Mapping[str, Any]) -> dict:
    """The contract content the digest covers: everything except the digest itself."""
    unsigned = dict(document)
    unsigned.pop(DIGEST_FIELD, None)
    return unsigned


def compute_contract_digest(document: Mapping[str, Any]) -> str:
    """SHA-256 over the canonical contract content."""
    return canonical_sha256(contract_signing_payload(_thaw_json(document)))


def _freeze_json(value: Any) -> Any:
    if isinstance(value, Mapping):
        return MappingProxyType({str(key): _freeze_json(item) for key, item in value.items()})
    if isinstance(value, list):
        return tuple(_freeze_json(item) for item in value)
    return value


def _thaw_json(value: Any) -> Any:
    if isinstance(value, Mapping):
        return {str(key): _thaw_json(item) for key, item in value.items()}
    if isinstance(value, tuple):
        return [_thaw_json(item) for item in value]
    return value


@dataclass(frozen=True)
class AlphaContract:
    """A locked, digest-bound alpha acceptance contract."""

    schema_version: int
    contract_version: int
    contract_id: str
    locked_at: str
    locked_by: str
    target: Mapping[str, Any]
    sections: Mapping[str, Mapping[str, Any]]
    verdicts: tuple[str, ...]
    amendment_policy: str
    digest: str
    raw: Mapping[str, Any]
    source: str = "<memory>"

    def section(self, name: str) -> Mapping[str, Any]:
        if name not in self.sections:
            raise AlphaContractError(f"contract section {name!r} is missing from {self.source}")
        return self.sections[name]

    def threshold(self, section: str, key: str) -> Any:
        block = self.section(section)
        if key not in block:
            raise AlphaContractError(
                f"threshold {section}.{key} is not locked in {self.source}; an unlocked "
                "threshold cannot be used to judge a result"
            )
        return block[key]

    def to_dict(self) -> dict:
        return _thaw_json(self.raw)


def parse_alpha_contract(data: Any, source: str = "<memory>") -> AlphaContract:
    """Validate a contract document and verify it has not been mutated since locking."""
    if not isinstance(data, Mapping):
        raise AlphaContractError(f"contract root in {source} must be a JSON object")

    schema_version = data.get("schema_version")
    if (
        not isinstance(schema_version, int)
        or isinstance(schema_version, bool)
        or schema_version != ALPHA_CONTRACT_SCHEMA_VERSION
    ):
        raise AlphaContractError(
            f"{source} declares alpha-contract schema version {schema_version!r}, but this "
            f"node reads version {ALPHA_CONTRACT_SCHEMA_VERSION}"
        )

    contract_version = data.get("contract_version")
    if (
        not isinstance(contract_version, int)
        or isinstance(contract_version, bool)
        or contract_version != ALPHA_CONTRACT_VERSION
    ):
        raise AlphaContractError(
            f"{source} declares contract version {contract_version!r}, but this node "
            f"reads version {ALPHA_CONTRACT_VERSION}"
        )

    contract_id = data.get("contract_id")
    if contract_id != ALPHA_CONTRACT_ID:
        raise AlphaContractError(
            f"{source} declares contract_id {contract_id!r}, but this node is locked "
            f"to {ALPHA_CONTRACT_ID!r}"
        )

    for field in ("locked_at", "locked_by"):
        value = data.get(field)
        if not isinstance(value, str) or not value.strip():
            raise AlphaContractError(f"{source} must carry a non-empty {field}")

    declared = data.get(DIGEST_FIELD)
    if not isinstance(declared, str) or not declared:
        raise AlphaContractError(
            f"{source} carries no {DIGEST_FIELD}; an unsealed contract cannot prove it "
            "predates the results it judges"
        )

    computed = compute_contract_digest(data)
    if computed != declared:
        raise AlphaContractError(
            f"{source} has been modified since it was locked: its content hashes to "
            f"{computed}, but it declares {declared}. Alpha thresholds are locked before "
            "target execution and may not be weakened afterwards. To change them, open a "
            "new contract_id under human review; do not edit this one."
        )

    if not data.get("locked_before_target_execution"):
        raise AlphaContractError(
            f"{source} does not assert locked_before_target_execution; a contract written "
            "after the results are known is not a contract"
        )

    missing = [name for name in REQUIRED_SECTIONS if not isinstance(data.get(name), Mapping)]
    if missing:
        raise AlphaContractError(
            f"{source} is missing locked acceptance section(s) {missing}"
        )

    verdicts = data.get("verdicts")
    if not isinstance(verdicts, list) or sorted(verdicts) != sorted([VERDICT_ALPHA, VERDICT_STOP]):
        raise AlphaContractError(
            f"{source} must offer exactly the verdicts {[VERDICT_ALPHA, VERDICT_STOP]}; a "
            "third outcome is how 'it loaded' becomes a pass"
        )

    target = data.get("target")
    if not isinstance(target, Mapping):
        raise AlphaContractError(f"{source} is missing its 'target' block")
    for field in (
        "source_repo_id",
        "source_revision",
        "gguf_repo_id",
        "gguf_revision",
        "quantization",
        "target_manifest_sha256",
        "architecture_snapshot_sha256",
        "reasoning_effort",
    ):
        if not target.get(field):
            raise AlphaContractError(f"{source} target block is missing {field!r}")
    for field in ("source_revision", "gguf_revision"):
        if not re.fullmatch(r"[0-9a-f]{40}", str(target[field])):
            raise AlphaContractError(f"{source} target.{field} is not a full commit revision")
    for field in ("target_manifest_sha256", "architecture_snapshot_sha256"):
        if not re.fullmatch(r"[0-9a-f]{64}", str(target[field])):
            raise AlphaContractError(f"{source} target.{field} is not a SHA-256 digest")
    if target["quantization"] != ALPHA_QUANTIZATION:
        raise AlphaContractError(
            f"{source} targets {target['quantization']!r}, not locked alpha quantization "
            f"{ALPHA_QUANTIZATION!r}"
        )
    if target["reasoning_effort"] != "max":
        raise AlphaContractError(f"{source} must lock reasoning_effort='max'")
    shard_count = target.get("shard_count")
    if (
        not isinstance(shard_count, int)
        or isinstance(shard_count, bool)
        or shard_count != ALPHA_SHARD_COUNT
    ):
        raise AlphaContractError(
            f"{source} target.shard_count must be exactly {ALPHA_SHARD_COUNT}"
        )
    total_bytes = target.get("total_bytes")
    if not isinstance(total_bytes, int) or isinstance(total_bytes, bool) or total_bytes <= 0:
        raise AlphaContractError(f"{source} target.total_bytes must be a positive integer")

    amendment_policy = data.get("amendment_policy")
    if not isinstance(amendment_policy, str) or not amendment_policy.strip():
        raise AlphaContractError(f"{source} must state its amendment policy")

    if declared != ALPHA_CONTRACT_V1_SHA256:
        raise AlphaContractError(
            f"{source} is a re-sealed mutation of {ALPHA_CONTRACT_ID}: digest "
            f"{declared} does not match the trusted pre-execution digest "
            f"{ALPHA_CONTRACT_V1_SHA256}. An amendment requires a new supported "
            "contract identity under human review."
        )

    frozen = _freeze_json(data)

    return AlphaContract(
        schema_version=schema_version,
        contract_version=contract_version,
        contract_id=contract_id,
        locked_at=str(data["locked_at"]),
        locked_by=str(data["locked_by"]),
        target=frozen["target"],
        sections=MappingProxyType({name: frozen[name] for name in REQUIRED_SECTIONS}),
        verdicts=tuple(verdicts),
        amendment_policy=amendment_policy,
        digest=declared,
        raw=frozen,
        source=source,
    )


def require_contract_target(
    contract: AlphaContract,
    manifest: TargetManifest,
    snapshot: ArchitectureSnapshot,
) -> None:
    """Bind the sealed contract to the exact manifest and architecture snapshot.

    Repository revisions alone do not bind shard LFS objects or derived architecture
    semantics. Call this before planning, admission, download, or execution.
    """

    expected = contract.target
    actual = {
        "source_repo_id": manifest.source_repo_id,
        "source_revision": manifest.source_revision,
        "gguf_repo_id": manifest.gguf_repo_id,
        "gguf_revision": manifest.gguf_revision,
        "quantization": manifest.quantization,
        "shard_count": len(manifest.shards),
        "total_bytes": manifest.total_bytes,
        "target_manifest_sha256": manifest.digest,
        "architecture_snapshot_sha256": snapshot.digest,
    }
    if snapshot.source_repo_id != manifest.source_repo_id:
        raise AlphaContractError(
            "architecture snapshot repository does not match the target manifest repository"
        )
    if snapshot.source_revision != manifest.source_revision:
        raise AlphaContractError(
            "architecture snapshot revision does not match the target manifest revision"
        )
    mismatches = {
        key: (expected.get(key), value)
        for key, value in actual.items()
        if expected.get(key) != value
    }
    if mismatches:
        details = ", ".join(
            f"{key}: locked={locked!r}, actual={value!r}"
            for key, (locked, value) in sorted(mismatches.items())
        )
        raise AlphaContractError(f"target documents do not match the sealed contract: {details}")


def load_alpha_contract(path: Path | None = None) -> AlphaContract:
    """Load the packaged alpha contract, or one at ``path``."""
    if path is not None:
        source = str(path)
        try:
            raw = path.read_text(encoding="utf-8")
        except OSError as exc:
            raise AlphaContractError(f"cannot read {source}: {exc.strerror or exc}") from exc
    else:
        source = f"packaged {_CONTRACT_RESOURCE}"
        try:
            raw = (
                files("meshnet_node.glm_alpha")
                .joinpath("data", _CONTRACT_RESOURCE)
                .read_text(encoding="utf-8")
            )
        except (OSError, FileNotFoundError, ModuleNotFoundError) as exc:
            raise AlphaContractError(
                f"{source} is missing from this node installation ({type(exc).__name__})"
            ) from exc

    try:
        data = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise AlphaContractError(
            f"{source} is not valid JSON: {exc.msg} at line {exc.lineno} column {exc.colno}"
        ) from exc

    return parse_alpha_contract(data, source=source)


def seal_contract(document: Mapping[str, Any]) -> dict:
    """Return the document with a freshly computed digest.

    This is the only supported way to produce a contract file. It is deliberately
    *not* called at load time: sealing on load would turn every mutation into a
    valid contract, which is precisely the property the digest exists to deny.
    """
    sealed = dict(document)
    sealed[DIGEST_FIELD] = compute_contract_digest(document)
    return sealed
