"""Concrete target-bound NutriCoach v1.5 successor controller."""

from __future__ import annotations

from datetime import UTC, datetime
from pathlib import Path
from types import TracebackType
from typing import Literal, Self, final

from pydantic import JsonValue, TypeAdapter

from scripts.nutricoach_v150_concrete_host import (
    ConcreteLiveHost,
)
from scripts.nutricoach_v150_live_models import (
    WEEKLY_AUTHORITY_EXPIRES_AT,
    WEEKLY_AUTHORITY_ISSUED_AT,
)
from scripts.nutricoach_v150_sealed_authority import (
    AuthorityError,
    GlobalLedger,
    Snapshot,
    capture,
    load_snapshot,
    restore,
)
from scripts.nutricoach_v150_phase_journal import PhaseJournal
from scripts.nutricoach_v150_sealed_target import DisposableService
from scripts.verify_nutricoach_v150_preseal_v15 import verify as verify_preseal

PACKAGE_DIGEST = "DISPOSABLE_V3"
APPROVAL_PHRASE = f"AUTHORIZE NUTRICOACH V1.5 LIVE UPGRADE {PACKAGE_DIGEST}"
V2_APPROVAL_PHRASE = (
    "AUTHORIZE NUTRICOACH V1.5 LIVE UPGRADE "
    "869c1e8f080ce79600ed7af58e0bd33db54cf316fa9aff4efe924fee15409fa5"
)
OLD_APPROVAL_PHRASE = (
    "AUTHORIZE NUTRICOACH V1.5 LIVE UPGRADE "
    "a362d994b41a5d05ed2fcdafc76482e72d18ff69334cd8249eec061bff733acb"
)
SEALED_TARGET = Path(
    "/home/cube/.hermes/migrations/nutricoach-v1.5.0-combined/"
    + "live-transaction-preseal-v15-runtime-authority-r71/sealed-target.json"
)
_OBJECT = TypeAdapter(dict[str, JsonValue])


class SealedControllerError(RuntimeError):
    """Fail-closed sealed controller error."""


@final
class _Capture:
    """Record one rollback failure while continuing later recovery."""

    def __init__(self, failures: list[str], stage: str) -> None:
        self.failures = failures
        self.stage = stage

    def __enter__(self) -> Self:
        return self

    def __exit__(
        self,
        error_type: type[BaseException] | None,
        error: BaseException | None,
        traceback: TracebackType | None,
    ) -> Literal[True]:
        del error_type, traceback
        if error is not None:
            self.failures.append(f"{self.stage}:{error}")
        return True


@final
class RollbackGuard:
    """Exact v7-derived stop-through-fence rollback boundary."""

    def __init__(self, host: ConcreteLiveHost, journal: PhaseJournal) -> None:
        self.host = host
        self.journal = journal
        self.snapshot: Snapshot | None = None
        self.committed = False

    def __enter__(self) -> Self:
        return self

    def bind(self, snapshot: Snapshot) -> None:
        self.snapshot = snapshot

    def commit(self) -> None:
        self.committed = True

    def rollback(self, original: BaseException) -> None:
        failures = self.host.rollback_failures
        if self.host.service.running:
            with _Capture(failures, "stop"):
                self.host.service.stop()
        if self.snapshot is not None:
            with _Capture(failures, "authority_restore"):
                self.host.restore_runtime_authority()
            with _Capture(failures, "snapshot_restore"):
                restore(self.snapshot)
            with _Capture(failures, "created_remove"):
                self.host.remove_created()
            restore_failed = any(
                failure.startswith(("authority_restore:", "snapshot_restore:"))
                for failure in failures
            )
            if not restore_failed:
                with _Capture(failures, "systemd_reload"):
                    self.host.service.reload()
        else:
            restore_failed = False
        if not restore_failed:
            if not self.host.service.running:
                with _Capture(failures, "service_restore"):
                    self.host.service.start()
            if not self.host.service.running:
                with _Capture(failures, "service_restore_retry"):
                    self.host.service.start()
            if not self.host.service.running:
                failures.append("service_restore:inactive")
            with _Capture(failures, "protected_restore"):
                self.host.verify_preflight()
        protected_failed = any(
            failure.startswith("protected_restore:") for failure in failures
        )
        self.journal.advance(
            "RECOVERY_REQUIRED"
            if restore_failed or protected_failed or not self.host.service.running
            else "ROLLED_BACK"
        )
        if failures:
            original.add_note("rollback_failures=" + ",".join(failures))

    def __exit__(
        self,
        error_type: type[BaseException] | None,
        error: BaseException | None,
        traceback: TracebackType | None,
    ) -> Literal[False]:
        del error_type, traceback
        if error is not None:
            self.rollback(error)
        elif not self.committed:
            missing = SealedControllerError("uncommitted")
            self.rollback(missing)
            raise missing
        return False


