from xml.etree import cElementTree as eTree
from sysactions import (
    get_model,
    get_posot,
    OrderTaker,
)
from services.model import OrderCanceledRequest
from externals.repository import AlertsRepository
from services.model import ConfiguredAlert
from services.util import (
    get_configured_alerts_by_type,
    insert_event_into_repository,
    get_order_total,
    get_table_picture,
)
from mwhelper import ModelService


class OrderCanceledInteractor(object):
    def __init__(
        self,
        alerts_repository,  # type: AlertsRepository
    ):
        # type: (...) -> None

        self.alerts_repository = alerts_repository
        self.configured_alerts = self.alerts_repository.get_configured_alerts()

    def process_request(
        self,
        request,  # type: OrderCanceledRequest
    ):
        # type: (...) -> None

        configured_alerts = get_configured_alerts_by_type(
            notify_type="CANCEL",
            configured_alerts=self.configured_alerts,
        )
        if not configured_alerts:
            return

        pos_id = request.pos_id
        order_id = request.order_id
        model = get_model(posid=pos_id)
        pos_ot = get_posot(model=model)
        order_picture = eTree.XML(pos_ot.orderPicture(orderid=order_id)).find("Order")

        if not self._should_notify_order(order_picture=order_picture):
            return

        is_table = ModelService.is_table_service(order_picture)
        fiscalization_date = "CustomOrderProperties/OrderProperty[@key='FISCALIZATION_DATE']"
        has_fiscalization_date = order_picture.find(fiscalization_date) is not None
        state_history = eTree.tostring(order_picture.find("StateHistory"))
        is_fiscalized = "PAID" in state_history and has_fiscalization_date
        if is_table and not is_fiscalized:
            total = self._get_table_total(
                pos_id=pos_id,
                order_picture=order_picture,
                pos_ot=pos_ot,
            )
        else:
            total = get_order_total(order_picture=order_picture)

        order_picture_dict = None
        table_picture_dict = None
        for alert in configured_alerts:
            notify_name = "cancelAmount"
            if is_table:
                notify_name = "tableCancelAmount"
            valid_conditions = self._validate_alert(
                alert=alert,
                has_fiscalization_date=has_fiscalization_date,
                total=total,
                notify_name=notify_name,
                is_table=is_table
            )
            if not valid_conditions:
                continue

            order_picture_dict, table_picture_dict = insert_event_into_repository(
                pos_id=pos_id,
                alerts_repository=self.alerts_repository,
                order_picture=order_picture,
                alert=alert,
                order_picture_dict=order_picture_dict,
                table_picture_dict=table_picture_dict,
            )

    @staticmethod
    def _should_notify_order(
        order_picture,    # type: eTree.Element
    ):
        # type: (...) -> bool

        state_history = eTree.tostring(order_picture.find("StateHistory"))
        was_stored = "STORED" in state_history

        fiscalization_date = "CustomOrderProperties/OrderProperty[@key='FISCALIZATION_DATE']"
        has_fiscalization_date = order_picture.find(fiscalization_date) is not None
        is_fiscalized = "PAID" in state_history and has_fiscalization_date

        is_table = ModelService.is_table_service(order_picture)
        if is_table:
            if was_stored or is_fiscalized:
                return True
        else:
            is_stored_drive_thru = order_picture.get("saleTypeDescr") == "DRIVE_THRU" and was_stored
            if is_stored_drive_thru:
                return True

            return is_fiscalized

        return False

    @staticmethod
    def _validate_alert(
        alert,                   # type: ConfiguredAlert
        has_fiscalization_date,  # type: bool
        total,                   # type: float
        notify_name,             # type: str
        is_table,                # type: bool
    ):
        # type: (...) -> bool

        alert_conditions = alert.alert_json.get("conditions")
        notify_cancel = notify_name in alert_conditions
        reached_cancel_amount = total >= float(alert_conditions.get(notify_name))
        if notify_cancel and not reached_cancel_amount:
            return False

        alert_after_fiscalization = "onlyAfterFiscalization" in alert_conditions
        if alert_after_fiscalization:
            only_after_fiscalization = alert_conditions.get("onlyAfterFiscalization") in ("True", True)
            if only_after_fiscalization and not has_fiscalization_date:
                return False
        elif not has_fiscalization_date and not is_table:
            return False

        return True

    @staticmethod
    def _get_table_total(
        pos_id,             # type: str
        order_picture,      # type: eTree.XML
        pos_ot,             # type: OrderTaker
    ):
        # type: (...) -> float

        table_id = order_picture.find("CustomOrderProperties/OrderProperty[@key='TABLE_ID']").get("value")
        table_picture = eTree.XML(
            get_table_picture(
                pos_id=pos_id,
                table_id=table_id,
            )
        )

        service_orders = table_picture.findall("ServiceOrders/ServiceOrder")
        table_total = 0
        for order in service_orders:
            state = order.get("state")
            order_id = order.get("orderId")
            order_picture = eTree.XML(pos_ot.orderPicture(orderid=order_id)).find("Order")
            state_history = eTree.tostring(order_picture.find("StateHistory"))
            if state != "VOIDED" or "STORED" not in state_history:
                continue
            table_total += get_order_total(order_picture=order_picture)
        return table_total
