"""SnapChore sub-client — capture, verify, seal, and chain operations."""

from __future__ import annotations

from typing import Any, Mapping

from sbn._http import HttpTransport

PREFIX = "/internal/snapchore"

# SBN server whitelist — seal rejects anything outside this set.
_VALID_BLOCK_TYPES = {"event", "stimulus", "audit", "instruction", "meta"}


class SnapChoreClient:
    """Hash capture, verification, sealing, and chain management."""

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

    # ── Core block operations ──────────────────────────────────────────

    def capture(
        self,
        payload: Mapping[str, Any],
        *,
        volatile_keys: list[str] | None = None,
    ) -> dict[str, Any]:
        """Capture a block — hash the payload and register in the ledger.

        Returns ``{"hash": "<64-char hex>", "canon_version": "SVP-1.0"}``.
        The hash is a raw SHA-256 hex digest (no ``sha256:`` prefix).
        """
        body: dict[str, Any] = {"payload": dict(payload)}
        if volatile_keys:
            body["volatile_keys"] = volatile_keys
        return self._t.post(f"{PREFIX}/capture", json=body).json()

    def verify(
        self,
        expected_hash: str,
        payload: Mapping[str, Any],
        *,
        volatile_keys: list[str] | None = None,
    ) -> dict[str, Any]:
        """Verify a snapchore hash matches the given payload.

        Returns ``{"valid": true/false, ...}``.
        """
        body: dict[str, Any] = {
            "expected_hash": expected_hash,
            "payload": dict(payload),
        }
        if volatile_keys:
            body["volatile_keys"] = volatile_keys
        return self._t.post(f"{PREFIX}/verify", json=body).json()

    def seal(
        self,
        payload: Mapping[str, Any],
        *,
        domain: str = "general",
        block_type: str = "event",
        metadata: Mapping[str, Any] | None = None,
        metrics: Mapping[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Seal a block — commit it to the lattice via the aggregator.

        Flows through the validator quorum for attestation.

        Raises:
            ValueError: If *block_type* is not in the server whitelist.
        """
        if block_type not in _VALID_BLOCK_TYPES:
            raise ValueError(
                f"Invalid block_type {block_type!r}; "
                f"must be one of {sorted(_VALID_BLOCK_TYPES)}"
            )
        body: dict[str, Any] = {
            "domain": domain,
            "block_type": block_type,
            "payload": dict(payload),
        }
        if metadata is not None:
            body["metadata"] = dict(metadata)
        if metrics is not None:
            body["metrics"] = dict(metrics)
        return self._t.post(f"{PREFIX}/seal", json=body).json()

    def verify_block(
        self,
        block: Mapping[str, Any],
    ) -> dict[str, Any]:
        """Verify a sealed block's integrity.

        Pass the full block dict (as returned by ``seal()``).
        """
        return self._t.post(
            f"{PREFIX}/block/verify", json={"block": dict(block)}
        ).json()

    def discover(self, snapchore_hash: str) -> dict[str, Any]:
        """Look up a hash in the ledger by exact match."""
        return self._t.get(f"{PREFIX}/discover/{snapchore_hash}").json()

    def discover_recent(
        self,
        *,
        limit: int = 20,
        offset: int = 0,
        since: str | None = None,
        source: str | None = None,
        domain: str | None = None,
    ) -> dict[str, Any]:
        """List recent hashes from the ledger.

        Supports filtering by ``source`` (seal, capture, chain, aggregator,
        import) and ``domain`` (supports wildcard prefix, e.g. ``"numa.*"``).

        Args:
            limit: Max entries per page (default 20).
            offset: Pagination offset.
            since: ISO-8601 cursor — only entries created after this time.
            source: Filter by ledger entry source.
            domain: Filter by domain (wildcard prefix OK).
        """
        params: dict[str, str] = {"limit": str(limit)}
        if offset:
            params["offset"] = str(offset)
        if since:
            params["since"] = since
        if source:
            params["source"] = source
        if domain:
            params["domain"] = domain
        return self._t.get(f"{PREFIX}/discover", params=params).json()

    def receipts_discover(
        self,
        *,
        domain: str | None = None,
        hash: str | None = None,
        project_id: str | None = None,
        limit: int = 20,
        offset: int = 0,
        since: str | None = None,
    ) -> dict[str, Any]:
        """Cross-project receipt discovery (service-auth).

        Endpoint: ``GET /internal/snapchore/receipts/discover``

        Unlike ``discover_recent()`` (which queries the hash ledger),
        this endpoint queries the receipt catalogue directly and supports
        cross-project queries when authenticated with SERVICE_CLIENT_SECRET.

        Args:
            domain: Filter by domain (wildcard suffix OK, e.g. ``"numa.*"``).
            hash: Exact receipt hash lookup.
            project_id: Scope to a specific project.
            limit: Max entries per page (default 20, max 200).
            offset: Pagination offset.
            since: ISO-8601 cursor — only receipts created after this time.
        """
        params: dict[str, str] = {"limit": str(limit)}
        if offset:
            params["offset"] = str(offset)
        if since:
            params["since"] = since
        if domain:
            params["domain"] = domain
        if hash:
            params["hash"] = hash
        if project_id:
            params["project_id"] = project_id
        return self._t.get(f"{PREFIX}/receipts/discover", params=params).json()

    # ── Chain operations ───────────────────────────────────────────────

    def create_chain(
        self,
        chain_id: str | None = None,
    ) -> dict[str, Any]:
        """Create a new chain.

        Returns ``{"chain_id": "...", "created_at": "..."}``.
        """
        body: dict[str, Any] = {}
        if chain_id:
            body["chain_id"] = chain_id
        return self._t.post(f"{PREFIX}/chain", json=body).json()

    def append_to_chain(
        self,
        chain_id: str,
        *,
        block_hash: str,
        block_id: str | None = None,
        event_type: str = "block_sealed",
        metadata: Mapping[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Append an entry to an existing chain."""
        body: dict[str, Any] = {
            "block_hash": block_hash,
            "event_type": event_type,
        }
        if block_id:
            body["block_id"] = block_id
        if metadata is not None:
            body["metadata"] = dict(metadata)
        return self._t.post(f"{PREFIX}/chain/{chain_id}/append", json=body).json()

    def get_chain(self, chain_id: str) -> dict[str, Any]:
        """Retrieve the full chain state."""
        return self._t.get(f"{PREFIX}/chain/{chain_id}").json()

    def list_chains(self) -> dict[str, Any]:
        """List all chains for the current project (max 100)."""
        return self._t.get(f"{PREFIX}/chains").json()

    def verify_chain(self, chain_id: str) -> dict[str, Any]:
        """Verify chain integrity."""
        return self._t.post(f"{PREFIX}/chain/{chain_id}/verify", json={}).json()

    def get_stats(self) -> dict[str, Any]:
        """Get SnapChore system statistics."""
        return self._t.get(f"{PREFIX}/stats").json()

    def get_usage(self) -> dict[str, Any]:
        """Get monthly usage meters for the current billing period."""
        return self._t.get(f"{PREFIX}/usage").json()

    # ── SnapChore API keys ─────────────────────────────────────────────

    def create_api_key(
        self,
        *,
        name: str = "SnapChore key",
        mode: str = "live",
        plan: str | None = None,
    ) -> dict[str, Any]:
        """Create a SnapChore-scoped API key.

        Returns the full key (shown once) plus key metadata.
        """
        body: dict[str, Any] = {"name": name, "mode": mode}
        if plan:
            body["plan"] = plan
        return self._t.post(f"{PREFIX}/api-keys", json=body).json()

    def list_api_keys(self) -> dict[str, Any]:
        """List all SnapChore API keys for the current project."""
        return self._t.get(f"{PREFIX}/api-keys").json()

    def revoke_api_key(self, key_id: str) -> dict[str, Any]:
        """Revoke a SnapChore API key.  Idempotent."""
        return self._t.post(f"{PREFIX}/api-keys/{key_id}/revoke", json={}).json()

    def reconcile(self) -> dict[str, Any]:
        """Reconcile unconfirmed ledger entries against the receipt catalogue.

        Updates ``lattice_confirmed`` for entries that have been attested.
        """
        return self._t.post(f"{PREFIX}/reconcile", json={}).json()