def _execute(
    approval: str,
    host: ConcreteLiveHost,
    expected_approval: str = APPROVAL_PHRASE,
) -> str:
    if approval in {OLD_APPROVAL_PHRASE, V2_APPROVAL_PHRASE}:
        raise SealedControllerError("superseded_approval")
    if approval != expected_approval:
        raise SealedControllerError("wrong_approval")
    now = datetime.now(UTC)
    if not (
        datetime.fromisoformat(WEEKLY_AUTHORITY_ISSUED_AT)
        <= now
        < datetime.fromisoformat(WEEKLY_AUTHORITY_EXPIRES_AT)
    ):
        raise SealedControllerError("weekly_authority_window")
    host.capture_preflight()
    host.verify_preflight()
    host.semantic_clean_boundary()
    ledger = GlobalLedger(host.paths.ledger_root, host.candidate_digest)
    try:
        ledger.reserve()
    except AuthorityError as exc:
        raise SealedControllerError("authorization_already_used") from exc
    with ledger.attempt():
        host.write_recovery_manifest()
        journal = PhaseJournal(host.paths.execution_root / "phase.json")
        journal.advance("PREPARING")
        journal.advance("RESERVED")
        with RollbackGuard(host, journal) as guard:
            journal.advance("STOPPING")
            host.stop()
            snapshot = capture(host.snapshot_paths(), host.paths.execution_root)
            guard.bind(snapshot)
            journal.advance("SNAPSHOT")
            host.record_snapshot()
            host.stopped_probe()
            host.semantic_clean_boundary()
            host.install()
            journal.advance("INSTALLED")
            host.off_smoke()
            host.migration_dry_run()
            host.migration_apply()
            journal.advance("MIGRATED")
            host.weekly_startup_smoke()
            host.promote_runtime_authority()
            host.switch_systemd()
            host.reload()
            host.start()
            journal.advance("STARTED")
            host.post_fence()
            if host.network_events or host.telegram_events or host.provider_events:
                raise SealedControllerError("privacy_postcondition")
            journal.advance("COMMITTING")
            ledger.consume("SUCCEEDED")
            guard.commit()
            journal.advance("COMMITTED")
    return snapshot.manifest_digest


def recover_pending(host: ConcreteLiveHost) -> None:
    journal = PhaseJournal(host.paths.execution_root / "phase.json")
    ledger = GlobalLedger(host.paths.ledger_root, host.candidate_digest)
    if journal.phase() is None:
        if not ledger.reservation_pending():
            return
        ledger.consume("FAILED")
        journal.advance("ROLLED_BACK")
        interrupted = SealedControllerError("interrupted_transaction")
        raise SealedControllerError("rollback_recovery_completed") from interrupted
    if not journal.recovery_required():
        return
    if journal.phase() == "COMMITTING" and ledger.outcome() == "SUCCEEDED":
        journal.advance("COMMITTED")
        return
    guard = RollbackGuard(host, journal)
    host.load_recovery_manifest()
    if journal.phase() not in {"PREPARING", "STOPPING", "RESERVED"}:
        guard.bind(load_snapshot(host.paths.execution_root))
    interrupted = SealedControllerError("interrupted_transaction")
    guard.rollback(interrupted)
    ledger.consume("FAILED")
    raise SealedControllerError("rollback_recovery_completed") from interrupted


def rollback_committed(host: ConcreteLiveHost, reason: str) -> None:
    """Run the sealed postcommit rollback after a failed Manual-QA fence."""
    if not reason.strip():
        raise SealedControllerError("postcommit_rollback_reason")
    journal = PhaseJournal(host.paths.execution_root / "phase.json")
    ledger = GlobalLedger(host.paths.ledger_root, host.candidate_digest)
    if journal.phase() != "COMMITTED" or ledger.outcome() != "SUCCEEDED":
        raise SealedControllerError("postcommit_rollback_state")
    host.load_recovery_manifest()
    guard = RollbackGuard(host, journal)
    guard.bind(load_snapshot(host.paths.execution_root))
    failure = SealedControllerError(f"postcommit_manual_qa:{reason.strip()}")
    guard.rollback(failure)
    if journal.phase() != "ROLLED_BACK":
        raise SealedControllerError("postcommit_rollback_incomplete") from failure


def execute_authorized(approval: str) -> str:
    """Execute only the compile-time V15 runtime-authority target."""
    if approval in {OLD_APPROVAL_PHRASE, V2_APPROVAL_PHRASE}:
        raise SealedControllerError("superseded_approval")
    binding = _OBJECT.validate_json(SEALED_TARGET.read_bytes())
    expected = binding.get("approval_phrase")
    package_path = binding.get("permission_package")
    derivation = binding.get("controller_derivation_sha256")
    if (
        not isinstance(expected, str)
        or not isinstance(package_path, str)
        or not isinstance(derivation, str)
    ):
        raise SealedControllerError("sealed_target")
    if approval != expected:
        raise SealedControllerError("wrong_approval")
    host = ConcreteLiveHost.live_target(SEALED_TARGET)
    recover_pending(host)
    verified = verify_preseal()
    if verified["approval_phrase"] != approval or verified[
        "package_digest"
    ] != binding.get("package_digest"):
        raise SealedControllerError("preseal_binding")
    return _execute(approval, host, expected)


def execute_disposable(
    approval: str,
    root: Path,
    host: ConcreteLiveHost,
    *,
    expected_approval: str = APPROVAL_PHRASE,
) -> str:
    """Exercise identical semantics against an isolated target."""
    if host.paths.profile != root / "profile":
        raise SealedControllerError("disposable_binding")
    return _execute(approval, host, expected_approval)


__all__ = [
    "APPROVAL_PHRASE",
    "OLD_APPROVAL_PHRASE",
    "V2_APPROVAL_PHRASE",
    "ConcreteLiveHost",
    "DisposableService",
    "SealedControllerError",
    "execute_authorized",
    "execute_disposable",
    "rollback_committed",
]
