"""Production owner of per-customer weekly reminder authority."""

from __future__ import annotations

import hashlib
from contextlib import ExitStack
from dataclasses import dataclass
from datetime import datetime
from typing import Protocol, final, runtime_checkable

from checkin_cli.customer_coaching import CustomerRegistry, CustomerRuntime
from checkin_cli.weekly_operations import CustomerKey, WeeklyOperationsConflict
from checkin_cli.weekly_reminder_authority import (
    BoundWeeklyReminderCustomer,
    WeeklyReminderAuthorizationFacts,
    WeeklyReminderBindingInput,
    bind_weekly_reminder_customer,
    seal_weekly_reminder_authorization,
)
from checkin_cli.weekly_operations_customer_authority import CanonicalCheckinCustomerAuthority
from checkin_cli.weekly_operations_store import WeeklyOperationsStore
from checkin_cli.weekly_reminder_ledger_authority import (
    WeeklyReminderLedgerAuthority,
    acquire_registered_weekly_reminder_ledger_authority,
)
from checkin_cli.weekly_reminder_route import bind_registered_weekly_reminder_route

from .nutrition_weekly_operations_authority import (
    WeeklyOperationsAuthorityReceipt,
    WeeklyOperationsRuntimeContext,
    weekly_operations_is_authorized,
)
from .nutrition_weekly_operations_config import (
    WeeklyOperationsConfig,
)


class WeeklyReminderOwnerError(WeeklyOperationsConflict):
    """Production reminder owner is absent, stale, or inconsistent."""


@runtime_checkable
class WeeklyReminderOwnerAuthority(Protocol):
    def tick_snapshot(
        self, customer_key: str, now: datetime
    ) -> WeeklyOperationsTickSnapshot: ...

    def bound_customer(
        self, customer_key: str, now: datetime
    ) -> BoundWeeklyReminderCustomer: ...


class WeeklyReminderContextSource(Protocol):
    def consent_digest(self, runtime: CustomerRuntime) -> str: ...

    def current_context(
        self, runtime: CustomerRuntime, now: datetime
    ) -> WeeklyOperationsRuntimeContext: ...


@final
class RegisteredWeeklyReminderCustomer:
    """One customer plus an idempotent, explicitly transferred closer."""

    __slots__ = ("runtime", "source", "store", "ledger", "_closer")

    def __init__(
        self,
        runtime: CustomerRuntime,
        source: CanonicalCheckinCustomerAuthority,
        store: WeeklyOperationsStore,
        ledger: WeeklyReminderLedgerAuthority,
        closer: ExitStack | None = None,
    ) -> None:
        self.runtime = runtime
        self.source = source
        self.store = store
        self.ledger = ledger
        if closer is None:
            closer = ExitStack()
            _ = closer.callback(ledger.close)
        self._closer = closer

    def close(self) -> None:
        self._closer.close()


def register_weekly_reminder_customer(
    runtime: CustomerRuntime,
    source: CanonicalCheckinCustomerAuthority,
    store: WeeklyOperationsStore,
) -> RegisteredWeeklyReminderCustomer:
    """Join Todo 3/4 capabilities to the runtime-derived ledger root."""
    if runtime.registered_binding.binding_digest != source.binding.registered_binding_digest:
        raise WeeklyReminderOwnerError("weekly reminder runtime is not canonical-registered")
    ledger = acquire_registered_weekly_reminder_ledger_authority(source, store)
    identity = ledger.customer_identity_digest
    if identity != source.customer_identity_digest or identity != store.customer_identity_digest:
        ledger.close()
        raise WeeklyReminderOwnerError("weekly reminder registered customers disagree")
    return RegisteredWeeklyReminderCustomer(runtime, source, store, ledger)


@dataclass(frozen=True, slots=True)
class WeeklyReminderOwnerInput:
    config: WeeklyOperationsConfig
    receipt: WeeklyOperationsAuthorityReceipt
    registry_digest: str
    customers: tuple[RegisteredWeeklyReminderCustomer, ...]
    context_source: WeeklyReminderContextSource


@dataclass(frozen=True, slots=True)
class WeeklyOperationsTickSnapshot:
    """Current gateway authority joined to exact canonical/sidecar capabilities."""

    config: WeeklyOperationsConfig
    receipt: WeeklyOperationsAuthorityReceipt
    runtime: WeeklyOperationsRuntimeContext
    customer: RegisteredWeeklyReminderCustomer


