"""Exception types raised by the gaiia client."""

from __future__ import annotations

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


class GaiiaError(Exception):
    """Base class for all gaiia client errors."""


class GaiiaTransportError(GaiiaError):
    """Network/transport failure talking to the gaiia API."""


@dataclass
class GraphQLError:
    message: str
    code: str | None = None
    path: list[str | int] | None = None
    extensions: dict[str, Any] = field(default_factory=dict)

    @classmethod
    def from_dict(cls, raw: dict[str, Any]) -> GraphQLError:
        extensions = dict(raw.get("extensions") or {})
        return cls(
            message=raw.get("message", ""),
            code=extensions.get("code"),
            path=raw.get("path"),
            extensions=extensions,
        )


class GaiiaAPIError(GaiiaError):
    """One or more top-level GraphQL errors were returned."""

    def __init__(self, errors: list[GraphQLError], data: Any | None = None) -> None:
        self.errors = errors
        self.data = data
        msg = "; ".join(e.message for e in errors) or "gaiia API error"
        super().__init__(msg)


class GaiiaMutationError(GaiiaError):
    """A mutation returned validation/expected errors under data.<mutation>.errors.

    These are the GraphQL "200 OK with errors in the payload" responses that
    represent expected failure modes (validation, business rules) rather than
    transport or schema issues.
    """

    def __init__(
        self,
        mutation: str,
        errors: list[dict[str, Any]],
        payload: dict[str, Any] | None = None,
    ) -> None:
        self.mutation = mutation
        self.errors = errors
        self.payload = payload
        codes = [e.get("code") for e in errors if e.get("code")]
        msg = (
            f"mutation {mutation!r} returned errors: "
            + ", ".join(f"{c}: {e.get('message','')}" for c, e in zip(codes, errors, strict=False))
            if codes
            else f"mutation {mutation!r} returned errors"
        )
        super().__init__(msg)


class GaiiaRateLimitedError(GaiiaAPIError):
    """Raised when the API returns a RATE_LIMITED error."""

    def __init__(
        self,
        errors: list[GraphQLError],
        *,
        cost: int | None = None,
        limit: int | None = None,
        used: int | None = None,
        remaining: int | None = None,
        retry_at: datetime | None = None,
    ) -> None:
        super().__init__(errors)
        self.cost = cost
        self.limit = limit
        self.used = used
        self.remaining = remaining
        self.retry_at = retry_at
