"""Live entrypoint binding regression for the sealed controller."""

from __future__ import annotations

import json
import os
from pathlib import Path
from typing import cast
from unittest.mock import patch

import pytest

import scripts.nutricoach_v150_detached_bootstrap as detached_bootstrap
import scripts.nutricoach_v150_sealed_controller as sealed_controller
from scripts.execute_nutricoach_v150_sealed_live import sandbox_command
from scripts.nutricoach_v150_sealed_controller import (
    ConcreteLiveHost,
    DisposableService,
    execute_authorized,
)
from scripts.nutricoach_v150_sealed_target import (
    HostError,
    HostPaths,
)
from scripts.nutricoach_v150_runtime_ops import make_writable
from tests.test_nutricoach_v150_sealed_controller import target_fixture


def test_live_controller_bootstrap_and_launcher_bind_r71b_preseal() -> None:
    preseal = cast(Path, getattr(detached_bootstrap, "PRESEAL"))
    assert sealed_controller.SEALED_TARGET == preseal / "sealed-target.json"
    assert preseal.name == "live-transaction-preseal-v15-runtime-authority-r71b-maintenance"
    command = sandbox_command("approval")
    assert str(preseal / "controller-source/scripts/nutricoach_v150_detached_bootstrap.py") in command


def test_live_entrypoint_uses_bound_sealed_target_approval(tmp_path: Path) -> None:
    _ = target_fixture(tmp_path)
    service = DisposableService()
    host = ConcreteLiveHost.disposable(tmp_path, service)
    package = tmp_path / "package.json"
    _ = package.write_text("{}\n")
    phrase = "AUTHORIZE NUTRICOACH V1.5 LIVE UPGRADE V5_BOUND"
    sealed_target = tmp_path / "sealed-target.json"
    _ = sealed_target.write_text(
        json.dumps({
            "approval_phrase": phrase,
            "controller_derivation_sha256": "derivation",
            "package_digest": "V5_BOUND",
            "permission_package": str(package),
        })
        + "\n"
    )

    with (
        patch.object(sealed_controller, "SEALED_TARGET", sealed_target),
        patch.object(
            ConcreteLiveHost,
            "live_target",
            return_value=host,
        ) as live_target,
        patch.object(
            sealed_controller,
            "verify_preseal",
            return_value={"approval_phrase": phrase, "package_digest": "V5_BOUND"},
        ),
    ):
        receipt = execute_authorized(phrase)

    assert receipt.startswith("sha256:")
    assert service.running
    live_target.assert_called_once_with(sealed_target)


def test_hardlinked_stable_runtime_is_verified_but_not_snapshotted(
    tmp_path: Path,
) -> None:
    profile = tmp_path / "profile"
    profile.mkdir()
    stable = profile / "stable.py"
    _ = stable.write_text("stable\n")
    os.link(stable, profile / "stable-alias.py")
    mutable = tuple(
        tmp_path / name for name in ("config", "registry", "unit", "dropin")
    )
    for path in mutable:
        _ = path.write_text(f"{path.name}\n")
    inventory = tmp_path / "protected-inventory.json"
    _ = inventory.write_text(
        json.dumps({
            "profiles": {
                "stable": [{"path": "stable.py", "profile": profile.name}],
                "volatile": [],
            },
        })
        + "\n"
    )
    paths = HostPaths(
        profile=profile,
        registry=mutable[1],
        config=mutable[0],
        unit=mutable[2],
        dropin=mutable[3],
        current_runtime=profile / "current",
        successor_runtime=profile / "successor",
        execution_root=tmp_path / "execution",
        ledger_root=tmp_path / "ledger",
        protected_inventory=inventory,
    )
    host = ConcreteLiveHost(paths, DisposableService(), live=False)

    host.capture_preflight()

    assert host.snapshot_paths() == paths.mutable
    host.verify_preflight()
    _ = stable.write_text("drift\n")
    with pytest.raises(HostError, match="protected_drift"):
        host.verify_preflight()


def test_successor_cleanup_never_chmods_symlink_target(tmp_path: Path) -> None:
    root = tmp_path / "successor"
    binary = root / "bin"
    binary.mkdir(parents=True)
    outside = tmp_path / "outside-python"
    _ = outside.write_text("python\n")
    outside.chmod(0o500)
    (binary / "python").symlink_to(outside)

    make_writable(root)

    assert outside.stat().st_mode & 0o777 == 0o500
