# -*- coding: utf-8 -*-
from cache_functions import UsersInfoCache
from logging import Logger

from messageprocessorutil import LoggingProcessorCallback
from messagebus import TokenCreator, TokenPriority, MessageBus
from messagehandler import MessageHandlerBuilder
from messageprocessor import (
    DefaultMessageProcessorExecutorFactory,
    MessageProcessorMessageHandler,
)
from production.box import ViewBox

from production.manager import ProductionManager, PicklistCleanerManager
from production.interactor import DeviceStatusChangedInteractor
from production.processor import (
    OrderModifiedProcessor,
    GetOrderMaxPrepTimeProcessor,
    GetOrderXmlProcessor,
    OrderModifiedIgnoredTypesProcessor,
    StatisticUpdateProcessor,
    PurgeOrdersProcessor,
    GetPrintersInfoProcessor,
    PickListOrdersProcessor,
    DeviceStatusChangedProcessor,
    GetCoursesProcessor,
)
from production.processor.command import SetOrderStateProcessor, ToggleTagLineProcessor, UndoProcessor, \
    RefreshViewProcessor, EnablePathProcessor, DisablePathProcessor
from messagehandlerbuilder.token import ListenedEvents
from production.repository import ProductRepository, ProductionRepository
from production.treebuilder import TreeBuilder
from production.treebuilder.boxfactory import ProductionBoxFactory
from production.treebuilder.viewfactory import ViewFactory
from production.util import ProductionConfig
from typing import List, Dict, Union

production_group = "C"
TK_PROD_REFRESH_VIEW = TokenCreator.create_token(TokenPriority.low, production_group, "1")
TK_PROD_SET_ORDER_STATE = TokenCreator.create_token(TokenPriority.low, production_group, "2")
TK_PROD_GET_ORDER_MAX_PREP_TIME = TokenCreator.create_token(TokenPriority.low, production_group, "3")
TK_PROD_GET_ORDER_XML = TokenCreator.create_token(TokenPriority.low, production_group, "4")  # 12582916
TK_PROD_UNDO = TokenCreator.create_token(TokenPriority.low, production_group, "7")
TK_PROD_TOGGLE_TAG_LINE = TokenCreator.create_token(TokenPriority.low, production_group, "8")

TK_PROD_DISABLE_PATH = TokenCreator.create_token(TokenPriority.low, production_group, "101")
TK_PROD_ENABLE_PATH = TokenCreator.create_token(TokenPriority.low, production_group, "100")
TK_PROD_GET_STATISTICS = TokenCreator.create_token(TokenPriority.low, production_group, "102")
TK_PROD_PURGE = TokenCreator.create_token(TokenPriority.low, production_group, "103")

TK_PROD_GET_PRINTERS_INFO = TokenCreator.create_token(TokenPriority.low, production_group, "201")
TK_PROD_GET_COURSES = TokenCreator.create_token(TokenPriority.low, production_group, "202")

TK_PROD_ORDER_PICKLIST = TokenCreator.create_token(TokenPriority.low, production_group, "301")  # 12583681

TK_SALE_ORDER_MODIFIED = TokenCreator.create_token(TokenPriority.low, "A", "32")


