import json
from xml.etree import cElementTree as eTree

from bustoken import TK_SLCTRL_OMGR_FRACTIONATELINE
from msgbus import TK_SYS_ACK
from mw_helper import (
    show_keyboard,
    show_message_dialog,
    show_messagebox,
)
from sysactions import (
    StopAction,
    action,
    send_message,
)
from typing import (
    Dict,
    List,
    Optional,
    Union,
)

from custom_sysactions import user_authorized_to_execute_action

INFORMATION_TITLE = "$INFORMATION"


@action
@user_authorized_to_execute_action
def do_split_items(
    pos_id,            # type: str
    sale_line_json,    # type: json
):
    # type: (...) -> Optional[bool]

    from msgbusboundary import MsgBusTableService as tableService

    sale_line_list = filter_sale_line_list(
        pos_id=pos_id,
        sale_line_json=sale_line_json,
    )

    if not sale_line_list:
        return False

    table_id = tableService().get_current_table_id(pos_id=pos_id)
    table_seats = tableService().get_service_seats(
        pos_id=pos_id,
        table_id=table_id,
    )

    fraction_qty = show_keyboard(
        posid=pos_id,
        message="",
        title="$FRACTION_QTY",
        mask="INTEGER",
        numpad=True,
        defvalue=table_seats,
    )

    if fraction_qty is None:
        return False

    is_valid_fraction_qty = validate_fraction_qty(
        pos_id=pos_id,
        fraction_qty=fraction_qty,
        message="$INVALID_FRACTION_QTY",
    )
    if not is_valid_fraction_qty:
        return False

    distribute_items_in_seats = False
    if int(fraction_qty) <= table_seats:
        ret = show_messagebox(
            posid=pos_id,
            message="$DISTRIBUTE_ITEMS_IN_SEATS",
            timeout=720000,
            buttons="$YES|$NO",
        )
        distribute_items_in_seats = ret == 0

    split_items(
        pos_id=pos_id,
        table_id=table_id,
        sale_line_list=sale_line_list,
        fraction_qty=fraction_qty,
        distribute_items_in_seats=distribute_items_in_seats,
    )


def validate_fraction_qty(
    pos_id,         # type: str
    fraction_qty,   # type: int
    message,        # type: str
):
    # type: (...) -> bool

    from table_actions import item_division_limit

    if fraction_qty == "" or int(fraction_qty) <= 1 or int(fraction_qty) > item_division_limit:
        show_message_dialog(
            pos_id=pos_id,
            title=INFORMATION_TITLE,
            message=message,
        )
        return False

    return True


def split_items(
    pos_id,                     # type: str
    table_id,                   # type: str
    sale_line_list,             # type: List
    fraction_qty,               # type: int
    distribute_items_in_seats,  # type: bool
):
    # type: (...) -> Optional[bool]

    from msgbusboundary import MsgBusTableService as tableService
    from table_actions import create_table_modified_event
    from api.table import get_table_service

    orders_data = _get_orders_data(sale_line_list=sale_line_list)
    table_service = get_table_service(pos_id=pos_id)
    table = table_service.get_table(table_id=table_id)

    data = {
        "orders_data": orders_data,
        "blk_notify": True,
        "pos_id": int(pos_id),
        "fraction_qty": int(fraction_qty),
        "occupied_seats": table.occupied_seats if distribute_items_in_seats else [],
    }

    msg = send_message(
        dest_name="ORDERMGR{}".format(pos_id),
        token=TK_SLCTRL_OMGR_FRACTIONATELINE,
        data=json.dumps(data),
        timeout=60 * 1000000,
    )
    if msg.token != TK_SYS_ACK:
        show_message_dialog(pos_id, "$FATAL_ERROR_CALL_SUPPORT", msg.data)
        raise StopAction()

    table_picture = tableService().get_table_picture(
        pos_id=pos_id,
        table_id=table_id,
    )
    create_table_modified_event(
        pos_id=pos_id,
        table_picture=table_picture,
    )
    return True


def _get_orders_data(
    sale_line_list,     # type: List
):
    # type: (...) -> Dict[str, List]

    orders_data = {}
    for sale_line in sale_line_list:
        order_id = sale_line.get("orderId", None)
        line_number = sale_line.get("lineNumber", None)
        if order_id in orders_data:
            orders_data[order_id].append(line_number)
        else:
            orders_data[order_id] = [line_number]

    return orders_data


def filter_sale_line_list(
    pos_id,            # type: str
    sale_line_json,    # type: json
):
    # type: (...) -> Union[List, bool]

    sale_line_list = json.loads(sale_line_json)
    sale_line_list = list(filter(lambda x: x.get("level") == 0, sale_line_list))

    if len(sale_line_list) < 1:
        show_message_dialog(
            pos_id=pos_id,
            title=INFORMATION_TITLE,
            message="$NO_LINE_SELECTED"
        )
        return False

    new_sale_line_list = []
    for sale_line in sale_line_list:
        if sale_line["fractionFrom"] is None:
            new_sale_line_list.append(sale_line)

    if not new_sale_line_list:
        show_message_dialog(
            pos_id=pos_id,
            title=INFORMATION_TITLE,
            message="$CANNOT_FRACTION_TWICE",
        )
        return False

    return new_sale_line_list
