"""
Python SDK for OpenFGA

API version: 1.x
Website: https://openfga.dev
Documentation: https://openfga.dev/docs
Support: https://openfga.dev/community
License: [Apache-2.0](https://github.com/openfga/python-sdk/blob/main/LICENSE)

NOTE: This file was auto generated by OpenAPI Generator (https://openapi-generator.tech). DO NOT EDIT.
"""

# Specific FGA header to be parsed
X_RATELIMIT_LIMIT = "x-ratelimit-limit"
X_RATELIMIT_REMAINING = "x_ratelimit_remaining"
X_RATELIMIT_RESET = "x_ratelimit_reset"
FGA_REQUEST_ID = "fga-request-id"
FGA_QUERY_DURATION_MS = "fga-query-duration-ms"
OPENFGA_AUTHORIZATION_MODEL_ID = "openfga_authorization_model_id"
RETRY_AFTER = "retry-after"
RESPONSE_HEADERS_TO_KEEP = [
    X_RATELIMIT_LIMIT,
    X_RATELIMIT_REMAINING,
    X_RATELIMIT_RESET,
    FGA_REQUEST_ID,
    FGA_QUERY_DURATION_MS,
    OPENFGA_AUTHORIZATION_MODEL_ID,
    RETRY_AFTER,
]


class OpenApiException(Exception):
    """The base exception class for all OpenAPIExceptions"""


class FgaValidationException(OpenApiException, TypeError):
    def __init__(self, msg, path_to_item=None, valid_classes=None, key_type=None):
        """Raises an exception for TypeErrors

        Args:
            msg (str): the exception message

        Keyword Args:
            path_to_item (list): a list of keys an indices to get to the
                                 current_item
                                 None if unset
            valid_classes (tuple): the primitive classes that current item
                                   should be an instance of
                                   None if unset
            key_type (bool): False if our value is a value in a dict
                             True if it is a key in a dict
                             False if our item is an item in a list
                             None if unset
        """
        self.path_to_item = path_to_item
        self.valid_classes = valid_classes
        self.key_type = key_type
        full_msg = msg
        if path_to_item:
            full_msg = f"{msg} at {render_path(path_to_item)}"
        super().__init__(full_msg)


class ApiValueError(OpenApiException, ValueError):
    def __init__(self, msg, path_to_item=None):
        """
        Args:
            msg (str): the exception message

        Keyword Args:
            path_to_item (list) the path to the exception in the
                received_data dict. None if unset
        """

        self.path_to_item = path_to_item
        full_msg = msg
        if path_to_item:
            full_msg = f"{msg} at {render_path(path_to_item)}"
        super().__init__(full_msg)


class ApiAttributeError(OpenApiException, AttributeError):
    def __init__(self, msg, path_to_item=None):
        """
        Raised when an attribute reference or assignment fails.

        Args:
            msg (str): the exception message

        Keyword Args:
            path_to_item (None/list) the path to the exception in the
                received_data dict
        """
        self.path_to_item = path_to_item
        full_msg = msg
        if path_to_item:
            full_msg = f"{msg} at {render_path(path_to_item)}"
        super().__init__(full_msg)


class ApiKeyError(OpenApiException, KeyError):
    def __init__(self, msg, path_to_item=None):
        """
        Args:
            msg (str): the exception message

        Keyword Args:
            path_to_item (None/list) the path to the exception in the
                received_data dict
        """
        self.path_to_item = path_to_item
        full_msg = msg
        if path_to_item:
            full_msg = f"{msg} at {render_path(path_to_item)}"
        super().__init__(full_msg)


