"""Sealed single-customer authority for weekly reminder provider admission."""

from __future__ import annotations

import hashlib
import json
from collections.abc import Callable
from contextlib import AbstractContextManager
from dataclasses import dataclass, field, replace
from datetime import date, datetime
from typing import Final, Protocol, TypeVar

from .store import CanonicalEventSnapshot
from .weekly_operations import (
    CanonicalPin,
    CustomerIdentityDigest,
    CustomerKey,
    AppendResult,
    WeeklyOperationInput,
    WeeklyOperationRow,
    WeeklyOperationsConflict,
    customer_identity_digest,
)
from .weekly_operations_lineage import canonical_prefix_pin
from .weekly_reminder_ledger_authority import WeeklyReminderLedgerAuthority
from .weekly_reminder_route import BoundWeeklyReminderRoute

_TEMPLATE_VERSION: Final = "weekly-missing-checkin-v1"
_TEMPLATE_TEXT: Final = "체크인이 확인되지 않았습니다. 오늘 아침 체크인을 제출해 주세요."
_ZERO_DIGEST: Final = "0" * 64
_T = TypeVar("_T")


class BoundCanonicalSource(Protocol):
    @property
    def customer_identity_digest(self) -> CustomerIdentityDigest: ...

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

    @property
    def registry_authority_binding_digest(self) -> str: ...

    def read_locked(self) -> AbstractContextManager[CanonicalEventSnapshot]: ...


class BoundSidecarStore(Protocol):
    @property
    def customer_identity_digest(self) -> CustomerIdentityDigest: ...

    @property
    def authority_binding_digest(self) -> str: ...

    def read(self) -> tuple[WeeklyOperationRow, ...]: ...

    def transact(
        self,
        decide: Callable[
            [tuple[WeeklyOperationRow, ...]],
            tuple[WeeklyOperationInput | None, _T],
        ],
    ) -> tuple[AppendResult | None, _T]: ...


@dataclass(frozen=True, slots=True)
class _AuthorizationToken:
    pass


_AUTHORIZATION_TOKEN: Final = _AuthorizationToken()


@dataclass(frozen=True, slots=True)
class WeeklyReminderAuthorizationProof:
    """Gateway-sealed Todo 2 and registry facts; callers cannot forge the token."""

    customer_key: CustomerKey
    candidate_digest: str
    config_digest: str
    runtime_registry_digest: str
    owner_digest: str
    consent_digest: str
    feature_epoch: str
    route: BoundWeeklyReminderRoute
    _token: _AuthorizationToken = field(repr=False, compare=False)

    def __post_init__(self) -> None:
        if self._token is not _AUTHORIZATION_TOKEN:
            raise WeeklyOperationsConflict("weekly reminder authorization is not sealed")


@dataclass(frozen=True, slots=True)
class WeeklyReminderAuthorizationFacts:
    customer_key: CustomerKey
    candidate_digest: str
    config_digest: str
    runtime_registry_digest: str
    owner_digest: str
    consent_digest: str
    feature_epoch: str
    route: BoundWeeklyReminderRoute


def seal_weekly_reminder_authorization(
    facts: WeeklyReminderAuthorizationFacts,
) -> WeeklyReminderAuthorizationProof:
    """Issue a proof only after the gateway's Todo 2 gate succeeds."""
    return WeeklyReminderAuthorizationProof(
        facts.customer_key,
        facts.candidate_digest,
        facts.config_digest,
        facts.runtime_registry_digest,
        facts.owner_digest,
        facts.consent_digest,
        facts.feature_epoch,
        facts.route,
        _AUTHORIZATION_TOKEN,
    )

@dataclass(frozen=True, slots=True)
class WeeklyReminderBindingInput:
    """Profile capabilities joined to one gateway authorization proof."""

    authorization: WeeklyReminderAuthorizationProof
    ledger: WeeklyReminderLedgerAuthority
    source: BoundCanonicalSource
    store: BoundSidecarStore


@dataclass(frozen=True, slots=True)
class _BoundToken:
    pass


_TOKEN: Final = _BoundToken()


