"""Console sub-client — projects, API keys, usage, and billing."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Mapping, Sequence

from sbn._http import HttpTransport

PREFIX = "/internal"


# ---------------------------------------------------------------------------
# Response models
# ---------------------------------------------------------------------------


@dataclass(slots=True)
class ApiKeyLimits:
    attestations_per_month: int | None
    snapchore_events_per_month: int | None
    facts_reads_per_month: int | None

    @classmethod
    def from_dict(cls, data: Mapping[str, Any]) -> ApiKeyLimits:
        return cls(
            attestations_per_month=data.get("attestations_per_month"),
            snapchore_events_per_month=data.get("snapchore_events_per_month"),
            facts_reads_per_month=data.get("facts_reads_per_month"),
        )


@dataclass(slots=True)
class ApiKeyRecord:
    id: str
    project_id: str
    label: str
    prefix: str
    scopes: list[str]
    plan: str
    revoked: bool
    limits: ApiKeyLimits
    rate: str
    default_frontier: str | None = None
    allowed_frontiers: list[str] | None = None

    @classmethod
    def from_dict(cls, data: Mapping[str, Any]) -> ApiKeyRecord:
        limits_raw = data.get("limits") or {}
        return cls(
            id=str(data.get("id")),
            project_id=str(data.get("project_id") or data.get("projectId")),
            label=str(data.get("label", "")),
            prefix=str(data.get("prefix", "")),
            scopes=list(data.get("scopes") or []),
            plan=str(data.get("plan", "sandbox")),
            revoked=bool(data.get("revoked")),
            limits=ApiKeyLimits.from_dict(limits_raw),
            rate=str(data.get("rate", "")),
            default_frontier=data.get("default_frontier") or data.get("defaultFrontier"),
            allowed_frontiers=data.get("allowed_frontiers") or data.get("allowedFrontiers"),
        )


@dataclass(slots=True)
class ProjectUsage:
    project_id: str
    month: str
    keys: list[dict[str, Any]]

    @classmethod
    def from_dict(cls, data: Mapping[str, Any]) -> ProjectUsage:
        return cls(
            project_id=str(data.get("project_id", "")),
            month=str(data.get("month", "")),
            keys=list(data.get("keys") or []),
        )


# ---------------------------------------------------------------------------
# Console client
# ---------------------------------------------------------------------------


class ConsoleClient:
    """Projects, API keys, usage, and billing."""

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

    # ── Projects ───────────────────────────────────────────────────────

    def list_projects(self) -> list[dict[str, Any]]:
        return self._t.get("/console/projects").json()

    def create_project(self, name: str) -> dict[str, Any]:
        return self._t.post("/console/projects", json={"name": name}).json()

    def switch_project(self, project_id: str) -> dict[str, Any]:
        return self._t.post(f"/console/projects/{project_id}/switch", json={}).json()

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

    def list_api_keys(self, project_id: str | None = None) -> list[ApiKeyRecord]:
        """List API keys for a project.

        If ``project_id`` is omitted the server uses the session project.
        """
        path = f"{PREFIX}/projects/{project_id}/api-keys" if project_id else f"{PREFIX}/api-keys"
        data = self._t.get(path).json()
        keys_raw = data.get("keys", data) if isinstance(data, dict) else data
        if isinstance(keys_raw, list):
            return [ApiKeyRecord.from_dict(k) for k in keys_raw]
        return []

    def create_api_key(
        self,
        project_id: str,
        *,
        label: str = "SDK key",
        plan: str = "sandbox",
        scopes: Sequence[str] | None = None,
        default_frontier: str | None = None,
        allowed_frontiers: Sequence[str] | None = None,
    ) -> dict[str, Any]:
        """Create a new API key.

        Returns ``{"api_key": "sbn_live_...", "prefix": "...", "record": {...}}``.
        """
        body: dict[str, Any] = {"label": label, "plan": plan}
        if scopes:
            body["scopes"] = list(scopes)
        if default_frontier:
            body["defaultFrontier"] = default_frontier
        if allowed_frontiers:
            body["allowedFrontiers"] = list(allowed_frontiers)
        return self._t.post(f"{PREFIX}/projects/{project_id}/api-keys", json=body).json()

    def revoke_api_key(self, project_id: str, key_id: str) -> dict[str, Any]:
        return self._t.post(
            f"{PREFIX}/projects/{project_id}/api-keys/{key_id}/revoke", json={}
        ).json()

    def rotate_api_key(self, project_id: str, key_id: str) -> dict[str, Any]:
        """Rotate a key — revokes the old one and returns a new secret."""
        return self._t.post(
            f"{PREFIX}/projects/{project_id}/api-keys/{key_id}/rotate", json={}
        ).json()

    # ── Usage & quota ──────────────────────────────────────────────────

    def get_usage(self, project_id: str | None = None) -> ProjectUsage:
        """Get usage metrics for the current billing period."""
        path = (
            f"{PREFIX}/projects/{project_id}/usage/keys"
            if project_id
            else f"{PREFIX}/usage/keys"
        )
        return ProjectUsage.from_dict(self._t.get(path).json())

    def get_usage_summary(self, project_id: str) -> dict[str, Any]:
        """Monthly rollup with daily trends."""
        return self._t.get(f"{PREFIX}/projects/{project_id}/usage/summary").json()

    def export_usage_csv(self, project_id: str) -> str:
        """Export usage as CSV text."""
        resp = self._t.get(f"{PREFIX}/projects/{project_id}/usage.csv")
        return resp.text

    # ── Key event audit ────────────────────────────────────────────────

    def get_key_events(self, project_id: str, key_id: str) -> list[dict[str, Any]]:
        """Audit trail for a specific API key."""
        data = self._t.get(
            f"{PREFIX}/projects/{project_id}/api-keys/{key_id}/events"
        ).json()
        return data if isinstance(data, list) else data.get("events", [])

    # ── Billing ────────────────────────────────────────────────────────

    def get_billing_status(self, project_id: str) -> dict[str, Any]:
        return self._t.get(f"{PREFIX}/projects/{project_id}/billing/status").json()

    def create_checkout(self, project_id: str, plan_id: str) -> dict[str, Any]:
        """Start a Stripe checkout session. Returns ``{"checkout_url": "..."}``."""
        return self._t.post(
            f"{PREFIX}/projects/{project_id}/billing/checkout",
            json={"plan": plan_id},
        ).json()

    def get_portal_url(self, project_id: str) -> dict[str, Any]:
        """Get Stripe customer portal URL."""
        return self._t.post(
            f"{PREFIX}/projects/{project_id}/billing/portal", json={}
        ).json()

    def pay_overage(self, project_id: str) -> dict[str, Any]:
        """Create a checkout session for usage overage payment."""
        return self._t.post(
            f"{PREFIX}/projects/{project_id}/billing/pay-overage", json={}
        ).json()

    # ── Project management ────────────────────────────────────────────

    def get_project(self) -> dict[str, Any]:
        """Get the current project details."""
        return self._t.get(f"{PREFIX}/project").json()

    def update_project(
        self,
        *,
        name: str | None = None,
        description: str | None = None,
    ) -> dict[str, Any]:
        """Update the current project's name or description."""
        body: dict[str, Any] = {}
        if name is not None:
            body["name"] = name
        if description is not None:
            body["description"] = description
        return self._t.patch(f"{PREFIX}/project", json=body).json()

    def get_project_settings(self) -> dict[str, Any]:
        """Get the current project settings."""
        return self._t.get(f"{PREFIX}/project/settings").json()

    def update_project_settings(
        self,
        settings: Mapping[str, Any],
    ) -> dict[str, Any]:
        """Update the current project settings."""
        return self._t.patch(f"{PREFIX}/project/settings", json=dict(settings)).json()

    def archive_project(self) -> dict[str, Any]:
        """Archive the current project."""
        return self._t.post(f"{PREFIX}/project/archive", json={}).json()

    def unarchive_project(self) -> dict[str, Any]:
        """Unarchive the current project."""
        return self._t.post(f"{PREFIX}/project/unarchive", json={}).json()

    def delete_project(self) -> dict[str, Any]:
        """Permanently delete the current project.

        .. warning::  This is irreversible.
        """
        return self._t.delete(f"{PREFIX}/project").json()

    # ── Audit ─────────────────────────────────────────────────────────

    def get_audit_log(self) -> dict[str, Any]:
        """Get the combined audit log for the current project."""
        return self._t.get(f"{PREFIX}/project/audit").json()

    # ── Analytics ─────────────────────────────────────────────────────

    def get_gec_trend(self, project_id: str) -> dict[str, Any]:
        """GEC efficiency trend analysis for a project."""
        return self._t.get(
            f"{PREFIX}/projects/{project_id}/analytics/gec-trend"
        ).json()

    def get_frontier_breakdown(self, project_id: str) -> dict[str, Any]:
        """Per-frontier attestation breakdown for a project."""
        return self._t.get(
            f"{PREFIX}/projects/{project_id}/analytics/frontier-breakdown"
        ).json()

    def get_csk_breakdown(self, project_id: str) -> dict[str, Any]:
        """CSK dimension averages for a project (builder+ plan)."""
        return self._t.get(
            f"{PREFIX}/projects/{project_id}/analytics/csk-breakdown"
        ).json()

    # ── Webhooks ──────────────────────────────────────────────────────

    def list_webhooks(self, project_id: str) -> dict[str, Any]:
        """List all webhook endpoints for a project."""
        return self._t.get(
            f"{PREFIX}/projects/{project_id}/webhooks"
        ).json()

    def create_webhook(
        self,
        project_id: str,
        *,
        url: str,
        events: Sequence[str] | None = None,
        secret: str | None = None,
        enabled: bool = True,
    ) -> dict[str, Any]:
        """Create a new webhook endpoint.

        Parameters
        ----------
        url : str
            The HTTPS endpoint URL.
        events : list of str, optional
            Event types to subscribe to (e.g. ``["governance.proposal_submitted"]``).
            Omit to receive all events.
        secret : str, optional
            Shared signing secret. Auto-generated if omitted.
        enabled : bool
            Whether the webhook is active (default True).
        """
        body: dict[str, Any] = {"url": url, "enabled": enabled}
        if events:
            body["events"] = list(events)
        if secret is not None:
            body["secret"] = secret
        return self._t.post(
            f"{PREFIX}/projects/{project_id}/webhooks", json=body
        ).json()

    def update_webhook(
        self,
        project_id: str,
        webhook_id: str,
        **kwargs: Any,
    ) -> dict[str, Any]:
        """Update a webhook endpoint. Pass any fields to update."""
        return self._t.patch(
            f"{PREFIX}/projects/{project_id}/webhooks/{webhook_id}",
            json=kwargs,
        ).json()

    def delete_webhook(self, project_id: str, webhook_id: str) -> dict[str, Any]:
        """Delete a webhook endpoint."""
        return self._t.delete(
            f"{PREFIX}/projects/{project_id}/webhooks/{webhook_id}"
        ).json()

    def test_webhook(self, project_id: str, webhook_id: str) -> dict[str, Any]:
        """Send a test event to a webhook endpoint."""
        return self._t.post(
            f"{PREFIX}/projects/{project_id}/webhooks/{webhook_id}/test",
            json={},
        ).json()

    def list_webhook_deliveries(
        self,
        project_id: str,
        webhook_id: str,
    ) -> dict[str, Any]:
        """List delivery attempts for a webhook endpoint."""
        return self._t.get(
            f"{PREFIX}/projects/{project_id}/webhooks/{webhook_id}/deliveries"
        ).json()

    def list_event_types(self) -> dict[str, Any]:
        """List all available webhook event types."""
        return self._t.get(f"{PREFIX}/webhooks/event-types").json()


# Plans — static definitions matching the SBN tiers.
# These are informational; the server is the authority on limits.
PLANS = {
    "sandbox": {
        "title": "Sandbox",
        "price": "Free",
        "limits": {
            "attestations": 5_000,
            "snapchore_events": 25_000,
            "facts_reads": 100_000,
        },
    },
    "builder": {
        "title": "Builder",
        "price": "$49/mo",
        "limits": {
            "attestations": 100_000,
            "snapchore_events": 1_000_000,
            "facts_reads": 2_000_000,
        },
    },
    "network": {
        "title": "Network",
        "price": "Custom",
        "limits": {
            "attestations": None,
            "snapchore_events": None,
            "facts_reads": None,
        },
    },
}
