"""Same-thread acknowledged handoff for committed canonical registrations."""
from __future__ import annotations

from threading import get_ident
from types import TracebackType
from typing import Final, Self, final

from checkin_cli.customer_coaching import CustomerRuntime
from checkin_cli.weekly_operations import (
    CustomerKey, WeeklyOperationsError, WeeklyOperationsInputError,
    customer_identity_digest,
)
from checkin_cli.weekly_operations_authority import WeeklyOperationsAuthorityRoot
from checkin_cli.weekly_operations_canonical_registry import one_time_registration
from checkin_cli.weekly_operations_customer_authority import CanonicalCheckinCustomerAuthority
from checkin_cli.weekly_operations_customer_authority_factory import acquire_unregistered_authority
from checkin_cli.weekly_operations_registered_binding import RegisteredCanonicalCheckinCustomerBinding
from checkin_cli.weekly_operations_signals import (
    InitializationCancellationKind,
    InitializationCommitContext, InitializationSignalGuard,
)


@final
class CanonicalAuthorityRegistrationCommitted(WeeklyOperationsError):
    """A durable row committed before its caller acknowledged the binding."""
    __slots__ = ("binding", "exit_context", "cancellation_kind", "original_context")

    def __init__(
        self, binding: RegisteredCanonicalCheckinCustomerBinding,
        context: InitializationCommitContext,
        cancellation: InitializationCancellationKind | None = None,
        original: BaseException | None = None,
    ) -> None:
        super().__init__(f"canonical registration committed during {context.value}")
        self.binding = binding
        self.exit_context = context
        self.cancellation_kind = cancellation
        self.original_context = original


@final
class CanonicalAuthorityRegistrationTransaction:
    __slots__: Final = (
        "_runtime", "_registry", "_guard", "_active", "_entered",
        "_acknowledged", "_source", "_binding", "_creator",
    )

    def __init__(self, runtime: CustomerRuntime, registry: WeeklyOperationsAuthorityRoot) -> None:
        self._creator = get_ident()
        self._runtime = runtime
        self._registry = registry
        self._guard = InitializationSignalGuard()
        self._active = False
        self._entered = False
        self._acknowledged = False
        self._source: CanonicalCheckinCustomerAuthority | None = None
        self._binding: RegisteredCanonicalCheckinCustomerBinding | None = None

    def _require_active(self) -> None:
        if get_ident() != self._creator:
            raise WeeklyOperationsInputError("canonical registration transaction thread mismatch")
        if not self._active:
            raise WeeklyOperationsInputError("canonical registration transaction inactive")

    def __enter__(self) -> Self:
        if get_ident() != self._creator:
            raise WeeklyOperationsInputError("canonical registration transaction thread mismatch")
        if self._entered:
            raise WeeklyOperationsInputError("canonical registration transaction reused")
        self._entered = True
        self._guard.block()
        self._active = True
        customer = customer_identity_digest(CustomerKey(self._runtime.spec.customer_key))
        try:
            with one_time_registration(self._registry, customer) as registration:
                self._source = acquire_unregistered_authority(self._runtime, self._registry)
                self._binding = registration.append(self._source.binding)
                self._source.attach_registered_binding(self._binding)
            return self
        except (Exception, KeyboardInterrupt, SystemExit) as error:
            self._active = False
            if self._source is not None:
                self._source.close()
            if self._binding is not None:
                cancellation = self._guard.restore()
                committed = CanonicalAuthorityRegistrationCommitted(
                    self._binding, InitializationCommitContext.BODY_EXCEPTION,
                    None if cancellation is None else cancellation.cancellation_kind, error,
                )
                raise committed from error
            _ = self._guard.restore(error)
            raise AssertionError("signal restore returned")

    @property
    def binding(self) -> RegisteredCanonicalCheckinCustomerBinding:
        self._require_active()
        if self._binding is None:
            raise WeeklyOperationsInputError("registration binding unavailable")
        return self._binding

    @property
    def authority(self) -> CanonicalCheckinCustomerAuthority:
        self._require_active()
        if self._source is None:
            raise WeeklyOperationsInputError("registered canonical authority unavailable")
        return self._source

    def acknowledge_binding(self) -> None:
        self._require_active()
        _ = self.binding
        self._acknowledged = True

    def __exit__(
        self, _kind: type[BaseException] | None, error: BaseException | None,
        _traceback: TracebackType | None,
    ) -> bool:
        self._require_active()
        binding, source = self.binding, self.authority
        self._active = False
        if self._acknowledged:
            cancellation = self._guard.restore(error)
            if cancellation is not None:
                raise cancellation
            return False
        source.close()
        cancellation = self._guard.restore()
        context = (
            InitializationCommitContext.BODY_EXCEPTION if error is not None
            else InitializationCommitContext.CANCELLATION if cancellation is not None
            else InitializationCommitContext.MISSING_ACKNOWLEDGEMENT
        )
        committed = CanonicalAuthorityRegistrationCommitted(
            binding, context,
            None if cancellation is None else cancellation.cancellation_kind, error,
        )
        if error is not None:
            raise committed from error
        if cancellation is not None:
            raise committed from cancellation
        raise committed


def begin_canonical_authority_registration(
    runtime: CustomerRuntime, registry: WeeklyOperationsAuthorityRoot,
) -> CanonicalAuthorityRegistrationTransaction:
    return CanonicalAuthorityRegistrationTransaction(runtime, registry)
