"""Gateway sub-client — slots, receipts, attestations, and slot blocks."""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any, Mapping

from sbn._http import HttpTransport


def _parse_datetime(value: str | None) -> datetime | None:
    if not value:
        return None
    try:
        if value.endswith("Z"):
            value = value[:-1] + "+00:00"
        return datetime.fromisoformat(value)
    except ValueError:
        return None


# ---------------------------------------------------------------------------
# Data models
# ---------------------------------------------------------------------------


@dataclass(slots=True)
class SlotCreateRequest:
    worker_id: str
    task_type: str
    generation_interval: int | None = None
    metadata: Mapping[str, Any] | None = None

    def to_payload(self) -> dict[str, Any]:
        payload: dict[str, Any] = {
            "worker_id": self.worker_id,
            "task_type": self.task_type,
        }
        if self.generation_interval is not None:
            payload["generation_interval"] = self.generation_interval
        if self.metadata:
            payload["metadata"] = dict(self.metadata)
        return payload


@dataclass(slots=True)
class SlotHandle:
    id: str
    worker_id: str
    task_type: str
    state: str
    opened_at: datetime | None
    scheduler_key: str | None = None
    receipt_id: str | None = None
    metadata: Mapping[str, Any] = field(default_factory=dict)

    @classmethod
    def from_dict(cls, data: Mapping[str, Any]) -> SlotHandle:
        return cls(
            id=str(data.get("id")),
            worker_id=str(data.get("worker_id")),
            task_type=str(data.get("task_type")),
            state=str(data.get("state", "unknown")),
            opened_at=_parse_datetime(data.get("opened_at")),
            scheduler_key=data.get("scheduler_key"),
            receipt_id=data.get("receipt_id"),
            metadata=dict(data.get("metadata") or {}),
        )


@dataclass(slots=True)
class SlotClosure:
    slot_id: str
    receipt_id: str
    closed_at: datetime | None

    @classmethod
    def from_dict(cls, data: Mapping[str, Any]) -> SlotClosure:
        return cls(
            slot_id=str(data.get("slot_id")),
            receipt_id=str(data.get("receipt_id")),
            closed_at=_parse_datetime(data.get("closed_at")),
        )


@dataclass(slots=True)
class SlotSummary:
    slot_id: str
    snap_hash: str
    snap_version: str = "1"
    metadata: Mapping[str, Any] = field(default_factory=dict)
    metrics: Mapping[str, Any] = field(default_factory=dict)

    def to_payload(self) -> dict[str, Any]:
        payload: dict[str, Any] = {
            "slot_id": self.slot_id,
            "snap_hash": self.snap_hash,
            "snap_version": self.snap_version,
        }
        if self.metadata:
            payload["metadata"] = dict(self.metadata)
        if self.metrics:
            payload["metrics"] = dict(self.metrics)
        return payload


@dataclass(slots=True)
class Receipt:
    id: str
    tenant_id: str | None
    subject_id: str
    subject_type: str
    signer_id: str | None
    created_at: datetime | None
    payload: Mapping[str, Any]
    metadata: Mapping[str, Any]
    snapchore_hash: str | None = None

    @classmethod
    def from_dict(cls, data: Mapping[str, Any]) -> Receipt:
        return cls(
            id=str(data.get("id")),
            tenant_id=data.get("tenant_id"),
            subject_id=str(data.get("subject_id")),
            subject_type=str(data.get("subject_type")),
            signer_id=data.get("signer_id"),
            created_at=_parse_datetime(data.get("created_at")),
            payload=dict(data.get("payload") or {}),
            metadata=dict(data.get("metadata") or {}),
            snapchore_hash=data.get("snapchore_hash"),
        )


# ---------------------------------------------------------------------------
# Gateway client
# ---------------------------------------------------------------------------


class GatewayClient:
    """Slot lifecycle, receipts, and attestations."""

    def __init__(self, transport: HttpTransport) -> None:
        self._t = transport

    def _slot_path(self) -> str:
        return "/slots"

    def create_slot(
        self,
        worker_id: str,
        task_type: str,
        *,
        generation_interval: int | None = None,
        metadata: Mapping[str, Any] | None = None,
    ) -> SlotHandle:
        req = SlotCreateRequest(
            worker_id=worker_id,
            task_type=task_type,
            generation_interval=generation_interval,
            metadata=metadata,
        )
        resp = self._t.post(self._slot_path(), json=req.to_payload())
        body = resp.json()
        # Write gateway wraps slot responses in {"data": {...}}
        return SlotHandle.from_dict(body.get("data") or body)

    def list_slot_blocks(
        self,
        slot_id: str,
        *,
        limit: int | None = None,
    ) -> dict[str, Any]:
        """List all SmartBlocks belonging to a slot.

        Returns ``{"slot_id": "...", "slot_state": "...", "blocks": [...], "count": N}``.
        """
        params: dict[str, str] = {}
        if limit is not None:
            params["limit"] = str(limit)
        resp = self._t.get(f"{self._slot_path()}/{slot_id}/blocks", params=params)
        body = resp.json()
        return body.get("data") or body

    def close_slot(self, slot_id: str) -> SlotClosure:
        resp = self._t.post(f"{self._slot_path()}/{slot_id}/close", json={})
        body = resp.json()
        return SlotClosure.from_dict(body.get("data") or body)

    def fetch_receipt(self, receipt_id: str) -> Receipt:
        resp = self._t.get(f"/receipts/{receipt_id}")
        return Receipt.from_dict(resp.json())

    def request_attestation(self, summary: SlotSummary) -> Mapping[str, Any]:
        resp = self._t.post("/attest", json=summary.to_payload())
        return resp.json()

    def list_flags(self) -> Mapping[str, Any]:
        resp = self._t.get("/flags")
        data = resp.json()
        return data if isinstance(data, Mapping) else {}
