# -*- 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 (
    Dict,
    List,
    Optional,
    Union,
)
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 mwhelper import ModelService

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 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{}".format(str(pos_id), str(order_id or ''))
        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)
        pos_ot.blkopnotify = True
        sale_type = "EAT_IN"
        model_service = ModelService(pos_id=pos_id)
        order = pos_ot.createOrder(
            posid=pos_id,
            pricelist=model_service.get_price_list_from_model(
                sale_type=sale_type,
            ),
            saletype=sale_type,
        )

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

        pos_ot.blkopnotify = False

        return self.get_order_picture(pos_id)
