# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
# This product includes software developed at Datadog (https://www.datadoghq.com/).
# Copyright 2019-Present Datadog, Inc.

import json


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


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

        :param msg: The exception message.
        :type msg: str
        :param path_to_item: A list of keys an indices to get to the
            current_item None if unset.
        :type path_to_item: list
        :param valid_classes: The primitive classes that current item should
            be an instance of None if unset.
        :type valid_classes: tuple
        :param key_type: 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.
        :type key_type: bool
        """
        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 = "{0} at {1}".format(msg, render_path(path_to_item))
        super(ApiTypeError, self).__init__(full_msg)


class ApiValueError(OpenApiException, ValueError):
    def __init__(self, msg, path_to_item=None):
        """
        :param msg: The exception message.
        :type msg: str

        :param path_to_item: The path to the exception in the received_data
            dict. None if unset.
        :type path_to_item: list
        """
        self.path_to_item = path_to_item
        full_msg = msg
        if path_to_item:
            full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
        super(ApiValueError, self).__init__(full_msg)


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

        :param msg: The exception message.
        :type msg: str

        :param path_to_item: The path to the exception in the received_data
            dict. None if unset.
        :type path_to_item: list
        """
        self.path_to_item = path_to_item
        full_msg = msg
        if path_to_item:
            full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
        super(ApiAttributeError, self).__init__(full_msg)


class ApiKeyError(OpenApiException, KeyError):
    def __init__(self, msg, path_to_item=None):
        """
        :param msg: The exception message.
        :type msg: str

        :param path_to_item: The path to the exception in the received_data
            dict. None if unset.
        :type path_to_item: list
        """
        self.path_to_item = path_to_item
        full_msg = msg
        if path_to_item:
            full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
        super(ApiKeyError, self).__init__(full_msg)


class ApiException(OpenApiException):
    def __init__(self, status=None, reason=None, http_resp=None):
        if http_resp:
            self.status = http_resp.status
            self.reason = http_resp.reason
            try:
                self.body = json.loads(http_resp.data)
            except Exception:
                self.body = http_resp.data.decode("utf-8")
            self.headers = http_resp.getheaders()
        else:
            self.status = status
            self.reason = reason
            self.body = None
            self.headers = None

    def __str__(self):
        """Custom error messages for exception"""
        error_message = "({0})\n" "Reason: {1}\n".format(self.status, self.reason)
        if self.headers:
            error_message += "HTTP response headers: {0}\n".format(self.headers)

        if self.body:
            error_message += "HTTP response body: {0}\n".format(self.body)

        return error_message


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


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


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


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


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 += "[{0}]".format(pth)
        else:
            result += "['{0}']".format(pth)
    return result
