from __future__ import annotations

import os
import signal
from collections.abc import Callable
from typing import NoReturn
from pathlib import Path
from unittest import TestCase
from unittest.mock import patch

import checkin_cli.weekly_operations_authority as authority_module
from checkin_cli.weekly_operations import (
    WeeklyOperationsAuthorityCompromise, WeeklyOperationsCorruption,
)
from checkin_cli.weekly_operations_authority import (
    AuthorityId, begin_authority_initialization, open_authority_root,
)
from checkin_cli.weekly_operations_canonical_registry_history import REGISTRY_NAME
from checkin_cli.weekly_operations_parent import (
    AuthorityMarker, WeeklyOperationsAuthorityBinding,
    WeeklyOperationsParentAuthority, acquire_parent_authority,
)
from checkin_cli.weekly_operations_registry_bootstrap import (
    PreparedRegistry, commit_registry,
)
from checkin_cli.weekly_operations_signals import InitializationCancellation

ASSERTIONS = TestCase()


def _root(tmp_path: Path, name: str) -> Path:
    root = tmp_path / name
    root.mkdir(mode=0o700)
    return root


def test_marker_and_binding_seal_bootstrap_registry_identity(tmp_path: Path) -> None:
    root = _root(tmp_path, "sealed")
    parent = acquire_parent_authority(root)
    binding: WeeklyOperationsAuthorityBinding | None = None
    with begin_authority_initialization(parent, AuthorityId("d" * 64)) as transaction:
        binding = transaction.binding
        transaction.acknowledge_binding()
    registry = (root / REGISTRY_NAME).stat()
    marker = AuthorityMarker.model_validate_json(
        (root / "authority-v1.json").read_bytes()
    )
    assert binding is not None
    expected = registry.st_dev, registry.st_ino, 0o600, registry.st_uid, 1
    assert expected == (
        binding.registry_device, binding.registry_inode, binding.registry_mode,
        binding.registry_owner, binding.registry_links,
    )
    assert expected == tuple(getattr(marker, f"registry_{field}") for field in (
        "device", "inode", "mode", "owner", "links",
    ))


def test_bootstrap_registry_name_substitution_is_preserved_twice(tmp_path: Path) -> None:
    for attempt in (1, 2):
        root = _root(tmp_path, f"substitution-{attempt}")
        parent = acquire_parent_authority(root)
        stolen = tmp_path / f"owned-{attempt}"

        def substitute(
            _parent: WeeklyOperationsParentAuthority, _payload: bytes,
            _canonical: Callable[[bytes], bool], *, allowed: frozenset[str],
        ) -> NoReturn:
            _ = allowed
            _ = (root / REGISTRY_NAME).rename(stolen)
            (root / REGISTRY_NAME).touch(mode=0o600)
            raise WeeklyOperationsCorruption("injected pre-marker failure")

        with patch.object(authority_module, "prepare_authority_marker", side_effect=substitute):
            with ASSERTIONS.assertRaises(WeeklyOperationsAuthorityCompromise):
                with begin_authority_initialization(parent, AuthorityId("e" * 64)):
                    raise AssertionError("enter unexpectedly succeeded")
        assert (root / REGISTRY_NAME).is_file() and stolen.is_file()
        assert not (root / "authority-v1.json").exists()


def test_signals_at_registry_link_are_deferred_to_exact_handoff_twice(tmp_path: Path) -> None:
    real_commit = commit_registry
    for signal_number in (signal.SIGTERM, signal.SIGINT):
        for attempt in (1, 2):
            root = _root(tmp_path, f"signal-{signal_number}-{attempt}")
            parent = acquire_parent_authority(root)
            sent = False

            def commit_then_signal(
                parent_authority: WeeklyOperationsParentAuthority,
                prepared: PreparedRegistry,
            ) -> os.stat_result:
                nonlocal sent
                result = real_commit(parent_authority, prepared)
                if not sent:
                    sent = True
                    os.kill(os.getpid(), signal_number)
                return result

            binding = None
            with patch(
                "checkin_cli.weekly_operations_authority.commit_registry",
                new=commit_then_signal,
            ):
                with ASSERTIONS.assertRaises(InitializationCancellation):
                    with begin_authority_initialization(parent, AuthorityId("f" * 64)) as transaction:
                        binding = transaction.binding
                        transaction.acknowledge_binding()
            assert binding is not None
            reopened = open_authority_root(parent, binding)
            reopened.close()
