"""Verify a supplied operator binding against the canonical customer registry.

Binding digests are unkeyed integrity/provenance values, not signatures or
authentication. This module deliberately exposes no receipt or identity issuer.
"""

from __future__ import annotations

import fcntl
import hashlib
import json
import os
import stat
from dataclasses import dataclass
from pathlib import Path
from collections.abc import Mapping
from typing import ClassVar, Final

from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr, ValidationError

from .nutrition_weekly_operations_config import WeeklyOperationsAuthorityError
from .nutrition_weekly_reminder_resources import ResourceRegistrar

_IDENTITY_SCHEMA: Final = "nutricoach-weekly-registry-identity-v1"
_DIRECTORY_FLAGS: Final = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW | os.O_CLOEXEC
_FILE_FLAGS: Final = os.O_RDONLY | os.O_NONBLOCK | os.O_NOFOLLOW | os.O_CLOEXEC


class _IdentityInput(BaseModel):
    model_config: ClassVar[ConfigDict] = ConfigDict(
        frozen=True, extra="forbid", strict=True
    )
    schema_version: StrictStr
    relative_name: StrictStr
    device: StrictInt = Field(ge=0)
    inode: StrictInt = Field(ge=1)
    mode: StrictInt
    owner: StrictInt = Field(ge=0)
    links: StrictInt = Field(ge=1)
    content_digest: StrictStr
    binding_digest: StrictStr


@dataclass(frozen=True, slots=True)
class WeeklyOperationsRegistryIdentity:
    schema_version: str
    relative_name: str
    device: int
    inode: int
    mode: int
    owner: int
    links: int
    content_digest: str
    binding_digest: str

    def __post_init__(self) -> None:
        path = Path(self.relative_name)
        valid_name = (
            bool(path.parts)
            and path != Path(".")
            and not path.is_absolute()
            and ".." not in path.parts
        )
        digests = (self.content_digest, self.binding_digest)
        if (
            self.schema_version != _IDENTITY_SCHEMA
            or not valid_name
            or self.inode < 1
            or self.mode != 0o600
            or self.owner != os.geteuid()
            or self.links != 1
            or any(
                len(value) != 64
                or any(character not in "0123456789abcdef" for character in value)
                for value in digests
            )
            or self.binding_digest != registry_identity_binding_digest(self)
        ):
            raise WeeklyOperationsAuthorityError("registry identity")


def _payload(identity: WeeklyOperationsRegistryIdentity) -> bytes:
    values = {
        name: getattr(identity, name)
        for name in identity.__dataclass_fields__
        if name != "binding_digest"
    }
    return json.dumps(values, sort_keys=True, separators=(",", ":")).encode()


def registry_identity_binding_digest(identity: WeeklyOperationsRegistryIdentity) -> str:
    return hashlib.sha256(_payload(identity)).hexdigest()


def parse_weekly_operations_registry_identity(
    raw: Mapping[str, str | int],
) -> WeeklyOperationsRegistryIdentity:
    try:
        parsed = _IdentityInput.model_validate(raw)
    except ValidationError as error:
        raise WeeklyOperationsAuthorityError("registry identity shape") from error
    return WeeklyOperationsRegistryIdentity(
        parsed.schema_version,
        parsed.relative_name,
        parsed.device,
        parsed.inode,
        parsed.mode,
        parsed.owner,
        parsed.links,
        parsed.content_digest,
        parsed.binding_digest,
    )


def _open_root(
    path: Path, registrar: ResourceRegistrar
) -> tuple[int, Path]:
    absolute = path.absolute()
    descriptor = registrar.open_fd(
        lambda: os.open(absolute.anchor, _DIRECTORY_FLAGS)
    )
    for component in absolute.parts[1:]:
        child = registrar.open_fd(
            lambda component=component, descriptor=descriptor: os.open(
                component, _DIRECTORY_FLAGS, dir_fd=descriptor
            )
        )
        registrar.close_fd(descriptor)
        descriptor = child
    info = os.fstat(descriptor)
    if not stat.S_ISDIR(info.st_mode) or info.st_uid != os.geteuid():
        raise WeeklyOperationsAuthorityError("profile root identity")
    return descriptor, absolute


