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

import logging
from xml.etree import cElementTree as eTree

from api.order import get_order_service
from custom_sysactions import block_action_if_pos_is_blocked
from peripherals.scale import PosScaleEvents, ScaleEvent, WeightComparer
from sysactions import (
    StopAction,
    action,
    get_posot,
    show_messagebox,
    show_keyboard,
    close_asynch_dialog,
)
from thread_local import get_current_model
from typing import Optional
from utilfunctions import get_device_service

from .drive_thru import validate_can_change_order
from ..products.get_products_with_measure_unit import is_a_product_with_measure_unit

waiting_scale = False
logger = logging.getLogger("PosActions")


@action
def doCompleteOption(
    pos_id,                 # type: str
    context,                # type: str
    part_code,              # type: str
    qty="1",                # type: str
    line_number="",         # type: str
    sale_type="EAT_IN",     # type: str
    subst="",               # type: str
    seat="",                # type: str
    comment=None,           # type: Optional[str]
    barcode_number=None,    # type: Optional[str]
):
    # type: (...) -> Optional[str]

    from posactions import (
        is_table_service,
        is_changing_order,
    )

    global waiting_scale

    model = get_current_model()

    order_service = get_order_service(pos_id=pos_id)
    order_picture = order_service.get_order_picture()

    is_order_table_service = is_table_service(model=model, order_picture=order_picture)
    if is_order_table_service and is_changing_order(model=model, order_picture=order_picture):
        show_messagebox(
            posid=pos_id,
            message="$CANNOT_ADD_ITEMS_IN_AN_OPENED_ORDER",
            icon="error",
        )
        raise StopAction()

    order_is_in_progress = order_picture and order_picture.get("state") == "IN_PROGRESS"
    if not is_order_table_service or not order_is_in_progress:
        _verify_pos_is_blocked(pos_id=pos_id, model=model)

    if order_is_in_progress:
        validate_can_change_order(
            pos_id=pos_id,
            order_picture=order_picture,
            line_to_replace=subst,
            context=context,
            line_number=line_number,
            sale_type=sale_type,
        )

    if is_a_product_with_measure_unit(product_code=part_code) and not isinstance(qty, float):
        if waiting_scale:
            return

        try:
            waiting_scale = True
            qty = _get_weight_qty(pos_id=pos_id)
        finally:
            waiting_scale = False

    sale_type = sale_type or "EAT_IN"
    return _do_complete_option(
        pos_id=pos_id,
        model=model,
        subst=subst,
        line_number=line_number,
        context=context,
        part_code=part_code,
        sale_type=sale_type,
        comment=comment,
        qty=qty,
        seat=seat,
        barcode_number=barcode_number,
        order_picture=order_picture,
    )


def _do_complete_option(
    pos_id,             # type: str
    model,              # type: eTree.Element
    subst,              # type: str
    line_number,        # type: str
    context,            # type: str
    part_code,          # type: str
    sale_type,          # type: str
    comment,            # type: str
    qty,                # type: str
    seat,               # type: str
    barcode_number,     # type: str
    order_picture,      # type: eTree.Element
):
    # type: (...) -> Optional[str]

    from posactions import (
        doClearOptionItem,
        doOption,
    )

    sale_xml = None
    pos_ot = get_posot(model=model)
    try:
        if subst:
            if qty != "0":
                pos_ot.blkopnotify = True

            doClearOptionItem(
                pos_id=pos_id,
                linenumber=line_number,
                item_id=subst,
                pos_ot=pos_ot,
            )
            pos_ot.blkopnotify = False

        if qty != "0":
            sale_xml = doOption(
                pos_id=pos_id,
                context=context,
                part_code=part_code,
                qty=qty,
                line_number=line_number,
                sale_type=sale_type,
                comment=comment,
                pos_ot=pos_ot,
                seat=seat,
                barcode_number=barcode_number,
                order_picture=order_picture,
            )

        return eTree.tostring(sale_xml).decode("utf-8") if sale_xml is not None else None
    finally:
        pos_ot.blkopnotify = False


@block_action_if_pos_is_blocked
def _verify_pos_is_blocked(
    pos_id,     # type: str
    model,      # type: eTree.Element
):
    # type: (...) -> None

    return


def _get_weight_qty(
    pos_id,    # type: str
):
    # type: (...) -> str

    weight_qty = _get_scale_weight(pos_id=pos_id)
    if weight_qty is None:
        weight_qty = show_keyboard(
            posid=pos_id,
            message="$TYPE_WEIGHT_QTY",
            title="",
            mask="weight",
            defvalue="0.000",
            numpad=True,
        )
    if weight_qty in (None, "", "."):
        raise StopAction()

    if weight_qty == "0":
        raise StopAction()

    return str(float(weight_qty))


def _get_scale_weight(
    pos_id,    # type: str
):
    # type: (...) -> str

    scale_name = get_device_service(
        pos_id=pos_id,
        device_type="scale",
    )
    weight = _scale_wait_data(
        pos_id=pos_id,
        service_name=scale_name,
    )
    return weight


def _scale_wait_data(
    pos_id,          # type: str
    service_name,    # type: str
    timeout=2000,    # type: Optional[float]
):
    # type: (...) -> Optional[str]

    scale_event = PosScaleEvents().create_scale_event(
        pos_id=pos_id,
        service_name=service_name,
    )

    return _get_scaled_data_weight(
        pos_id=pos_id,
        service_name=service_name,
        scale_event=scale_event,
        timeout=timeout,
    )


def _get_scaled_data_weight(
    pos_id,          # type: str
    service_name,    # type: str
    scale_event,     # type: ScaleEvent
    timeout,         # type: float
):
    # type: (...) -> Optional[str]

    from posactions import pos_scale_dialogs

    dialog_id = None
    weight_comparer = WeightComparer()
    try:
        while True:
            if dialog_id is None:
                dialog_id = show_messagebox(
                    posid=pos_id,
                    title="$WAIT_SCALE_WEIGHT",
                    message="$WAIT_SCALE_WEIGHT",
                    buttons="$CANCEL",
                    asynch=True,
                )
                pos_scale_dialogs.append(str(dialog_id))
            elif str(dialog_id) not in pos_scale_dialogs:
                return ""

            scale_event.wait(float(timeout / 1000.0))
            if scale_event.weight is None:
                return None

            if scale_event.weight > 0 and weight_comparer.is_weight_equal_to_previous(weight=scale_event.weight):
                close_asynch_dialog(
                    posid=pos_id,
                    dlgid=dialog_id,
                )
                dialog_id = None

                selected_option = show_messagebox(
                    posid=pos_id,
                    message="$CONFIRM_SCALE_WEIGHT|{0}".format("%.3f" % scale_event.weight),
                    buttons="$OK|$GET_WEIGHT_AGAIN|$CANCEL",
                )

                if selected_option == 0:
                    return scale_event.weight
                elif selected_option == 2:
                    return ""
    finally:
        _update_scale_event(
            pos_id=pos_id,
            dialog_id=dialog_id,
            scale_event=scale_event,
        )


def _update_scale_event(
    pos_id,         # type: str
    dialog_id,      # type: str
    scale_event,    # type: ScaleEvent
):
    # type: (...) -> None

    if pos_id in PosScaleEvents().get_wait_pos_id_event_dict():
        PosScaleEvents().finish_event(scale_event=scale_event)

    if dialog_id is not None:
        close_asynch_dialog(
            posid=pos_id,
            dlgid=dialog_id,
        )
