"""Adapter contracts for external integrations."""

from __future__ import annotations

from collections.abc import Mapping
from copy import deepcopy
from dataclasses import dataclass, field
from typing import Any, Protocol, runtime_checkable

from .schema_validation import JsonSchemaInvariant, PydanticModelInvariant

__all__ = [
    "JsonSchemaInvariant",
    "PydanticModelInvariant",
    "TransparencyLogAdapter",
    "TransparencyLogReceipt",
    "TransparencyVerification",
    "normalise_transparency_receipt",
    "normalise_transparency_verification",
]


@dataclass(frozen=True, slots=True)
class TransparencyLogReceipt:
    """Receipt returned after appending evidence to a transparency log."""

    adapter: str
    reference: str
    timestamp: str | None = None
    details: Mapping[str, Any] = field(default_factory=dict)

    def __post_init__(self) -> None:
        object.__setattr__(self, "adapter", _require_non_empty_string("adapter", self.adapter))
        object.__setattr__(
            self,
            "reference",
            _require_non_empty_string("reference", self.reference),
        )
        if self.timestamp is not None:
            object.__setattr__(
                self,
                "timestamp",
                _require_non_empty_string("timestamp", self.timestamp),
            )
        if not isinstance(self.details, Mapping):
            msg = "details must be a mapping."
            raise ValueError(msg)
        object.__setattr__(self, "details", deepcopy(dict(self.details)))

    def as_dict(self) -> dict[str, Any]:
        """Return a serialisable mapping."""
        payload: dict[str, Any] = {
            "adapter": self.adapter,
            "reference": self.reference,
        }
        if self.timestamp is not None:
            payload["timestamp"] = self.timestamp
        if self.details:
            payload["details"] = dict(self.details)
        return payload


@dataclass(frozen=True, slots=True)
class TransparencyVerification:
    """Normalised transparency verification result."""

    verified: bool
    reason: str = ""
    checkpoint: str | None = None
    details: Mapping[str, Any] = field(default_factory=dict)

    def __post_init__(self) -> None:
        if not isinstance(self.verified, bool):
            msg = "verified must be a boolean."
            raise ValueError(msg)
        if not isinstance(self.reason, str):
            msg = "reason must be a string."
            raise ValueError(msg)
        if self.checkpoint is not None:
            object.__setattr__(
                self,
                "checkpoint",
                _require_non_empty_string("checkpoint", self.checkpoint),
            )
        if not isinstance(self.details, Mapping):
            msg = "details must be a mapping."
            raise ValueError(msg)
        object.__setattr__(self, "details", deepcopy(dict(self.details)))


@runtime_checkable
class TransparencyLogAdapter(Protocol):
    """Contract for append-only transparency log integrations."""

    def append(
        self,
        *,
        evidence_payload: Mapping[str, Any],
    ) -> TransparencyLogReceipt | Mapping[str, Any] | None:
        """Append evidence payload and return an optional append receipt."""

    def verify(
        self,
        *,
        evidence_payload: Mapping[str, Any],
        receipt: TransparencyLogReceipt | None = None,
    ) -> TransparencyVerification | Mapping[str, Any] | bool:
        """Verify transparency-log inclusion for an evidence payload."""


def normalise_transparency_receipt(
    value: TransparencyLogReceipt | Mapping[str, Any] | None,
) -> TransparencyLogReceipt | None:
    """Normalise append receipt values from transparency adapters."""
    if value is None:
        return None
    if isinstance(value, TransparencyLogReceipt):
        return value
    if not isinstance(value, Mapping):
        msg = "Transparency receipt must be a mapping, TransparencyLogReceipt, or None."
        raise ValueError(msg)
    missing_fields = [name for name in ("adapter", "reference") if name not in value]
    if missing_fields:
        msg = f"Transparency receipt is missing required fields: {', '.join(missing_fields)}."
        raise ValueError(msg)
    return TransparencyLogReceipt(
        adapter=value["adapter"],
        reference=value["reference"],
        timestamp=value.get("timestamp"),
        details=value.get("details", {}),
    )


def normalise_transparency_verification(
    value: TransparencyVerification | Mapping[str, Any] | bool,
) -> TransparencyVerification:
    """Normalise verification values returned from transparency adapters."""
    if isinstance(value, TransparencyVerification):
        return value
    if isinstance(value, bool):
        return TransparencyVerification(verified=value)
    if not isinstance(value, Mapping):
        msg = (
            "Transparency verification result must be a bool, mapping, or TransparencyVerification."
        )
        raise ValueError(msg)
    if "verified" not in value:
        msg = "Transparency verification result is missing required field: verified."
        raise ValueError(msg)
    return TransparencyVerification(
        verified=value["verified"],
        reason=str(value.get("reason", "")),
        checkpoint=value.get("checkpoint"),
        details=value.get("details", {}),
    )


def _require_non_empty_string(field_name: str, value: Any) -> str:
    if not isinstance(value, str) or not value.strip():
        msg = f"{field_name} must be a non-empty string."
        raise ValueError(msg)
    return value.strip()