@final
class WeeklyReminderAuthorityOwner:
    """Retain and refresh exact per-customer reminder capabilities."""

    def __init__(self, data: WeeklyReminderOwnerInput) -> None:
        self._data = data
        self._customers = {
            customer.runtime.spec.customer_key: customer for customer in data.customers
        }
        if len(self._customers) != len(data.customers):
            raise WeeklyReminderOwnerError("weekly reminder owner has duplicate customers")
        self._current_runtimes = {
            customer.runtime.spec.customer_key: customer.runtime
            for customer in data.customers
        }

    def verify_registry(self, registry: CustomerRegistry) -> None:
        """Reject a retained owner after coordinator registry refresh or route drift."""
        current = {item.spec.customer_key: item for item in registry.customers}
        receipt_owner = self._data.receipt.owner
        if (
            registry.owner.user_id != receipt_owner.user_id
            or registry.owner.chat_id != receipt_owner.chat_id
        ):
            raise WeeklyReminderOwnerError("weekly reminder owner registry drift")
        if set(current) != set(self._customers):
            raise WeeklyReminderOwnerError("weekly reminder owner registry drift")
        verified: dict[str, CustomerRuntime] = {}
        for key, customer in self._customers.items():
            runtime = current[key]
            retained = customer.runtime
            consent = runtime.spec.ai_processing_consent
            if (
                runtime.spec.customer_key != retained.spec.customer_key
                or runtime.spec.enabled != retained.spec.enabled
                or runtime.registered_binding != retained.registered_binding
                or runtime.customer_root != retained.customer_root
                or runtime.wizard_root != retained.wizard_root
                or runtime.nutrition_plans_root != retained.nutrition_plans_root
                or bind_registered_weekly_reminder_route(runtime).digest
                != bind_registered_weekly_reminder_route(retained).digest
            ):
                raise WeeklyReminderOwnerError("weekly reminder owner route drift")
            if (
                not consent.granted
                or self._data.context_source.consent_digest(runtime)
                != self._data.receipt.consent_digest
            ):
                raise WeeklyReminderOwnerError("weekly reminder owner consent drift")
            verified[key] = runtime
        self._current_runtimes = verified

    @property
    def customer_keys(self) -> tuple[str, ...]:
        """Return only separately authorized customer keys in stable order."""
        return self._data.receipt.enabled_customer_keys

    def tick_snapshot(
        self, customer_key: str, now: datetime
    ) -> WeeklyOperationsTickSnapshot:
        """Revalidate Todo2 authority and retain exact Todo3/4 capabilities."""
        customer = self._customers.get(customer_key)
        if customer is None:
            raise WeeklyReminderOwnerError("weekly operations customer is not registered")
        runtime = self._current_runtimes.get(customer_key)
        if runtime is None:
            raise WeeklyReminderOwnerError("weekly operations current runtime is unavailable")
        context = self._data.context_source.current_context(runtime, now)
        if context.customer_key != customer_key or not weekly_operations_is_authorized(
            self._data.config, self._data.receipt, context
        ):
            raise WeeklyReminderOwnerError("weekly operations Todo 2 authority is inactive")
        _ = self.bound_customer(customer_key, now)
        return WeeklyOperationsTickSnapshot(
            self._data.config, self._data.receipt, context, customer
        )

    def bound_customer(
        self, customer_key: str, now: datetime
    ) -> BoundWeeklyReminderCustomer:
        customer = self._customers.get(customer_key)
        if customer is None:
            raise WeeklyReminderOwnerError("weekly reminder customer is not registered")
        runtime = self._current_runtimes.get(customer_key)
        if runtime is None:
            raise WeeklyReminderOwnerError("weekly reminder current runtime is unavailable")
        context = self._data.context_source.current_context(runtime, now)
        if context.customer_key != customer_key:
            raise WeeklyReminderOwnerError("weekly reminder context customer drift")
        if not weekly_operations_is_authorized(
            self._data.config, self._data.receipt, context
        ):
            raise WeeklyReminderOwnerError("weekly reminder Todo 2 authority is inactive")
        route = bind_registered_weekly_reminder_route(customer.runtime)
        owner = self._data.receipt.owner
        owner_digest = hashlib.sha256(
            f"{owner.user_id}\0{owner.chat_id}\0{owner.version}".encode()
        ).hexdigest()
        proof = seal_weekly_reminder_authorization(
            WeeklyReminderAuthorizationFacts(
                CustomerKey(customer_key),
                context.candidate_digest,
                self._data.config.digest,
                self._data.registry_digest,
                owner_digest,
                context.consent_digest,
                context.feature_epoch,
                route,
            )
        )
        return bind_weekly_reminder_customer(
            WeeklyReminderBindingInput(
                proof, customer.ledger, customer.source, customer.store
            )
        )

    def close(self) -> None:
        for customer in reversed(tuple(self._customers.values())):
            customer.close()