class ProductionMessageHandlerBuilder(MessageHandlerBuilder):
    def __init__(
        self,                          # type: ProductionMessageHandlerBuilder
        message_bus,                   # type: MessageBus
        purge_interval,                # type: int
        orders_life_time,              # type: int
        product_repository,            # type: ProductRepository
        production_repository,         # type: ProductionRepository
        manager_log_level,             # type: str
        audit_logger_prod_states,      # type: List
        kds_manager,                   # type: str
        order_modified_by_message,     # type: bool
        production_config,             # type: ProductionConfig
        loggers,                       # type: Dict[unicode, Logger]
        timeout_to_cleanup_devices,    # type: int
    ):
        # type: (...) -> None

        self.message_bus = message_bus
        self.purge_interval = purge_interval
        self.orders_life_time = orders_life_time
        self.product_repository = product_repository
        self.production_repository = production_repository
        self.manager_log_level = manager_log_level
        self.audit_logger_prod_states = audit_logger_prod_states
        self.production_config = production_config
        self.boxes_config = production_config.boxes_config
        self.branches_config = production_config.branches_config
        self.views_config = production_config.views_config
        self.clean_picklist_database_service_config = production_config.picklist_database_manager_config
        self.kds_manager = kds_manager
        self.order_modified_by_message = order_modified_by_message
        self.loggers = loggers
        self.timeout_to_cleanup_devices = timeout_to_cleanup_devices

        self.production_manager = None  # type: Union[None, ProductionManager]
        self.message_handler = None
        self.user_cache_thread = None
        self.clean_picklist_database_service = None

    def build_singletons(self):
        if self.manager_log_level in self.loggers:
            manager_logger = self.loggers[self.manager_log_level]
        elif "ERROR" in self.loggers:
            manager_logger = self.loggers["ERROR"]
        elif len(self.loggers) > 0:
            manager_logger = self.loggers[self.loggers.keys()[0]]
        else:
            manager_logger = None

        self.production_manager = ProductionManager(
            self.production_repository,
            self.purge_interval,
            self.orders_life_time,
            self.message_bus,
            self.audit_logger_prod_states,
            self.kds_manager,
            manager_logger
        )

        production_box_factory = ProductionBoxFactory(
            self.production_manager,
            self.production_manager,
            self.production_manager,
            self.production_manager,
            self.product_repository,
            self.loggers
        )

        view_factory = ViewFactory(
            message_bus=self.message_bus,
            loggers=self.loggers,
            production_manager=self.production_manager,
            kds_manager=self.kds_manager,
            timeout_to_cleanup_devices=self.timeout_to_cleanup_devices,
        )
        tree_builder = TreeBuilder(production_box_factory, view_factory)
        trees = tree_builder.build_trees(
            self.boxes_config,
            self.branches_config,
            self.views_config
        )

        root_boxes = []
        view_boxes = []
        for tree in trees:
            root_boxes.append(tree.root)
            leaves = [t for t in tree.leaves if isinstance(t, ViewBox)]
            view_boxes.extend(leaves)

        self.production_manager.set_root_boxes(root_boxes)
        self.production_manager.set_views(view_boxes)
        self.production_manager.initialize()

        self.user_cache_thread = UsersInfoCache(self.message_bus, manager_logger)
        self.user_cache_thread.daemon = True
        self.user_cache_thread.start()

        self.clean_picklist_database_service = PicklistCleanerManager(
            production_repository=self.production_repository,
            config=self.clean_picklist_database_service_config,
        )
        self.clean_picklist_database_service.start()

        order_modified_processor = OrderModifiedProcessor(
            self.production_manager,
            self.product_repository,
            self.user_cache_thread,
            self.order_modified_by_message
        )

        device_status_interactor = DeviceStatusChangedInteractor(production_manager=self.production_manager)
        device_status_changed_processor = DeviceStatusChangedProcessor(
            device_status_interactor=device_status_interactor,
        )

        event_processors = {
            ListenedEvents.DEVICE_STATUS_CHANGED: device_status_changed_processor,
        }
        if not self.order_modified_by_message:
            order_modified_ignored_types_processor = OrderModifiedIgnoredTypesProcessor()
            event_processors.update({
                ListenedEvents.ORDER_MODIFIED: order_modified_processor,
                ListenedEvents.ORDER_MODIFIED_DO_TENDER: order_modified_ignored_types_processor,
                ListenedEvents.ORDER_MODIFIED_DISCOUNT_APPLIED: order_modified_ignored_types_processor,
                ListenedEvents.ORDER_MODIFIED_DISCOUNT_REMOVED: order_modified_ignored_types_processor,
                ListenedEvents.ORDER_MODIFIED_CLEAR_TENDERS: order_modified_ignored_types_processor,
                ListenedEvents.ORDER_MODIFIED_RESET_CURRENT_ORDER: order_modified_ignored_types_processor,
            })

        max_prep_time_processor = GetOrderMaxPrepTimeProcessor(self.production_manager, self.product_repository)
        message_processors = {
            TK_PROD_SET_ORDER_STATE: SetOrderStateProcessor(view_boxes),
            TK_PROD_TOGGLE_TAG_LINE: ToggleTagLineProcessor(view_boxes, self.loggers),
            TK_PROD_UNDO: UndoProcessor(view_boxes),
            TK_PROD_REFRESH_VIEW: RefreshViewProcessor(view_boxes),
            TK_PROD_ENABLE_PATH: EnablePathProcessor(self.production_manager),
            TK_PROD_DISABLE_PATH: DisablePathProcessor(self.production_manager),
            TK_PROD_GET_ORDER_MAX_PREP_TIME: max_prep_time_processor,
            TK_PROD_GET_ORDER_XML: GetOrderXmlProcessor(self.production_manager),
            TK_PROD_GET_STATISTICS: StatisticUpdateProcessor(self.production_manager),
            TK_PROD_PURGE: PurgeOrdersProcessor(self.production_manager),
            TK_PROD_GET_PRINTERS_INFO: GetPrintersInfoProcessor(view_boxes),
            TK_PROD_GET_COURSES: GetCoursesProcessor(self.product_repository),
            TK_PROD_ORDER_PICKLIST: PickListOrdersProcessor(
                message_bus=self.message_bus,
                production_repository=self.production_repository,
            )
        }

        if not self.order_modified_by_message:
            order_modified_processor = OrderModifiedIgnoredTypesProcessor(True)
        message_processors[TK_SALE_ORDER_MODIFIED] = order_modified_processor

        callbacks = [LoggingProcessorCallback(manager_logger)]

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

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

        pass

    def build_message_handler(self, message, event):
        return self.message_handler

    def destroy_singletons(self):
        self.production_manager.stop()
        self.user_cache_thread.stop()
        self.production_repository.stop()
        self.clean_picklist_database_service.stop()
