# This file was auto-generated by Fern from our API Definition.

import json
from typing import Any, Dict, List, Optional

from square.types.error import Error

FALLBACK_ERROR = Error(
    category="API_ERROR",
    code="Unknown",
)


class ApiError(Exception):
    """Exception thrown for any non-2XX API responses."""

    status_code: Optional[int]
    body: Any
    errors: List[Error]

    def __init__(self, *, status_code: Optional[int] = None, body: Any = None):
        """
        Initialize an ApiError.

        Args:
            status_code: HTTP status code
            body: Response body
        """
        message = "API Error"
        self.status_code = status_code
        self.body = body
        self.errors = self._parse_errors(body)

        super().__init__(self._build_message(message, status_code, body))

    def _build_message(
        self, message: str, status_code: Optional[int], body: Any
    ) -> str:
        """
        Build a detailed error message.

        Args:
            message: Base error message
            status_code: HTTP status code
            body: Response body

        Returns:
            Formatted error message
        """
        result = f"{message}\nStatus code: {status_code}"

        if body is None or body == "":
            return result

        if isinstance(body, str):
            return f"{result}\nBody: {body}"

        try:
            return f"{result}\nBody: {json.dumps(body, indent=2)}"
        except (TypeError, ValueError):
            return f"{result}\nBody: {str(body)}"

    def _parse_errors(self, body: Any) -> List[Error]:
        """
        Parse errors from the response body.

        Args:
            body: Response body

        Returns:
            List of Error objects
        """

        if body is None:
            return [FALLBACK_ERROR]

        if isinstance(body, str):
            try:
                body = json.loads(body)
            except json.JSONDecodeError:
                return [FALLBACK_ERROR]

        if hasattr(body, "__dict__"):
            try:
                json_str = json.dumps(body, default=lambda o: o.__dict__)
                body = json.loads(json_str)
            except (TypeError, ValueError):
                return [FALLBACK_ERROR]

        if not isinstance(body, dict):
            return [FALLBACK_ERROR]

        if "errors" in body:
            errors = body["errors"]
            if isinstance(errors, list):
                return [self._parse_error(error) for error in errors]

            return [self._parse_error(errors)]

        return [self._parse_error(body)]

    def _parse_error(self, data: Optional[Dict[str, Any]] = None) -> Error:
        """
        Create an Error object from error data.

        Args:
            error_data: Dictionary containing error information

        Returns:
            Error object
        """
        if not data:
            return FALLBACK_ERROR

        return Error(
            category=data.get("category", FALLBACK_ERROR.category),
            code=data.get("code", data.get("type", FALLBACK_ERROR.code)),
            detail=data.get("detail", data.get("message")),
            field=data.get("field"),
        )

    def __str__(self) -> str:
        return f"status_code: {self.status_code}, body: {self.body}"
