"""Descriptor-bound registered-customer authority for canonical check-ins."""

from __future__ import annotations

import fcntl
import os
import stat
from collections.abc import Generator
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import final

from . import weekly_operations_canonical_snapshot as canonical_snapshot
from .store import CanonicalEventSnapshot, CanonicalEventTransaction
from .weekly_operations import (
    CustomerIdentityDigest,
    WeeklyOperationsConflict,
)
from .weekly_operations_authority import WeeklyOperationsAuthorityRoot
from .weekly_operations_canonical_registry import require_registered_binding
from .weekly_operations_registered_binding import RegisteredCanonicalCheckinCustomerBinding
from .weekly_operations_customer_authority_identity import (
    CANONICAL_AUTHORITY_SEAL,
    CanonicalAuthoritySeal,
    CanonicalCustomerNames,
)
from .weekly_operations_canonical_snapshot import (
    CanonicalCheckinCustomerBinding,
    CanonicalDescriptorSet,
    CanonicalFilePin,
    read_descriptor,
)

@final
@dataclass(frozen=True, slots=True)
class CanonicalCheckinCustomerAuthority:
    """Pinned registered root, child names, descriptors, and sealed binding."""

    customer_identity_digest: CustomerIdentityDigest
    transaction: CanonicalEventTransaction
    binding: CanonicalCheckinCustomerBinding
    _names: CanonicalCustomerNames = field(repr=False)
    _files: CanonicalDescriptorSet = field(repr=False)
    _registry_authority: WeeklyOperationsAuthorityRoot = field(repr=False)
    _registered: RegisteredCanonicalCheckinCustomerBinding | None = field(repr=False)
    _token: CanonicalAuthoritySeal = field(repr=False, compare=False)

    def __post_init__(self) -> None:
        if self._token is not CANONICAL_AUTHORITY_SEAL or self.customer_identity_digest != self.binding.customer_identity_digest:
            raise WeeklyOperationsConflict("canonical customer authority binding is invalid")

    def duplicate_registered_root_descriptor(self) -> int:
        """Duplicate the verified customer root only for bound child capabilities."""
        self.verify()
        try:
            return os.dup(self._files.root)
        except OSError as error:
            raise WeeklyOperationsConflict(
                "registered canonical root descriptor unavailable"
            ) from error

    @property
    def registered_root_descriptor(self) -> int:
        """Expose the verified retained root to an allocation-safe wrapper."""
        self.verify()
        return self._files.root

    @property
    def binding_digest(self) -> str:
        return self.binding.binding_digest

    def _verify_attachment(
        self, parent: int, name: str, pin: CanonicalFilePin
    ) -> None:
        info = os.stat(name, dir_fd=parent, follow_symlinks=False)
        if stat.S_ISLNK(info.st_mode):
            raise WeeklyOperationsConflict("canonical child symlink substitution")
        pin.verify(info, read_descriptor(self._files.lock) if pin is self.binding.pins.lock else None)

    @property
    def registered_binding(self) -> RegisteredCanonicalCheckinCustomerBinding:
        if self._registered is None:
            raise WeeklyOperationsConflict("canonical registration binding unavailable")
        return self._registered

    def attach_registered_binding(
        self, binding: RegisteredCanonicalCheckinCustomerBinding
    ) -> None:
        if self._registered is not None or binding.canonical_binding != self.binding:
            raise WeeklyOperationsConflict("canonical registration handoff disagrees")
        object.__setattr__(self, "_registered", binding)

    @property
    def registry_authority_binding_digest(self) -> str:
        return self._registry_authority.binding.binding_digest

    def verify_canonical(self) -> None:
        """Revalidate sealed descriptors, names, transaction paths, and lock bytes."""
        expected = (
            self._names.root / self._names.wizard / self._names.events,
            self._names.root / self._names.plans / self._names.sequence,
            self._names.root / self._names.wizard / self._names.lock,
        )
        observed = (
            self.transaction.events_path,
            self.transaction.sequence_path,
            self.transaction.lock_path,
        )
        if observed != expected:
            raise WeeklyOperationsConflict("canonical customer transaction path drift")
        pins = self.binding.pins
        try:
            pins.root.verify(os.fstat(self._files.root))
            root_named = self._names.root.lstat()
            if stat.S_ISLNK(root_named.st_mode):
                raise WeeklyOperationsConflict("canonical customer root symlink substitution")
            pins.root.verify(root_named)
            attachments = (
                (self._files.root, self._names.wizard, pins.wizard),
                (self._files.root, self._names.plans, pins.plans),
                (self._files.wizard, self._names.events, pins.events),
                (self._files.plans, self._names.sequence, pins.sequence),
                (self._files.wizard, self._names.lock, pins.lock),
            )
            for descriptor, pin in zip(self._files.values(), pins.values(), strict=True):
                content = read_descriptor(descriptor) if pin is pins.lock else None
                pin.verify(os.fstat(descriptor), content)
            for parent, name, pin in attachments:
                self._verify_attachment(parent, name, pin)
        except OSError as error:
            raise WeeklyOperationsConflict("canonical customer authority unavailable") from error

    def verify(self) -> None:
        self.verify_canonical()
        with require_registered_binding(
            self._registry_authority, self.customer_identity_digest, self.registered_binding
        ):
            self.verify_canonical()

    @contextmanager
    def read_locked(self) -> Generator[CanonicalEventSnapshot]:
        """Hold the pinned lock and yield a descriptor-only immutable snapshot."""
        with require_registered_binding(
            self._registry_authority, self.customer_identity_digest, self.registered_binding
        ):
            self.verify_canonical()
            fcntl.flock(self._files.lock, fcntl.LOCK_SH)
            try:
                self.verify_canonical()
                snapshot = canonical_snapshot.read_canonical_snapshot(self._files)
                self.verify_canonical()
                yield snapshot
                self.verify_canonical()
            finally:
                fcntl.flock(self._files.lock, fcntl.LOCK_UN)

    def close(self) -> None:
        for descriptor in self._files.values():
            os.close(descriptor)
