"""External, lock-linearized runtime authority for Task26 production operations."""
from __future__ import annotations

import fcntl
import hashlib
import json
import os
import stat
from contextlib import contextmanager
from pathlib import Path
from typing import Iterator, Mapping, cast

from .task26_candidate_authority import (
    AuthorityAction,
    append_candidate_authority,
    verify_candidate_authority,
)

HEX = set("0123456789abcdef")


def canonical(value: object) -> bytes:
    return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False).encode()


def digest(value: object) -> str:
    return hashlib.sha256(canonical(value)).hexdigest()


def _hex(value: object) -> bool:
    return isinstance(value, str) and len(value) == 64 and set(value) <= HEX


def _private(path: Path, *, directory: bool = False) -> None:
    info = path.lstat()
    expected = stat.S_ISDIR if directory else stat.S_ISREG
    modes = {0o700, 0o500} if directory else {0o600, 0o400}
    if stat.S_ISLNK(info.st_mode) or not expected(info.st_mode) or info.st_uid != os.geteuid() or (not directory and info.st_nlink != 1) or stat.S_IMODE(info.st_mode) not in modes:
        raise ValueError("runtime authority path is not private")


def _read(path: Path) -> dict[str, object]:
    _private(path)
    value = json.loads(path.read_text(encoding="utf-8"))
    if not isinstance(value, dict):
        raise ValueError("runtime authority document is invalid")
    return cast(dict[str, object], value)


def _snapshot(value: Mapping[str, object]) -> dict[str, object]:
    snapshot = {
        "schema": "task26-runtime-authority-snapshot-v1",
        "source_id": value["source_id"],
        "genesis_sha256": value["genesis_sha256"],
        "candidate_digest": value["current_qualified_candidate"],
        "registry_head_sha256": value["registry_head_sha256"],
        "ledger_head_sha256": value["ledger_head_sha256"],
        "event_count": value["event_count"],
    }
    snapshot["snapshot_sha256"] = digest(snapshot)
    return snapshot


def validate_snapshot(value: object) -> dict[str, object]:
    if not isinstance(value, dict) or set(value) != {"schema", "source_id", "genesis_sha256", "candidate_digest", "registry_head_sha256", "ledger_head_sha256", "event_count", "snapshot_sha256"}:
        raise ValueError("runtime authority snapshot schema is invalid")
    snapshot = cast(dict[str, object], value)
    unsigned = {key: item for key, item in snapshot.items() if key != "snapshot_sha256"}
    if snapshot.get("schema") != "task26-runtime-authority-snapshot-v1" or any(not _hex(snapshot.get(field)) for field in ("genesis_sha256", "candidate_digest", "registry_head_sha256", "ledger_head_sha256")) or not isinstance(snapshot.get("source_id"), str) or type(snapshot.get("event_count")) is not int or cast(int, snapshot["event_count"]) < 1 or snapshot.get("snapshot_sha256") != digest(unsigned):
        raise ValueError("runtime authority snapshot binding is invalid")
    return snapshot


class FileCandidateAuthoritySource:
    def __init__(self, pin_path: Path, *, forbidden_roots: tuple[Path, ...]) -> None:
        pin = _read(pin_path.absolute())
        if set(pin) != {"schema", "authority_root", "source_id", "genesis_sha256", "registry_head_sha256", "ledger_head_sha256", "event_count", "pin_sha256"}:
            raise ValueError("runtime authority pin schema is invalid")
        unsigned = {key: item for key, item in pin.items() if key != "pin_sha256"}
        if pin.get("schema") != "task26-authority-pin-v1" or pin.get("pin_sha256") != digest(unsigned) or not isinstance(pin.get("source_id"), str) or any(not _hex(pin.get(field)) for field in ("genesis_sha256", "registry_head_sha256", "ledger_head_sha256")) or type(pin.get("event_count")) is not int or cast(int, pin["event_count"]) < 2:
            raise ValueError("runtime authority pin binding is invalid")
        if not forbidden_roots:
            raise ValueError("runtime authority forbidden roots are required")
        root = Path(str(pin["authority_root"]))
        if not root.is_absolute():
            raise ValueError("runtime authority root must be absolute")
        resolved = root.resolve(strict=True)
        _private(resolved, directory=True)
        for forbidden in forbidden_roots:
            candidate = forbidden.resolve(strict=False)
            if resolved == candidate or resolved.is_relative_to(candidate):
                raise ValueError("runtime authority root is under a forbidden root")
        self.root = resolved
        self.pin = pin
        self.lock_path = resolved / "runtime-authority.lock"
        if not self.lock_path.exists():
            fd = os.open(self.lock_path, os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_NOFOLLOW, 0o600)
            os.close(fd)
        self.lock_path.chmod(0o600)
        self._read_current(require_pin=True)

    def _read_current(self, *, require_pin: bool = False) -> dict[str, object]:
        result = verify_candidate_authority(self.root, None)
        registry = _read(self.root / "candidate-authority/registry.json")
        ledger = _read(self.root / "candidate-authority/qualification-ledger.json")
        events = registry.get("events")
        if not isinstance(events, list) or not events:
            raise ValueError("runtime authority event chain is unavailable")
        source_id = registry.get("source_id")
        genesis = registry.get("genesis_sha256")
        if ledger.get("source_id") != source_id or ledger.get("genesis_sha256") != genesis:
            raise ValueError("paired runtime authority source differs")
        current = {
            **result,
            "source_id": source_id,
            "genesis_sha256": genesis,
            "event_count": len(events),
            "events": events,
            "rows": ledger.get("rows"),
        }
        if source_id != self.pin["source_id"] or genesis != self.pin["genesis_sha256"]:
            raise ValueError("runtime authority source or genesis differs")
        if require_pin and (len(events) != self.pin["event_count"] or result["registry_head_sha256"] != self.pin["registry_head_sha256"] or result["ledger_head_sha256"] != self.pin["ledger_head_sha256"]):
            raise ValueError("runtime authority pin is stale")
        return current

    @contextmanager
    def authorize(self, candidate: str, stage: str, predecessor: object | None = None) -> Iterator[dict[str, object]]:
        if not _hex(candidate) or stage not in {"activation", "capability_issue", "transport"}:
            raise ValueError("runtime authority request is invalid")
        previous = validate_snapshot(predecessor) if predecessor is not None else None
        with self.lock_path.open("r+") as lock:
            fcntl.flock(lock.fileno(), fcntl.LOCK_SH)
            try:
                current = self._read_current(require_pin=previous is None)
                if current.get("current_qualified_candidate") != candidate or candidate in cast(list[str], current.get("invalidated_candidate_digests", [])):
                    raise ValueError("runtime candidate is not current or was revoked")
                if previous is not None:
                    if previous["source_id"] != current["source_id"] or previous["genesis_sha256"] != current["genesis_sha256"] or previous["candidate_digest"] != candidate or cast(int, previous["event_count"]) > cast(int, current["event_count"]):
                        raise ValueError("runtime authority predecessor is stale")
                    index = cast(int, previous["event_count"]) - 1
                    events = cast(list[dict[str, object]], current["events"])
                    rows = cast(list[dict[str, object]], current["rows"])
                    if events[index].get("event_sha256") != previous["registry_head_sha256"] or rows[index].get("row_sha256") != previous["ledger_head_sha256"]:
                        raise ValueError("runtime authority rollback or successor mismatch")
                yield _snapshot(current)
            finally:
                fcntl.flock(lock.fileno(), fcntl.LOCK_UN)


