# coding=utf-8
import json
from logging import Logger

from helper import json_serialize
from requests import Response
from .._TranslatedMessage import TranslatedMessage


class ConfigurationReaderException(Exception):
    pass


class NotFound(Exception):
    pass


class ValidationError(Exception):
    pass


class ResponseError(Exception):
    def __init__(self, logger, response):
        # type: (Logger, Response) -> None

        self.logger = logger
        self.response = response
        self.error_status_code = response.status_code
        self.message = _get_response_content(response)
        response_str = json_serialize(response)

        message = "Unexpected response received. StatusCode: {}; Content: {}; FullResponse:\n{}"
        self.logger.error(message.format(self.error_status_code, self.message, response_str))

        super(ResponseError, self).__init__(self.message)


class NoResponseError(Exception):
    def __init__(self, logger, response):
        self.logger = logger
        self.response = response

        error_status_code = response.status_code
        content = _get_response_content(response)
        message = "No response. StatusCode: {}; Content: {}; FullResponse: {}"
        response_str = json_serialize(response)
        self.logger.error(message.format(error_status_code, content, response_str))
        super(NoResponseError, self).__init__(content)


class ForbiddenError(Exception):
    def __init__(self, logger, response):
        self.logger = logger
        self.response = response

        error_status_code = response.status_code
        content = _get_response_content(response)
        message = "Forbidden error. StatusCode: {}; Content: {}; FullResponse: {}"
        response_str = json_serialize(response)
        self.logger.error(message.format(error_status_code, content, response_str))
        super(ForbiddenError, self).__init__(content)


class NotFoundError(Exception):
    def __init__(self, logger, response):
        self.logger = logger
        self.response = response

        error_status_code = response.status_code
        content = _get_response_content(response)
        message = "Not found. StatusCode: {}; Content: {}; FullResponse: {}"
        response_str = json_serialize(response)
        self.logger.error(message.format(error_status_code, content, response_str))
        super(NotFoundError, self).__init__(content)


class NotAllowedError(Exception):
    def __init__(self, logger, response):
        self.logger = logger
        self.response = response

        error_status_code = response.status_code
        content = _get_response_content(response)
        message = "Method not allowed. StatusCode: {}; Content: {}; FullResponse: {}"
        response_str = json_serialize(response)
        self.logger.error(message.format(error_status_code, content, response_str))
        super(NotAllowedError, self).__init__(content)


class NotAvailableError(Exception):
    def __init__(self, logger, response):
        self.logger = logger
        self.response = response

        error_status_code = response.status_code
        content = _get_response_content(response)
        message = "Code not available. StatusCode: {}; Content: {}; FullResponse: {}"
        response_str = json_serialize(response)
        self.logger.error(message.format(error_status_code, content, response_str))
        super(NotAvailableError, self).__init__(content)


class InUseError(Exception):
    def __init__(self, logger, response):
        self.logger = logger
        self.response = response

        error_status_code = response.status_code
        content = _get_response_content(response)
        message = "Code in use on another terminal. StatusCode: {}; Content: {}; FullResponse: {}"
        response_str = json_serialize(response)
        self.logger.error(message.format(error_status_code, content, response_str))
        super(InUseError, self).__init__(content)


class AlreadyUnlockedError(Exception):
    def __init__(self, logger, response):
        self.logger = logger
        self.response = response

        error_status_code = response.status_code
        content = _get_response_content(response)
        message = "Code already unlocked. StatusCode: {}; Content: {}; FullResponse: {}"
        response_str = json_serialize(response)
        self.logger.error(message.format(error_status_code, content, response_str))
        super(AlreadyUnlockedError, self).__init__(content)


class ProcessorError(Exception):
    def __init__(self, logger, response):
        self.logger = logger
        self.response = response

        error_status_code = response.status_code
        content = _get_response_content(response)
        message = "Processing error. StatusCode: {}; Content: {}; FullResponse: {}"
        response_str = json_serialize(response)
        self.logger.error(message.format(error_status_code, content, response_str))
        super(ProcessorError, self).__init__(content)

    def __str__(self):
        return "ProcessorError. StatusCode: {}; Content: {}".format(
            self.response.status_code,
            self.response.content
        )

    def __repr__(self):
        return self.__str__()


class ClientCustomError(Exception):
    def __init__(
        self,
        message,   # type: TranslatedMessage
    ):
        # type: (...) -> None

        self.message = message


class GoneError(Exception):
    def __init__(self, *args, **kwargs):
        super(Exception, self).__init__()


class OrderHasNotBenefitListException(Exception):
    def __init__(
        self,       # type: OrderHasNotBenefitListException
        order_id,   # type: str
    ):
        # type: (...) -> None

        self.order_id = order_id

        super(Exception, self).__init__()

    def __str__(
        self,   # type: OrderHasNotBenefitListException
    ):
        # type: (...) -> str

        return "Order {} has not Benefit List".format(self.order_id)


class BenefitIdNotFoundInAppliedOrderBenefitsException(Exception):
    def __init__(
        self,           # type: BenefitIdNotFoundInAppliedOrderBenefitsException
        order_id,       # type: str
        benefit_id,     # type: str
    ):
        # type: (...) -> None

        self.order_id = order_id
        self.benefit_id = benefit_id

        super(Exception, self).__init__()

    def __str__(
        self,   # type: BenefitIdNotFoundInAppliedOrderBenefitsException
    ):
        # type: (...) -> str

        return "Benefit ID: {} not found in applied benefits for Order {}".format(self.benefit_id, self.order_id)


def _get_response_content(response):
    try:
        content = json.loads(response.content).get("detail").encode("utf8")
        return content
    except (AttributeError, ValueError):
        return ""


def build_message_exception(ex):
    if hasattr(ex, "response") and ex.response is not None:
        text = ex.response.text
        status_code = ex.response.status_code
        error_message = _get_error_message(
            status_code=status_code,
            text=text,
        )
        error_detail = {
            "type": "ClientError",
            "status_code": status_code,
            "message": error_message or None
        }
    else:
        error_type = type(ex).__name__
        error_message = ex.message
        error_detail = {"type": error_type, "message": error_message or None}

    ex.message = json.dumps(error_detail)
    return ex


def _get_error_message(
    status_code,    # type: int
    text,           # type: str
):
    # type: (...) -> str

    if status_code == 404:
        return "ERROR_COUPON_NOT_FOUND"
    if status_code == 406:
        return json.loads(text) if text not in (None, "None", "") else ""
    if status_code == 409:
        return "ERROR_COUPON_LOCKED_BY_ANOTHER_REQUEST"
    if status_code == 410:
        return "ERROR_COUPON_ALREADY_IN_USE"
    return ""
