"""Pure canonical-authority registration row and history validation."""

from __future__ import annotations

import hashlib
import json
from datetime import datetime, timedelta
from typing import ClassVar, Final

from pydantic import BaseModel, ConfigDict, Field, ValidationError

from checkin_cli.weekly_operations import (
    CustomerIdentityDigest,
    WeeklyOperationsCorruption,
    ZERO_DIGEST,
)

REGISTRY_NAME: Final = "canonical-authorities-v1.jsonl"
REGISTRATION_SCHEMA: Final = "nutricoach-canonical-authority-registration-v2"
_DIGEST_PATTERN: Final = r"^[0-9a-f]{64}$"


class CanonicalAuthorityRegistrationRow(BaseModel):
    model_config: ClassVar[ConfigDict] = ConfigDict(
        frozen=True, extra="forbid", strict=True
    )
    schema_version: str
    customer_identity_digest: CustomerIdentityDigest = Field(pattern=_DIGEST_PATTERN)
    binding_digest: str = Field(pattern=_DIGEST_PATTERN)
    authority_binding_digest: str = Field(pattern=_DIGEST_PATTERN)
    authority_marker_digest: str = Field(pattern=_DIGEST_PATTERN)
    registry_device: int = Field(ge=0)
    registry_inode: int = Field(ge=1)
    registry_binding_digest: str = Field(pattern=_DIGEST_PATTERN)
    root_device: int = Field(ge=0)
    root_inode: int = Field(ge=1)
    root_mode: int = Field(ge=0)
    root_owner: int = Field(ge=0)
    root_links: int = Field(ge=1)
    events_device: int = Field(ge=0)
    events_inode: int = Field(ge=1)
    events_mode: int = Field(ge=0)
    events_owner: int = Field(ge=0)
    events_links: int = Field(ge=1)
    sequence_device: int = Field(ge=0)
    sequence_inode: int = Field(ge=1)
    sequence_mode: int = Field(ge=0)
    sequence_owner: int = Field(ge=0)
    sequence_links: int = Field(ge=1)
    lock_device: int = Field(ge=0)
    lock_inode: int = Field(ge=1)
    lock_mode: int = Field(ge=0)
    lock_owner: int = Field(ge=0)
    lock_links: int = Field(ge=1)
    registration_epoch: int = Field(ge=1)
    occurred_at_utc: datetime
    predecessor_row_digest: str = Field(pattern=_DIGEST_PATTERN)
    row_digest: str = Field(pattern=_DIGEST_PATTERN)


def registration_row_digest(row: CanonicalAuthorityRegistrationRow) -> str:
    body = row.model_dump(mode="json", exclude={"row_digest"})
    encoded = json.dumps(body, sort_keys=True, separators=(",", ":")).encode()
    return hashlib.sha256(encoded).hexdigest()


def canonical_registration_row(row: CanonicalAuthorityRegistrationRow) -> bytes:
    return json.dumps(
        row.model_dump(mode="json"), sort_keys=True, separators=(",", ":")
    ).encode()


def validate_registration_history(
    payload: bytes,
) -> tuple[CanonicalAuthorityRegistrationRow, ...]:
    if payload and not payload.endswith(b"\n"):
        raise WeeklyOperationsCorruption("canonical authority registry has a torn tail")
    rows: list[CanonicalAuthorityRegistrationRow] = []
    customers: set[CustomerIdentityDigest] = set()
    predecessor = ZERO_DIGEST
    try:
        for epoch, line in enumerate(payload.splitlines(), start=1):
            if not line:
                raise WeeklyOperationsCorruption(
                    "canonical authority registry has an empty row"
                )
            row = CanonicalAuthorityRegistrationRow.model_validate_json(line)
            canonical = canonical_registration_row(row)
            valid = (
                line == canonical
                and row.schema_version == REGISTRATION_SCHEMA
                and row.registration_epoch == epoch
                and row.predecessor_row_digest == predecessor
                and row.row_digest == registration_row_digest(row)
                and row.occurred_at_utc.utcoffset() == timedelta(0)
                and row.customer_identity_digest not in customers
            )
            if not valid:
                raise WeeklyOperationsCorruption(
                    "canonical authority registry history disagrees"
                )
            rows.append(row)
            customers.add(row.customer_identity_digest)
            predecessor = row.row_digest
    except (ValidationError, UnicodeDecodeError) as error:
        raise WeeklyOperationsCorruption(
            "canonical authority registry row is malformed"
        ) from error
    return tuple(rows)
