import logging
import xml.sax

from cache_functions import UsersInfoCache
from xml.etree import cElementTree as eTree

from typing import List

from messageprocessorutil import UuidMessageProcessor
from messagebus import NakMessage, AckMessage
from production.repository import ProductRepository
from production.manager import ProductionManager
from production.model import ProductionOrder, Item

from ._SystemOrderParser import SystemOrderParser

performance_event_logger = logging.getLogger("ProductionPerformanceEvent")


class OrderModifiedProcessor(UuidMessageProcessor):
    def __init__(self, production_manager, product_repository, user_cache_thread=None):
        # type: (ProductionManager, ProductRepository, UsersInfoCache) -> None
        super(OrderModifiedProcessor, self).__init__()
        self.production_manager = production_manager
        self.part_code_jit_lines = product_repository.get_all_part_code_jit_lines()
        self.user_cache_thread = user_cache_thread

        self.product_custom_params = product_repository.get_products_with_custom_params(["KitchenLabels"])

    def parse_data(self, data, event_type):
        if not self._performance_log(data):
            return None

        handler = SystemOrderParser(
            part_code_jit_lines=self.part_code_jit_lines,
            product_custom_params=self.product_custom_params,
            user_cache_thread=self.user_cache_thread,
        )
        xml.sax.parseString(data, handler)
        if handler.order.order_id == 0:
            return None

        self._order_items(handler.order.items)
        return handler.order

    @staticmethod
    def _performance_log(
        data,   # type: str
    ):
        # type (...) -> None

        try:
            data_xml = eTree.XML(data)
            order_id = (data_xml.find(".//Order") or {}).get("orderId", "")
            order_state = (data_xml.find(".//Order") or {}).get("state", "")
            subject = data_xml.get("subject") or ""
            event_type = data_xml.get("type") or ""
            status = "IGNORED" if order_state in ["", "IN_PROGRESS"] and event_type != "RECALLED" else "PROCESS"
            message = "Event - {:10} / {:10} / {:20} / {}".format(status, order_id, subject, event_type)
            performance_event_logger.info(message)
            if status == "PROCESS":
                return True

        except Exception as _:  # noqa
            performance_event_logger.exception("Error parsing event xml")

        return False

    def _order_items(self, items):
        # type: (List[Item]) -> None

        for item in items:
            item.items = sorted(item.items, key=lambda i: (i.sequence, i.part_code))
            for son in item.items:
                if len(son.items) > 0:
                    son.items = sorted(son.items, key=lambda i: (i.sequence, i.part_code))
                    self._order_items(son.items)

    def call_business(self, order):
        # type: (ProductionOrder) -> None
        if order is not None:
            self.production_manager.order_modified(order)

    def format_response(self, message_bus, message, event, input_obj, result):
        message_bus.reply_event(event, AckMessage())

    def format_exception(self, message_bus, message, event, input_obj, exception):
        message_bus.reply_event(event, NakMessage())

    def format_parse_exception(self, message_bus, message, event, data, exception):
        self.format_exception(message_bus, message, event, data, exception)
