# src/chatiss_open_sdk/exceptions.py
from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Optional


class ChatissError(Exception):
    """SDK base error."""


class NetworkError(ChatissError):
    """Network/transport layer error (timeouts, DNS, connection reset, etc.)."""


class AuthError(ChatissError):
    """Authentication/authorization error (401/403, invalid token, etc.)."""


@dataclass(frozen=True)
class APIError(ChatissError):
    """
    API-level error returned by server (e.g. status == FAIL).

    Notes:
      - status_code: prefer your business 'code' field if available,
        otherwise fallback to HTTP status code.
      - request_id: pass through from response headers/body if your backend provides it.
      - body: raw parsed response body (dict/str/etc.) for debugging.
    """

    status_code: int
    message: str
    request_id: Optional[str] = None
    body: Any = None
    http_status: Optional[int] = None  # optional: keep HTTP status if you have it

    def __str__(self) -> str:
        parts = [f"APIError({self.status_code})"]
        if self.http_status is not None:
            parts.append(f"http_status={self.http_status}")
        if self.request_id:
            parts.append(f"request_id={self.request_id}")
        parts.append(f"message={self.message}")
        return ", ".join(parts)


@dataclass(frozen=True)
class ProtocolError(ChatissError):
    """
    Protocol/contract error: server response doesn't match the expected schema.

    Use this for:
      - missing required fields (e.g. 'status' absent)
      - invalid status values (not SUCCESS/FAIL)
      - result schema mismatch (e.g. dict expected but got list)
      - invalid JSON when JSON was expected (if you want to classify that here)
    """

    message: str
    endpoint: Optional[str] = None
    method: Optional[str] = None
    request_id: Optional[str] = None
    body: Any = None

    def __str__(self) -> str:
        parts = ["ProtocolError"]
        if self.method and self.endpoint:
            parts.append(f"{self.method} {self.endpoint}")
        if self.request_id:
            parts.append(f"request_id={self.request_id}")
        parts.append(self.message)
        return ": ".join(parts[:2]) + (f", {', '.join(parts[2:])}" if len(parts) > 2 else "")


@dataclass(frozen=True)
class SSEStreamError(ChatissError):
    """SSE stream interrupted; includes last received event/data for debugging."""
    message: str
    method: str
    endpoint: str
    last_event: Optional[str] = None
    last_data: Any = None

    def __str__(self) -> str:
        base = f"SSEStreamError: {self.message} ({self.method} {self.endpoint})"
        if self.last_event is not None:
            base += f", last_event={self.last_event}"
        if self.last_data is not None:
            base += f", last_data={self.last_data!r}"
        return base
