from application.interactor import (
    AddBenefitToApplyInteractor,
    GetBenefitsInteractor,
    RemoveBenefitInteractor,
    OnOrderReopenInteractor,
    OnSaleItemInteractor,
    OnOrderTotalInteractor,
    OnTableOpenInteractor,
    OnTabOpenInteractor
)
from application.model import ListenedTokens, Configurations
from application.model.exceptions import BenefitExpiredOrInactiveException, CustomBenefitAlreadyAppliedException, \
    InsufficientSaleAmountException, ExcessiveSaleAmountException, MissingItemsException, PodTypeNotValidException, \
    UserLevelNotAllowedException, InvalidServiceTypeException, InvalidSaleTypeException, InvalidSectorException, \
    InvalidMinSeatsException
from application.processor import (
    AddBenefitProcessor,
    GetBenefitsProcessor,
    RemoveBenefitProcessor,
    OnOrderReopenProcessor,
    OnSaleItemProcessor,
    OnOrderTotalProcessor,
    OnTableOpenProcessor,
    OnTabOpenProcessor
)
from application.repository import NDiscountRepository
from application.service import OrderService, BenefitService
from messagebus import MessageBus, Message, Event
from messagehandler import MessageHandlerBuilder, MessageHandler
from messageprocessor import MessageProcessorMessageHandler, DefaultMessageProcessorExecutorFactory
from messageprocessorutil import LoggingProcessorCallback
from typing import Union, Optional


class ComponentMessageHandlerBuilder(MessageHandlerBuilder):
    def __init__(self, configs, repository, message_bus):
        # type: (Configurations, NDiscountRepository, MessageBus) -> None

        self.configs = configs
        self.logger = configs.logger
        self.repository = repository
        self.message_bus = message_bus

        self.message_handler = None  # type: Union[None, MessageProcessorMessageHandler]

    def build_singletons(self):
        # type: () -> None

        order_service = OrderService(self.configs, self.repository, self.message_bus)
        benefit_service = BenefitService(self.configs, self.repository, self.message_bus, order_service)

        add_benefit_to_apply_interactor = AddBenefitToApplyInteractor(benefit_service)
        add_benefit_to_apply_processor = AddBenefitProcessor(
            self.configs,
            add_benefit_to_apply_interactor,
            order_service,
            benefit_service
        )

        get_benefits_interactor = GetBenefitsInteractor(self.configs, self.repository)
        get_benefits_processor = GetBenefitsProcessor(self.configs, get_benefits_interactor)

        remove_benefit_interactor = RemoveBenefitInteractor(self.configs, benefit_service)
        remove_benefit_processor = RemoveBenefitProcessor(self.configs, remove_benefit_interactor)

        on_sale_item_interactor = OnSaleItemInteractor(self.configs, benefit_service)
        on_sale_item_processor = OnSaleItemProcessor(self.configs, on_sale_item_interactor)

        on_order_total_interactor = OnOrderTotalInteractor(self.configs, benefit_service, order_service)
        on_order_total_processor = OnOrderTotalProcessor(self.configs, on_order_total_interactor)

        on_order_reopen_interactor = OnOrderReopenInteractor(self.configs, order_service, benefit_service)
        on_order_reopen_processor = OnOrderReopenProcessor(self.configs, on_order_reopen_interactor, order_service)

        on_table_open_interactor = OnTableOpenInteractor(self.configs, benefit_service)
        on_table_open_processor = OnTableOpenProcessor(self.configs, on_table_open_interactor)

        on_tab_open_interactor = OnTabOpenInteractor(
            configs=self.configs,
            benefit_service=benefit_service
        )
        on_tab_open_processor = OnTabOpenProcessor(
            configs=self.configs,
            interactor=on_tab_open_interactor
        )

        messages_processors = {
            ListenedTokens.TK_NDISCOUNT_ADD_BENEFIT.value: add_benefit_to_apply_processor,
            ListenedTokens.TK_NDISCOUNT_GET_BENEFITS.value: get_benefits_processor,
            ListenedTokens.TK_NDISCOUNT_REMOVE.value: remove_benefit_processor,
            ListenedTokens.TK_NDISCOUNT_ON_ITEM_SALE.value: on_sale_item_processor,
            ListenedTokens.TK_NDISCOUNT_ON_ORDER_TOTAL.value: on_order_total_processor,
            ListenedTokens.TK_NDISCOUNT_ON_ORDER_REOPEN.value: on_order_reopen_processor,
            ListenedTokens.TK_NDISCOUNT_ON_TABLE_REOPEN.value: on_order_reopen_processor,
            ListenedTokens.TK_NDISCOUNT_ON_TABLE_OPEN.value: on_table_open_processor,
            ListenedTokens.TK_NDISCOUNT_ON_TAB_OPEN.value: on_tab_open_processor
        }

        log_ignored_exceptions = [
            BenefitExpiredOrInactiveException, CustomBenefitAlreadyAppliedException, InsufficientSaleAmountException,
            ExcessiveSaleAmountException, MissingItemsException, PodTypeNotValidException, UserLevelNotAllowedException,
            InvalidServiceTypeException, InvalidSaleTypeException, InvalidSectorException, InvalidMinSeatsException
        ]
        callbacks = [LoggingProcessorCallback(self.logger, log_ignored_exceptions)]

        self.message_handler = MessageProcessorMessageHandler(
            {},
            messages_processors,
            DefaultMessageProcessorExecutorFactory(callbacks)
        )

    def build_message_handler(self, message, event):
        # type: (Optional[Message], Optional[Event]) -> MessageHandler

        return self.message_handler

    def destroy_singletons(self):
        # type: () -> None

        return

    def destroy_message_handler(self, message_handler):
        # type: (MessageHandler) -> None

        return