CandidateAuthoritySource = FileCandidateAuthoritySource


def _credential_path(name: str, environment_name: str) -> Path:
    explicit = os.environ.get(environment_name)
    if explicit:
        path = Path(explicit)
    else:
        credential_directory = os.environ.get("CREDENTIALS_DIRECTORY")
        if not credential_directory:
            raise ValueError(f"{name} credential is unavailable")
        path = Path(credential_directory) / name
    if not path.is_absolute():
        raise ValueError(f"{name} credential path must be absolute")
    return path


def load_task26_production_authority(
    *,
    profile_root: Path,
    package_root: Path,
    additional_forbidden_roots: tuple[Path, ...] = (),
) -> tuple[FileCandidateAuthoritySource, str]:
    """Resolve production authority only from env/systemd credential inputs."""
    pin_path = _credential_path("task26-authority-pin.json", "TASK26_AUTHORITY_PIN")
    candidate_path = _credential_path(
        "task26-candidate-digest", "TASK26_CANDIDATE_DIGEST_FILE"
    )
    _private(candidate_path)
    candidate = candidate_path.read_text(encoding="utf-8").strip()
    if not _hex(candidate):
        raise ValueError("Task26 candidate digest credential is invalid")
    configured_roots: list[Path] = [profile_root, package_root]
    for name in (
        "TASK26_CANDIDATE_ROOT",
        "TASK26_WHEELHOUSE_ROOT",
        "TASK26_RECEIPT_ROOT",
    ):
        value = os.environ.get(name)
        if value:
            path = Path(value)
            if not path.is_absolute():
                raise ValueError(f"{name} must be absolute")
            configured_roots.append(path)
    configured_roots.extend(additional_forbidden_roots)
    source = FileCandidateAuthoritySource(
        pin_path,
        forbidden_roots=tuple(configured_roots),
    )
    with source.authorize(candidate, "activation"):
        pass
    return source, candidate


def append_external_authority(root: Path, *, source_id: str, candidate_digest: str, action: str, historical_pass_digest: str, reason: str) -> dict[str, object]:
    root.mkdir(parents=True, exist_ok=True, mode=0o700)
    root.chmod(0o700)
    lock_path = root / "runtime-authority.lock"
    with lock_path.open("a+") as lock:
        lock_path.chmod(0o600)
        fcntl.flock(lock.fileno(), fcntl.LOCK_EX)
        try:
            if action not in {"qualify", "revoke"}:
                raise ValueError("runtime authority action is invalid")
            typed_action: AuthorityAction = (
                "qualify" if action == "qualify" else "revoke"
            )
            result = append_candidate_authority(
                root,
                candidate_digest=candidate_digest,
                action=typed_action,
                historical_pass_digest=historical_pass_digest,
                reason=reason,
            )
            registry_path = root / "candidate-authority/registry.json"
            ledger_path = root / "candidate-authority/qualification-ledger.json"
            registry = json.loads(registry_path.read_text())
            ledger = json.loads(ledger_path.read_text())
            genesis = registry.get("genesis_sha256") or registry["events"][0]["event_sha256"]
            for document, path in ((registry, registry_path), (ledger, ledger_path)):
                document["source_id"] = source_id
                document["genesis_sha256"] = genesis
                document["document_sha256"] = digest({key: value for key, value in document.items() if key != "document_sha256"})
                path.write_bytes(canonical(document) + b"\n")
                path.chmod(0o600)
            return {**result, "source_id": source_id, "genesis_sha256": genesis}
        finally:
            fcntl.flock(lock.fileno(), fcntl.LOCK_UN)
