"""Factories for sealed canonical customer authority receipts."""

from __future__ import annotations

import os
from contextlib import ExitStack
from dataclasses import dataclass

from .customer_coaching import CustomerRuntime
from .store import CanonicalEventTransaction
from .weekly_operations import (
    CustomerKey,
    WeeklyOperationsConflict,
    customer_identity_digest,
)
from .weekly_operations_authority import WeeklyOperationsAuthorityRoot
from .weekly_operations_canonical_snapshot import (
    CanonicalBindingMaterial,
    create_binding,
)
from .weekly_operations_customer_authority import CanonicalCheckinCustomerAuthority
from .weekly_operations_customer_authority_identity import (
    CANONICAL_AUTHORITY_SEAL,
    CANONICAL_CUSTOMER_SCHEMA_DIGEST,
    CanonicalCustomerNames,
)
from .weekly_operations_customer_authority_parser import (
    OpenedCanonicalCustomerFiles,
    open_canonical_customer_files,
)
from .weekly_operations_registered_binding import RegisteredCanonicalCheckinCustomerBinding


@dataclass(frozen=True, slots=True)
class CanonicalAuthorityConstruction:
    """Owned descriptor set plus an optional exact durable registration."""

    files: OpenedCanonicalCustomerFiles
    expected: RegisteredCanonicalCheckinCustomerBinding | None = None


def construct_unregistered_authority_from_owned_descriptors(
    runtime: CustomerRuntime,
    registry_authority: WeeklyOperationsAuthorityRoot,
    construction: CanonicalAuthorityConstruction,
) -> CanonicalCheckinCustomerAuthority:
    """Construct after an external registrar owns every descriptor."""
    registered = runtime.registered_binding
    customer = customer_identity_digest(CustomerKey(runtime.spec.customer_key))
    names = CanonicalCustomerNames(runtime.data_root.absolute())
    observed = create_binding(
        CanonicalBindingMaterial(
            customer,
            registered.binding_digest,
            CANONICAL_CUSTOMER_SCHEMA_DIGEST,
            construction.files.pins,
        )
    )
    expected = construction.expected
    if expected is not None and observed != expected.canonical_binding:
        raise WeeklyOperationsConflict(
            "canonical customer authority reacquisition drift"
        )
    transaction = CanonicalEventTransaction.for_customer_runtime(runtime)
    authority = CanonicalCheckinCustomerAuthority(
        customer,
        transaction,
        observed,
        names,
        construction.files.descriptors,
        registry_authority,
        expected,
        CANONICAL_AUTHORITY_SEAL,
    )
    authority.verify_canonical()
    return authority


def acquire_unregistered_authority(
    runtime: CustomerRuntime,
    registry_authority: WeeklyOperationsAuthorityRoot,
    expected: RegisteredCanonicalCheckinCustomerBinding | None = None,
) -> CanonicalCheckinCustomerAuthority:
    """Acquire one exact descriptor-bound authority without registering it."""
    names = CanonicalCustomerNames(runtime.data_root.absolute())
    opened = open_canonical_customer_files(names)
    try:
        return construct_unregistered_authority_from_owned_descriptors(
            runtime, registry_authority, CanonicalAuthorityConstruction(opened, expected)
        )
    except WeeklyOperationsConflict:
        for descriptor in opened.descriptors.values():
            os.close(descriptor)
        raise


def open_canonical_checkin_customer_authority(
    runtime: CustomerRuntime,
    expected_binding: RegisteredCanonicalCheckinCustomerBinding,
    registry_authority: WeeklyOperationsAuthorityRoot,
) -> CanonicalCheckinCustomerAuthority:
    """Open only an exact caller-held and durably registered binding."""
    with ExitStack() as cleanup:
        authority = acquire_unregistered_authority(
            runtime, registry_authority, expected_binding
        )
        _ = cleanup.callback(authority.close)
        authority.verify()
        _ = cleanup.pop_all()
        return authority
