import json
from logging import Logger

from msgbus import TK_SYS_ACK
from posot import OrderTaker

from interactor.route.loyalty.search_customer_info.dto import SearchCustomerInfoResponse
from mbcontextmessagehandler import MbContextMessageBus
from messagebus import (
    Message,
    DataType,
)
from model.error_type import NotFoundErrorsType
from model.exception import NotFoundError
from model.loyalty import CustomerInfo, Benefit, BenefitInfo

from model.services import LoyaltyServiceModel
from model.config import Config
from model.tokens import BusTokens
from typing import Dict


class LoyaltyService(LoyaltyServiceModel):

    def __init__(
        self,
        message_bus,    # type: MbContextMessageBus
        logger,         # type: Logger
        config,         # type: Config
    ):
        # type: (...) -> None

        self.message_bus = message_bus
        self.logger = logger
        self.config = config

    def get_customer_info(
        self,
        pos_ot,         # type: OrderTaker
        customer_id,    # type: str
    ):
        # type: (...) -> SearchCustomerInfoResponse

        message = Message(
            token=BusTokens.TK_LOYALTY_GET_LOYALTY_ID,
            data_type=DataType.param,
            data=customer_id,
        )

        response = self.message_bus.send_message(
           component="Loyalty",
           message=message,
        )

        if response.token != TK_SYS_ACK or not response.data:
            self.logger.info("CustomerId: {}, not found".format(customer_id))
            raise NotFoundError(
                ex=NotFoundErrorsType.CustomerNotFound,
            )

        return self._process_response(
            pos_ot=pos_ot,
            response=response,
        )

    def _process_response(
        self,
        pos_ot,     # type: OrderTaker
        response,   # type: Message
    ):
        # type: (...) -> SearchCustomerInfoResponse

        try:
            loyalty = json.loads(
                response.data,
            )

            if loyalty in [None, ""]:
                self.logger.exception("Empty customer info")
                raise NotFoundError(
                    ex=NotFoundErrorsType.CustomerNotFound,
                )

            self._save_custom_properties(
                pos_ot=pos_ot,
                loyalty=loyalty,
            )

            return self._get_response(
                loyalty=loyalty,
            )
        except NotFoundError as ex:
            raise ex
        except Exception as _:
            self.logger.exception("Error process response loyalty customer info")
            raise NotFoundError(
                ex=NotFoundErrorsType.CustomerNotFound,
            )

    @staticmethod
    def _save_custom_properties(
        pos_ot,       # type: OrderTaker
        loyalty,      # type: Dict
    ):
        properties = {"LOYALTY_CUSTOMER_INFO": loyalty}

        customer_info = loyalty.get("customerInfo")

        if customer_info is not None:
            customer_data = customer_info.get("customer")

            if customer_data is not None:
                customer_document = customer_data.get("document", None)
                properties.update({"CUSTOMER_DOC": customer_document})

                customer_name = customer_data.get("name", None)
                properties.update({"CUSTOMER_NAME": customer_name})

            for prop in properties.keys():
                if properties[prop] in [None, ""]:
                    del properties[prop]

        pos_ot.setOrderCustomProperties(
           properties=properties,
        )

    @staticmethod
    def _get_response(
        loyalty,      # type: Dict
    ):
        # type: (...) -> SearchCustomerInfoResponse

        customer_info = CustomerInfo(
            loyalty=loyalty,
        )

        benefits = loyalty.get("benefits", [])
        list_benefit = []
        for benefit in benefits:
            current_benefit = Benefit(
               loyalty=benefit,
            )

            notify = benefit.get("notify", False)
            benefit_info = BenefitInfo(
                benefit=current_benefit,
                notify=notify,
            )
            list_benefit.append(benefit_info)

        response = SearchCustomerInfoResponse(
            customer_info=customer_info,
            benefits=list_benefit,
        )

        return response