@dataclass(frozen=True, slots=True)
class BoundWeeklyReminderCustomer:
    """One opaque customer and every pin needed for provider admission."""

    customer_identity_digest: CustomerIdentityDigest
    ledger: WeeklyReminderLedgerAuthority = field(repr=False, compare=False)
    source: BoundCanonicalSource = field(repr=False, compare=False)
    store: BoundSidecarStore = field(repr=False, compare=False)
    candidate_digest: str
    config_digest: str
    runtime_registry_digest: str
    canonical_registry_digest: str
    canonical_binding_digest: str
    sidecar_authority_digest: str
    sidecar_history_digest: str
    owner_digest: str
    consent_digest: str
    feature_epoch: str
    route: BoundWeeklyReminderRoute
    template_version: str
    template_digest: str
    canonical: CanonicalPin
    authority_digest: str
    _token: _BoundToken = field(repr=False, compare=False)

    def __post_init__(self) -> None:
        if self._token is not _TOKEN:
            raise WeeklyOperationsConflict("weekly reminder authority is not sealed")
        if self.customer_identity_digest != self.source.customer_identity_digest:
            raise WeeklyOperationsConflict("weekly reminder canonical customer drift")
        if self.customer_identity_digest != self.store.customer_identity_digest:
            raise WeeklyOperationsConflict("weekly reminder sidecar customer drift")
        if self.customer_identity_digest != self.ledger.customer_identity_digest:
            raise WeeklyOperationsConflict("weekly reminder ledger customer drift")
        if self.customer_identity_digest != self.route.customer_identity_digest:
            raise WeeklyOperationsConflict("weekly reminder route customer drift")
        self.route.verify()
        self.ledger.verify()
        if self.authority_digest != _ZERO_DIGEST and self.authority_digest != _authority_digest(self):
            raise WeeklyOperationsConflict("weekly reminder authority digest drift")


    @property
    def route_chat_id(self) -> str:
        self.route.verify()
        return self.route.chat_id

    @property
    def route_topic_id(self) -> str | None:
        self.route.verify()
        return self.route.topic_id

    @property
    def route_digest(self) -> str:
        self.route.verify()
        return self.route.digest


@dataclass(frozen=True, slots=True)
class WeeklyReminderRequest:
    bound_customer: BoundWeeklyReminderCustomer
    kst_day: date
    now: datetime


def _digest(value: str | tuple[str, str] | dict[str, str | None]) -> str:
    encoded = json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
    return hashlib.sha256(encoded).hexdigest()


def _authority_digest(bound: BoundWeeklyReminderCustomer) -> str:
    values = (
        bound.customer_identity_digest,
        bound.candidate_digest,
        bound.config_digest,
        bound.runtime_registry_digest,
        bound.canonical_registry_digest,
        bound.canonical_binding_digest,
        bound.sidecar_authority_digest,
        bound.sidecar_history_digest,
        bound.ledger.binding_digest,
        bound.owner_digest,
        bound.consent_digest,
        bound.feature_epoch,
        bound.route.canonical_bytes.hex(),
        bound.route.digest,
        bound.template_digest,
        str(bound.canonical.sequence),
        bound.canonical.digest,
    )
    return hashlib.sha256("\0".join(values).encode()).hexdigest()


def bind_weekly_reminder_customer(data: WeeklyReminderBindingInput) -> BoundWeeklyReminderCustomer:
    """Seal one authorized registry customer after canonical and sidecar snapshots."""
    proof = data.authorization
    identity = customer_identity_digest(proof.customer_key)
    if (
        identity != data.source.customer_identity_digest
        or identity != data.store.customer_identity_digest
        or identity != data.ledger.customer_identity_digest
        or identity != proof.route.customer_identity_digest
    ):
        raise WeeklyOperationsConflict("weekly reminder customer authorities disagree")
    if not proof.candidate_digest or not proof.consent_digest:
        raise WeeklyOperationsConflict("weekly reminder gateway authority is incomplete")
    data_rows = data.store.read()
    sidecar_history = data_rows[-1].row_digest if data_rows else _ZERO_DIGEST
    with data.source.read_locked() as snapshot:
        canonical = canonical_prefix_pin(snapshot, len(snapshot.sequence_rows))
    proof.route.verify()
    data.ledger.verify()
    template_digest = _digest((_TEMPLATE_VERSION, _TEMPLATE_TEXT))
    provisional = BoundWeeklyReminderCustomer(
        identity,
        data.ledger,
        data.source,
        data.store,
        proof.candidate_digest,
        proof.config_digest,
        proof.runtime_registry_digest,
        data.source.registry_authority_binding_digest,
        data.source.binding_digest,
        data.store.authority_binding_digest,
        sidecar_history,
        proof.owner_digest,
        proof.consent_digest,
        proof.feature_epoch,
        proof.route,
        _TEMPLATE_VERSION,
        template_digest,
        canonical,
        _ZERO_DIGEST,
        _TOKEN,
    )
    return replace(provisional, authority_digest=_authority_digest(provisional))


def reminder_template(bound: BoundWeeklyReminderCustomer) -> str:
    """Resolve the sole approved body only from its sealed authority."""
    if bound.template_version != _TEMPLATE_VERSION or bound.template_digest != _digest((_TEMPLATE_VERSION, _TEMPLATE_TEXT)):
        raise WeeklyOperationsConflict("weekly reminder template authority drift")
    return _TEMPLATE_TEXT
