import sys

if sys.version_info[0] < 3:
    import inspect2 as inspect
else:
    import inspect

import json
from typing import Dict, Union, Tuple

from messagebus import HttpResponseMessage
from ._util import convert_to_json


class ExceptionFormatter(object):
    def __init__(self, exception_map):
        # type: (Dict[type, Union[int, Tuple[int, str]]]) -> None
        self.exception_map = exception_map

    def format(self, message_bus, message, exception, default_status_code):
        try:
            map_info = self.exception_map[type(exception)]
            if isinstance(map_info, int):
                exception_cls = type(exception)
                body = b""
                headers = {}
                if self.has_own_init(exception_cls):
                    body = convert_to_json(exception).encode("utf-8")
                    headers["Content-Type"] = "application/json; charset=UTF-8"

                message_bus.reply_message(message, HttpResponseMessage(map_info, body, headers))
            else:
                status_code = map_info[0]
                body = map_info[1]
                if isinstance(body, dict):
                    message_bus.reply_message(message,
                                              HttpResponseMessage(
                                                  status_code,
                                                  json.dumps(body).encode("utf-8"),
                                                  {"Content-Type": "application/json; charset=UTF-8"}))
                else:
                    message_bus.reply_message(message, HttpResponseMessage(status_code, body))

        except TypeError:
            self._create_response_when_exception_map_is_none(message_bus, message, default_status_code)
        except KeyError:
            self._create_response_when_exception_map_does_not_have_exception(message_bus,
                                                                             message,
                                                                             exception,
                                                                             default_status_code)

    @staticmethod
    def _create_response_when_exception_map_is_none(message_bus, message, default_status_code):
        message_bus.reply_message(message, HttpResponseMessage(default_status_code))

    @staticmethod
    def _create_response_when_exception_map_does_not_have_exception(message_bus,
                                                                    message,
                                                                    exception,
                                                                    default_status_code):
        cls = type(exception)
        status_code = default_status_code
        body = b""

        if hasattr(cls, "_cls_attrs"):
            status_code = cls._cls_attrs.get("status_code", status_code)
            body = cls._cls_attrs.get("body", b"")

        if isinstance(body, dict):
            message_bus.reply_message(message,
                                      HttpResponseMessage(
                                          status_code,
                                          json.dumps(body).encode("utf-8"),
                                          {"Content-Type": "application/json; charset=UTF-8"}))
        else:
            message_bus.reply_message(message, HttpResponseMessage(status_code, body))

    @staticmethod
    def has_own_init(cls):
        mro = inspect.getmro(cls)
        for i in range(1, len(mro)):
            if mro[i].__init__ == cls.__init__:
                return False
        return True
