import logging
import pyscripts

from msgbus import (
    MBEasyContext,
)
from sysactions import (
    show_keyboard,
    StopAction,
)

from actions.util import get_authorization
from mw_helper import show_filtered_list_box_dialog
from application.repository.impl import MwI18nRepository
from application.domain import (
    I18nImpl,
    TenderIdEnum,
)
from typing import (
    Dict,
    List,
    Optional,
    Union,
)
from xml.etree import cElementTree as eTree

from api.product import get_product_service

logger = logging.getLogger("PosActions")
mb_context = pyscripts.mbcontext  # type: MBEasyContext

all_tenders = None


def get_tender_type(
    pos_id,             # type: str
    model,              # type: eTree.Element
    tender_type_id,     # type: str
):
    # type: (...) -> Optional[Dict[str, Union[str, int, bool]]]

    tender_type = _select_tender_type(
        pos_id=pos_id,
        tender_type_id=tender_type_id,
    )
    if tender_type is None:
        return None

    necessary_level = int(tender_type['needAuth'] or 0)
    operator_level = _get_current_operator_level(model=model)
    if necessary_level > operator_level:
        authorized = get_authorization(
            pos_id=pos_id,
            min_level=necessary_level,
            model=model,
        )[0]

        if not authorized:
            return None

    return tender_type


def get_all_tender_types():
    # type: (...) -> List[Dict[str, Union[str, int, bool]]]

    global all_tenders
    if all_tenders is not None:
        return all_tenders

    product_service = get_product_service()
    tender_types = product_service.get_tenders_list()
    for tender in tender_types:
        if tender["id"] >= 200:
            continue

        tender["showInFC"] = True if tender["id"] < 90 else False
        tender["showInDL"] = True if 90 <= tender["id"] < 120 else False

        has_child = False
        for tender_type_aux in tender_types:
            if tender_type_aux["parentId"] == tender["id"]:
                has_child = True
                break

        tender["hasChild"] = has_child

    try:
        tender_types = sorted(tender_types, key=lambda k: k['id'])
    except (Exception, ) as _:
        logger.exception("Error ordering tender types")

    all_tenders = tender_types
    return all_tenders


def _select_tender_type(
    pos_id,             # type: str
    tender_type_id,     # type: str
):
    # type: (...) -> Optional[Dict[str, Union[str, int, bool]]]

    all_tender_types = get_all_tender_types()
    tender_types = [
        tender
        for tender in all_tender_types
        if (tender["parentId"] is not None and tender["parentId"] == int(tender_type_id))
    ]
    if len(tender_types) != 0:
        if len(tender_types) == 1:
            return tender_types[0]
        else:
            options = [tender["descr"] for tender in tender_types]
            selected_option = show_filtered_list_box_dialog(
                pos_id=pos_id,
                options=options,
                title="$SELECT_PAYMENT_TYPE",
                mask="NOFILTER",
            )
            if selected_option is None:
                return None

            tender_type_id = tender_types[selected_option]["id"]
            is_payment_on_arrival = tender_type_id == TenderIdEnum.CASH_ON_DELIVERY.value
            if is_payment_on_arrival:
                return _payment_on_arrival_message(
                    pos_id=pos_id,
                    tender_types=tender_types,
                    tender_type_id=tender_type_id,
                )
    else:
        return [tender for tender in all_tender_types if tender["id"] == int(tender_type_id)][0]

    return [tender for tender in tender_types if tender["id"] == tender_type_id][0]


def _payment_on_arrival_message(
    pos_id,             # type: str
    tender_types,       # type: Optional[List[Dict[str, Union[str, int, bool]]]]
    tender_type_id,     # type: str
):
    # type: (...) -> Optional[Dict[str, Union[str, int, bool]]]

    payment_options = ["$BRING_CARD_MACHINE", "$BRING_CHANGE"]
    selected_option = show_filtered_list_box_dialog(
        pos_id=pos_id,
        options=payment_options,
        title="$PAYMENT_INSTRUCTION",
        mask="NOFILTER",
        buttons="$OK|$CANCEL",
    )

    if selected_option is None:
        raise StopAction()

    i18n = I18nImpl(MwI18nRepository(pos_id=pos_id))
    translated_options = i18n.internationalize(labels=payment_options)
    tender_on_arrival = next((tender for tender in tender_types if tender.get("id") == tender_type_id), None)
    tender_on_arrival["tenderTypeDescription"] = translated_options[payment_options[selected_option]]
    tender_on_arrival["changeAmount"] = ""

    ask_change = selected_option == payment_options.index('$BRING_CHANGE')
    if ask_change:
        change_amount = show_keyboard(
            posid=pos_id,
            message="$ONLY_NUMBERS",
            title="$ENTER_CHANGE_AMOUNT",
            buttons="$OK|$CANCEL",
            mask="CURRENCY",
            numpad=True,
        )

        if change_amount is None:
            raise StopAction()

        tender_on_arrival["changeAmount"] = change_amount

    return tender_on_arrival


def _get_current_operator_level(
    model,  # type: eTree.Element
):
    # type: (...) -> int

    operator_level = 0
    model_operator = model.find("Operator")
    if model_operator is not None:
        operator_level = int(model_operator.get("level") or 0)

    return operator_level