def open_relative(
    root_fd: int, relative_name: str, *, directory: bool,
    registrar: ResourceRegistrar,
) -> int:
    path = Path(relative_name)
    if not path.parts or path == Path(".") or path.is_absolute() or ".." in path.parts:
        raise WeeklyOperationsAuthorityError("profile-relative name")
    descriptor = registrar.open_fd(lambda: os.dup(root_fd))
    for index, component in enumerate(path.parts):
        final = index == len(path.parts) - 1
        flags = _DIRECTORY_FLAGS if directory or not final else _FILE_FLAGS
        child = registrar.open_fd(
            lambda component=component, flags=flags, descriptor=descriptor: os.open(
                component, flags, dir_fd=descriptor
            )
        )
        registrar.close_fd(descriptor)
        descriptor = child
    return descriptor

def _read(descriptor: int) -> bytes:
    size = os.fstat(descriptor).st_size
    first = os.pread(descriptor, size, 0)
    second = os.pread(descriptor, size, 0)
    if first != second or len(first) != size:
        raise WeeklyOperationsAuthorityError("registry bytes changed")
    return first


def _observed_identity(relative_name: str, descriptor: int) -> WeeklyOperationsRegistryIdentity:
    info = os.fstat(descriptor)
    if (
        not stat.S_ISREG(info.st_mode)
        or stat.S_IMODE(info.st_mode) != 0o600
        or info.st_uid != os.geteuid()
        or info.st_nlink != 1
    ):
        raise WeeklyOperationsAuthorityError("registry file identity")
    content_digest = hashlib.sha256(_read(descriptor)).hexdigest()
    values = {
        "schema_version": _IDENTITY_SCHEMA,
        "relative_name": relative_name,
        "device": info.st_dev,
        "inode": info.st_ino,
        "mode": stat.S_IMODE(info.st_mode),
        "owner": info.st_uid,
        "links": info.st_nlink,
        "content_digest": content_digest,
    }
    encoded = json.dumps(values, sort_keys=True, separators=(",", ":")).encode()
    return WeeklyOperationsRegistryIdentity(
        _IDENTITY_SCHEMA, relative_name, info.st_dev, info.st_ino,
        stat.S_IMODE(info.st_mode), info.st_uid, info.st_nlink, content_digest,
        hashlib.sha256(encoded).hexdigest(),
    )


@dataclass(frozen=True, slots=True)
class PinnedProfileRegistry:
    root_fd: int
    registry_fd: int
    root_path: Path
    identity: WeeklyOperationsRegistryIdentity
    payload: bytes

    def verify_registered(self, registrar: ResourceRegistrar) -> None:
        observed = _observed_identity(self.identity.relative_name, self.registry_fd)
        named_fd = open_relative(
            self.root_fd, self.identity.relative_name, directory=False,
            registrar=registrar,
        )
        try:
            named = os.fstat(named_fd)
            retained = os.fstat(self.registry_fd)
            if (
                observed != self.identity
                or self.payload != _read(self.registry_fd)
                or (named.st_dev, named.st_ino) != (retained.st_dev, retained.st_ino)
            ):
                raise WeeklyOperationsAuthorityError("registry attachment drift")
        finally:
            registrar.close_fd(named_fd)

    def close(self) -> None:
        fcntl.flock(self.registry_fd, fcntl.LOCK_UN)


def open_pinned_profile_registry(
    profile_root: Path, expected: WeeklyOperationsRegistryIdentity,
    registrar: ResourceRegistrar,
) -> PinnedProfileRegistry:
    root_fd, root_path = _open_root(profile_root, registrar)
    registry_fd = open_relative(
        root_fd, expected.relative_name, directory=False, registrar=registrar
    )
    observed = _observed_identity(expected.relative_name, registry_fd)
    if observed != expected:
        raise WeeklyOperationsAuthorityError("registry identity drift")
    fcntl.flock(registry_fd, fcntl.LOCK_SH)
    if _observed_identity(expected.relative_name, registry_fd) != expected:
        raise WeeklyOperationsAuthorityError("registry identity drift")
    payload = _read(registry_fd)
    pinned = registrar.adopt(
        lambda: PinnedProfileRegistry(
            root_fd, registry_fd, root_path, expected, payload
        ),
        PinnedProfileRegistry.close,
    )
    pinned.verify_registered(registrar)
    return pinned
