from application.interactor import (
    BenefitRetryInteractor,
    BurnBenefitInteractor,
    GetBenefitInfoInteractor,
    UnlockBenefitInteractor,
    OrderModifiedPaidInteractor,
    OrderModifiedVoidInteractor,
    OrderModifiedVoidLineInteractor,
    TestConnectionInteractor
)
from application.interactor.apply.apply_benefit import ApplyBenefitInteractor
from application.interactor.apply.apply_coupon import ApplyCouponInteractor
from application.interactor.apply.service import BenefitApplierService
from application.manager import BenefitRetryManager
from application.model import (
    ListenedTokens,
    ListenedEvents,
    BenefitAppliers,
)
from application.model.configuration import Configurations
from application.model.repository.benefit_applier import DefaultBenefitApplierRepository
from application.model.repository.benefit_applier.exception import (
    CouponCustomError,
    CouponNotFound,
    CouponAlreadyUsed,
    CouponLocked,
    BenefitApplierNotFound,
)
from application.processor import (
    GetBenefitInfoProcessor,
    OrderModifiedPaidProcessor,
    OrderModifiedVoidProcessor,
    OrderModifiedVoidLineProcessor,
    NotMappedProcessor,
    TestConnectionProcessor
)
from application.processor.apply.apply_benefit._ApplyBenefitProcessor import ApplyBenefitProcessor
from application.processor.apply.apply_coupon import ApplyCouponProcessor
from application.repository.pos import DBRepository
from messagebus import Message, Event
from messagehandler import MessageHandlerBuilder
from messageprocessor import (
    MessageProcessorMessageHandler,
    DefaultMessageProcessorExecutorFactory,
)
from messageprocessorutil import LoggingProcessorCallback
from typing import Union, Dict

from application.services import TableService


class ComponentMessageHandlerBuilder(MessageHandlerBuilder):
    def __init__(
        self,                               # type: ComponentMessageHandlerBuilder
        configs,                            # type: Configurations
        db_repository,                      # type: DBRepository
        benefit_appliers_repositories,      # type: Dict[BenefitAppliers: DefaultBenefitApplierRepository]
        table_service,                      # type: TableService
    ):
        # type: (...) -> None

        self.logger = configs.logger
        self.configs = configs
        self.db_repository = db_repository
        self.benefit_appliers_repositories = benefit_appliers_repositories
        self.table_service = table_service

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

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

        default_params = [
            self.configs,
            self.db_repository,
            self.benefit_appliers_repositories,
        ]

        benefit_applier_service = BenefitApplierService(
            benefit_appliers_repositories=self.benefit_appliers_repositories,
            benefit_applier_tender_ids=self.configs.benefit_applier_tender_ids,
            db_repository=self.db_repository,
        )
        burn_benefit_interactor = BurnBenefitInteractor(
            configs=self.configs,
            db_repository=self.db_repository,
            benefit_appliers_repositories=self.benefit_appliers_repositories,
            table_service=self.table_service,
        )
        get_benefit_info_interactor = GetBenefitInfoInteractor(*default_params)
        apply_coupon_interactor = ApplyCouponInteractor(
            benefit_applier_service=benefit_applier_service
        )
        apply_benefit_interactor = ApplyBenefitInteractor(
            benefit_applier_service=benefit_applier_service,
        )
        unlock_benefit_interactor = UnlockBenefitInteractor(
            configs=self.configs,
            db_repository=self.db_repository,
            benefit_appliers_repositories=self.benefit_appliers_repositories,
            table_service=self.table_service,
        )
        test_connection_interactor = TestConnectionInteractor(self.configs)

        order_modified_paid_interactor = OrderModifiedPaidInteractor(burn_benefit_interactor, *default_params)
        order_modified_void_interactor = OrderModifiedVoidInteractor(
            unlock_benefit_interactor=unlock_benefit_interactor,
            configs=self.configs,
            db_repository=self.db_repository,
            benefit_appliers_repositories=self.benefit_appliers_repositories,
            table_service=self.table_service,
        )
        order_modified_void_line_interactor = OrderModifiedVoidLineInteractor(
            unlock_benefit_interactor,
            *default_params
        )

        get_benefit_info_processor = GetBenefitInfoProcessor(get_benefit_info_interactor)
        apply_coupon_processor = ApplyCouponProcessor(interactor=apply_coupon_interactor)
        apply_benefit_processor = ApplyBenefitProcessor(interactor=apply_benefit_interactor)

        order_modified_paid_processor = OrderModifiedPaidProcessor(order_modified_paid_interactor)
        order_modified_void_processor = OrderModifiedVoidProcessor(order_modified_void_interactor)
        order_modified_void_line_processor = OrderModifiedVoidLineProcessor(order_modified_void_line_interactor)
        not_mapped_processor = NotMappedProcessor()

        test_connection_processor = TestConnectionProcessor(test_connection_interactor)

        messages_processors = {
            ListenedTokens.TK_NCOUPON_APPLY_COUPON.value: apply_coupon_processor,
            ListenedTokens.TK_NCOUPON_APPLY_BENEFIT.value: apply_benefit_processor,
            ListenedTokens.TK_NCOUPON_GET_COUPON_INFO.value: get_benefit_info_processor,
            ListenedTokens.TK_NCOUPON_TEST_CONNECTION.value: test_connection_processor
        }

        events_processors = {
            ListenedEvents.ORDER_MODIFIED_PAID.value: order_modified_paid_processor,
            ListenedEvents.ORDER_MODIFIED_VOID.value: order_modified_void_processor,
            ListenedEvents.ORDER_MODIFIED_VOID_LINE.value: order_modified_void_line_processor,
            ListenedEvents.ORDER_MODIFIED.value: not_mapped_processor
        }

        callbacks = [
            LoggingProcessorCallback(
                logger=self.logger,
                ignored_exceptions=[
                    CouponCustomError,
                    CouponNotFound,
                    CouponAlreadyUsed,
                    CouponLocked,
                    BenefitApplierNotFound,
                ]
            )
        ]

        self.message_handler = MessageProcessorMessageHandler(
            event_processors=events_processors,
            message_processors=messages_processors,
            executor_factory=DefaultMessageProcessorExecutorFactory(callbacks),
        )

        self.benefit_retry_manager = self._build_benefits_retry_manager(
            burn_benefit_interactor=burn_benefit_interactor,
            unlock_benefit_interactor=unlock_benefit_interactor,
        )
        self.benefit_retry_manager.start()

    def build_message_handler(self, message, event):
        # type: (Message, Event) -> None

        return self.message_handler

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

        self.benefit_retry_manager.stop()

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

        return

    def _build_benefits_retry_manager(self, burn_benefit_interactor, unlock_benefit_interactor):
        # type: (BurnBenefitInteractor, UnlockBenefitInteractor) -> BenefitRetryManager

        interactor = BenefitRetryInteractor(
            self.configs,
            self.db_repository,
            burn_benefit_interactor,
            unlock_benefit_interactor
        )
        return BenefitRetryManager(self.configs, interactor)
