# -*- coding: utf-8 -*-

import json

import sysactions
from application.model.benefit import BenefitList
from application.model.exceptions import OrderNotFoundException, OrderPictureException

from application.model.order import Order
from typing import List, Optional, Union, Dict
from xml.etree import cElementTree as eTree
from xml.etree.ElementTree import Element

from messagebus import MessageBus, Message, DataType, DefaultToken
from msgbus import TK_SLCTRL_OMGR_ORDERPICT
from posot import OrderTaker, OrderTakerException
from sysactions import get_model

from application.model import Configurations, NDiscountModelJsonEncoder
from application.repository import NDiscountRepository


class OrderService(object):
    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

    @staticmethod
    def get_burnt_items(applied_benefits):
        # type: (eTree) -> Dict

        burnt_items = {}
        for benefit in applied_benefits:
            current_burnt_items = benefit["burnt_items"]
            for key in current_burnt_items.keys():
                if key in burnt_items:
                    burnt_items[key] += current_burnt_items[key]
                else:
                    burnt_items[key] = current_burnt_items[key]

        return burnt_items

    def clean_order_discount_properties(self, pos_ot):
        # type: (OrderTaker) -> None

        self.logger.info("Clean order discount properties")
        pos_ot.setOrderCustomProperties({
            "DISCOUNT_APPLY_DATETIME": None,
            "DISCOUNT_APPLY_OPERATOR": None,
            "DISCOUNT_APPLY_AUTHORIZER": None,
            "DISCOUNT_APPLY_METHOD": None
        })

    def get_applied_benefits_from_order_picture(self, pos_id, order_picture=None):
        # type: (int, Optional[eTree]) -> List

        benefit_list = self.get_benefit_list_from_order_picture(pos_id, order_picture)
        if not benefit_list:
            return []

        return json.loads(benefit_list.get("benefitList", "[]"))

    def get_benefit_list_from_order_picture(self, pos_id, order_picture=None):
        # type: (int, Optional[eTree]) -> Dict

        try:
            if order_picture is None:
                order_picture = self.get_order_picture(pos_id)

            value = order_picture.find(".//OrderProperty[@key=\"BENEFIT_LIST\"]").get("value")
            return json.loads(value)
        except OrderTakerException:
            pass
        except AttributeError:
            pass
        return {}

    @staticmethod
    def save_benefit_list_custom_property(pos_ot, benefit_list):
        # type: (OrderTaker, BenefitList) -> None

        benefit_list_json = json.dumps(benefit_list, encoding="utf-8", cls=NDiscountModelJsonEncoder)
        pos_ot.setOrderCustomProperty("BENEFIT_LIST", benefit_list_json)

    def get_order_picture(self, pos_id, order_id=None):
        # type: (Union[str, int], Optional[str]) -> Optional[Element]

        data = "{}\0{}\0{}".format(str(pos_id), str(order_id or ''), "true")
        message = Message(TK_SLCTRL_OMGR_ORDERPICT, DataType.param, data, 30000000)
        reply = self.message_bus.send_message("ORDERMGR{}".format(str(pos_id)), message)

        if reply.token == DefaultToken.TK_SYS_ACK:
            try:
                split_reply = reply.data.split("\0")
                if not split_reply[2]:
                    return None
                order_picture = eTree.XML(split_reply[2])
                if order_id:
                    order_picture = order_picture.findall('.//Order')[0]
                return order_picture
            except AttributeError:
                return None

        raise OrderPictureException("Error getting OrderPicture: {} - {}".format(reply.token, reply.data))

    def get_order(self, pos_id, model=None, create_order=False, order_picture=None, table_id=None):
        # type: (int, Element, bool, Element, str) -> Order

        if not order_picture:
            order_picture = self.get_order_picture(pos_id)

        state = order_picture.get("state", "") if order_picture else ""
        if state not in ["IN_PROGRESS", "TOTALED"]:
            if not create_order:
                raise OrderNotFoundException()

            order_picture = self._create_new_order(pos_id, table_id)

        sale_lines = order_picture.findall(".//SaleLine")

        return Order(order_picture, sale_lines, model)

    @staticmethod
    def void_order(pos_id, pos_ot):
        # type: (int, OrderTaker) -> None

        pos_ot.voidOrder(pos_id)

    @staticmethod
    def void_line(pos_id, pos_ot, lines_to_void):
        # type: (int, OrderTaker, List) -> None

        if len(lines_to_void) > 0:
            lines_to_void = "|".join([str(x) for x in lines_to_void])
            pos_ot.voidLine(pos_id, lines_to_void)

    @staticmethod
    def clear_discounts(pos_ot, used_discount_ids):
        # type: (OrderTaker, List) -> None

        for discount_id in used_discount_ids:
            pos_ot.clearDiscount(discount_id)

    @staticmethod
    def clear_tenders(pos_id, pos_ot, used_order_tender_ids):
        # type: (int, OrderTaker, List) -> None

        for order_tender_id in used_order_tender_ids:
            pos_ot.clearTenders(pos_id, None, order_tender_id)

    def _create_new_order(self, pos_id, table_id=None):
        # type: (int, str) -> Element

        # TODO: Review parameters when there is no Order (PriceList and SaleType)
        model = sysactions.get_model(pos_id)
        pos_ot = sysactions.get_posot(model)
        order = pos_ot.createOrder(pos_id, sysactions.get_pricelist(model), saletype="EAT_IN")

        if table_id:
            pos_ot.setOrderCustomProperty("TABLE_ID", value=table_id, orderid=order.get("orderId"))

        return self.get_order_picture(pos_id)
