"""BitBlocks sub-client -- stream BitBlock events into open slots.

BitBlocks are the atomic event units that compose into SmartBlocks.
Each BitBlock carries an action, interaction, or event payload and lands
in the current generation window of its parent slot.  When the generation
seals (timer or explicit trigger), all BitBlocks in that generation are
fused into an attested SmartBlock.

Typical usage::

    client = SbnClient(base_url="https://api.smartblocks.network")
    client.authenticate_api_key("sbn_live_...")

    bb = client.bitblocks.submit(
        slot_id="abc-123",
        block_type="Action",
        domain="grid.atomic_rentals",
        payload={"action": "vehicle_checkout", "score": 0.9},
    )
"""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, List, Mapping, Sequence

from sbn._http import HttpTransport


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


@dataclass(slots=True)
class BitBlock:
    """A single BitBlock as returned by the API."""

    id: str
    slot_id: str
    block_type: str
    generation: int
    domain: str | None = None
    payload: Mapping[str, Any] = field(default_factory=dict)
    metadata: Mapping[str, Any] = field(default_factory=dict)
    snap_hash: str | None = None
    composed_block_id: str | None = None
    created_at: str | None = None

    @classmethod
    def from_dict(cls, data: Mapping[str, Any]) -> BitBlock:
        return cls(
            id=str(data.get("id", "")),
            slot_id=str(data.get("slot_id", "")),
            block_type=str(data.get("block_type", "")),
            generation=int(data.get("generation", 0)),
            domain=data.get("domain"),
            payload=dict(data.get("payload") or {}),
            metadata=dict(data.get("metadata") or {}),
            snap_hash=data.get("snap_hash"),
            composed_block_id=data.get("composed_block_id"),
            created_at=data.get("created_at"),
        )


@dataclass(slots=True)
class BitBlockList:
    """Response from listing BitBlocks."""

    bitblocks: List[BitBlock]
    slot_id: str | None = None
    count: int = 0

    @classmethod
    def from_dict(cls, data: Mapping[str, Any]) -> BitBlockList:
        raw_bbs = data.get("bitblocks", [])
        return cls(
            bitblocks=[BitBlock.from_dict(bb) for bb in raw_bbs],
            slot_id=data.get("slot_id"),
            count=int(data.get("count", len(raw_bbs))),
        )


# ---------------------------------------------------------------------------
# Client
# ---------------------------------------------------------------------------


class BitBlocksClient:
    """Submit and query BitBlock events within open slots."""

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

    def submit(
        self,
        *,
        slot_id: str,
        block_type: str,
        domain: str,
        payload: Mapping[str, Any],
        metadata: Mapping[str, Any] | None = None,
    ) -> BitBlock:
        """Submit a BitBlock into the current generation of a slot.

        Parameters
        ----------
        slot_id:
            The open slot to stream into.
        block_type:
            One of ``"Action"``, ``"Interaction"``, ``"Events"``.
        domain:
            Domain namespace (e.g. ``"grid.atomic_rentals"``).
        payload:
            Arbitrary event data for this BitBlock.
        metadata:
            Optional metadata dict attached to the BitBlock.

        Returns
        -------
        BitBlock
            The created BitBlock with its assigned generation and snap_hash.
        """
        body: dict[str, Any] = {
            "slot_id": slot_id,
            "block_type": block_type,
            "domain": domain,
            "payload": dict(payload),
        }
        if metadata is not None:
            body["metadata"] = dict(metadata)

        resp = self._t.post("/api/bitblocks/submit", json=body)
        data = resp.json()
        inner = data.get("data") or data
        return BitBlock.from_dict(inner)

    def list(
        self,
        slot_id: str,
        *,
        generation: int | None = None,
        limit: int | None = None,
    ) -> BitBlockList:
        """List BitBlocks for a slot, optionally filtered by generation.

        Parameters
        ----------
        slot_id:
            The slot whose BitBlocks to retrieve.
        generation:
            If given, only return BitBlocks from this generation number.
        limit:
            Max results to return.
        """
        params: dict[str, str] = {"slot_id": slot_id}
        if generation is not None:
            params["generation"] = str(generation)
        if limit is not None:
            params["limit"] = str(limit)

        resp = self._t.get("/api/bitblocks", params=params)
        data = resp.json()
        inner = data.get("data") or data
        return BitBlockList.from_dict(inner)
