from __future__ import annotations

import json
from dataclasses import dataclass
from typing import Any, ClassVar, Optional


class StewAIError(Exception):
    """Base exception for all StewAI SDK errors."""


StewError = StewAIError


@dataclass
class ApiError(StewAIError):
    """HTTP-level API error."""

    status_code: int
    body: Any = None
    message: str = "API request failed"

    def __str__(self) -> str:
        detail = _summarize_error_body(self.body)
        base = f"{self.message} ({self.status_code})"
        return f"{base}: {detail}" if detail else base


class AuthenticationError(ApiError):
    """401/403 authentication failure."""


class RateLimitError(ApiError):
    """429 rate limit exceeded."""

    retry_after: Optional[str] = None


@dataclass
class RunError(StewAIError):
    """Base class for run-level errors."""

    run_id: str
    run: dict
    label: ClassVar[str] = "Run error"

    def __str__(self) -> str:
        detail = _summarize_error_body(self.run)
        base = f"{type(self).label}: {self.run_id}"
        return f"{base}: {detail}" if detail else base


class RunFailedError(RunError):
    """Run completed with status='failed'."""

    label = "Run failed"


class RunCancelledError(RunError):
    """Run was cancelled (status='cancelled')."""

    label = "Run cancelled"


class RunBlockedError(RunError):
    """Run blocked due to insufficient credits (status='blocked')."""

    label = "Run blocked"


@dataclass
class StewAITimeoutError(StewAIError):
    """SDK-level timeout waiting for run completion."""

    run_id: str
    timeout: float
    last_status: str
    message: str = "Timed out waiting for run"

    def __str__(self) -> str:
        return (
            f"{self.message}: {self.run_id} "
            f"(timeout={self.timeout}, last_status={self.last_status})"
        )


def _summarize_error_body(body: Any) -> str:
    if body is None:
        return ""

    if isinstance(body, dict):
        for key in ("detail", "message", "error", "code", "status"):
            value = body.get(key)
            if isinstance(value, str) and value.strip():
                return value.strip()
        return _stringify_body(body)

    if isinstance(body, str):
        return body.strip()

    return _stringify_body(body)


def _stringify_body(body: Any) -> str:
    try:
        return json.dumps(body, sort_keys=True)
    except TypeError:
        return str(body)