class ApiException(OpenApiException):
    def __init__(
        self, status=None, reason=None, http_resp=None, *, operation_name=None
    ):
        if http_resp:
            try:
                headers = http_resp.headers.items()
            except AttributeError:
                headers = http_resp.getheaders().items()

            self.status = http_resp.status
            self.reason = http_resp.reason
            self.body = http_resp.data
            self._parsed_exception = None
            normalized_headers = {k.lower(): v for k, v in headers}
            self.header = dict()
            for key in RESPONSE_HEADERS_TO_KEEP:
                self.header[key] = normalized_headers.get(key)
        else:
            self.status = status
            self.reason = reason
            self.body = None
            self._parsed_exception = None
            self.header = dict()

        self.operation_name = operation_name

    def __str__(self):
        """
        Format error with operation context and structured details.
        Returns formatted string like:
            [write] HTTP 400 type 'invalid_type' not found (validation_error) [request-id: abc-123]
        """
        parts = []

        # Add operation context
        if self.operation_name:
            parts.append(f"[{self.operation_name}]")

        # Add error type/status
        if self.status:
            parts.append(f"HTTP {self.status}")

        # Add error message (parsed or reason)
        if self.error_message:
            parts.append(self.error_message)

        # Add error code in parentheses
        if self.code:
            parts.append(f"({self.code})")

        # Add request ID for debugging
        if self.request_id:
            parts.append(f"[request-id: {self.request_id}]")

        return " ".join(parts) if parts else "Unknown API error"

    @property
    def parsed_exception(self):
        """
        Return the parsed body of the exception
        """
        return self._parsed_exception

    @parsed_exception.setter
    def parsed_exception(self, content):
        """
        Update the deserialized content
        """
        self._parsed_exception = content

    @property
    def code(self):
        """
        Get the error code from the parsed exception.

        Returns:
            Error code string (e.g., "validation_error") or None
        """
        if self._parsed_exception and hasattr(self._parsed_exception, "code"):
            code_value = self._parsed_exception.code
            # Handle enum types
            if hasattr(code_value, "value"):
                return code_value.value
            return str(code_value) if code_value is not None else None
        return None

    @property
    def error_message(self):
        """
        Get the human-readable error message.

        Returns:
            Error message from API or HTTP reason phrase
        """
        if self._parsed_exception and hasattr(self._parsed_exception, "message"):
            message = self._parsed_exception.message
            if message:
                return message
        return self.reason or "Unknown error"

    @property
    def request_id(self):
        """
        Get the request ID for debugging and support.

        Returns:
            FGA request ID from response headers or None
        """
        if not self.header:
            return None
        # HTTP headers are case-insensitive, try different cases
        for key in self.header:
            if key.lower() == FGA_REQUEST_ID:
                return self.header[key]
        return None

    def is_validation_error(self):
        """
        Check if this is a validation error.

        Returns:
            True if error code indicates validation failure
        """
        return isinstance(self, ValidationException) or (
            self.code and "validation" in self.code.lower()
        )

    def is_not_found_error(self):
        """
        Check if this is a not found (404) error.

        Returns:
            True if HTTP status is 404
        """
        return isinstance(self, NotFoundException) or self.status == 404

    def is_authentication_error(self):
        """
        Check if this is an authentication (401) error.

        Returns:
            True if HTTP status is 401
        """
        return self.status == 401

    def is_rate_limit_error(self):
        """
        Check if this is a rate limit (429) error.

        Returns:
            True if HTTP status is 429 or error code indicates rate limiting
        """
        return self.status == 429 or (self.code and "rate_limit" in self.code.lower())

    def is_retryable(self):
        """
        Check if this error should be retried.

        Returns:
            True if error is temporary and retrying may succeed
        """
        return self.status in [429, 500, 502, 503, 504] if self.status else False

    def is_client_error(self):
        """
        Check if this is a client error (4xx).

        Returns:
            True if HTTP status is in 400-499 range
        """
        return 400 <= self.status < 500 if self.status else False

    def is_server_error(self):
        """
        Check if this is a server error (5xx).

        Returns:
            True if HTTP status is in 500-599 range
        """
        return 500 <= self.status < 600 if self.status else False


class NotFoundException(ApiException):
    def __init__(
        self, status=None, reason=None, http_resp=None, *, operation_name=None
    ):
        super().__init__(status, reason, http_resp, operation_name=operation_name)


class UnauthorizedException(ApiException):
    def __init__(
        self, status=None, reason=None, http_resp=None, *, operation_name=None
    ):
        super().__init__(status, reason, http_resp, operation_name=operation_name)


class ForbiddenException(ApiException):
    def __init__(
        self, status=None, reason=None, http_resp=None, *, operation_name=None
    ):
        super().__init__(status, reason, http_resp, operation_name=operation_name)


class ServiceException(ApiException):
    def __init__(
        self, status=None, reason=None, http_resp=None, *, operation_name=None
    ):
        super().__init__(status, reason, http_resp, operation_name=operation_name)


class ValidationException(ApiException):
    def __init__(
        self, status=None, reason=None, http_resp=None, *, operation_name=None
    ):
        super().__init__(status, reason, http_resp, operation_name=operation_name)


class AuthenticationError(ApiException):
    def __init__(
        self, status=None, reason=None, http_resp=None, *, operation_name=None
    ):
        super().__init__(status, reason, http_resp, operation_name=operation_name)


class RateLimitExceededError(ApiException):
    def __init__(
        self, status=None, reason=None, http_resp=None, *, operation_name=None
    ):
        super().__init__(status, reason, http_resp, operation_name=operation_name)


def render_path(path_to_item):
    """Returns a string representation of a path"""
    result = ""
    for pth in path_to_item:
        if isinstance(pth, int):
            result += f"[{pth}]"
        else:
            result += f"['{pth}']"
    return result
