# -*- coding: utf-8 -*-
import json

import sysactions

from collections import OrderedDict

from api.order import get_order_service
from bustoken import TK_FISCALWRAPPER_CANCEL_ORDER
from msgbus import (
    FM_PARAM,
    TK_SYS_ACK,
)
from mw_helper import show_filtered_list_box_dialog
from posot import OrderTaker
from utilconfigurations import get_cancel_json
from utilfunctions import build_clean_customer_props_dict
from typing import (
    Any,
    Dict,
    List,
    Optional,
    Union
)

from .. import mb_context
from ..util.get_menu_user_logged import get_menu_manager_authenticated_user


def void_order(
    pos_id,                             # type: Any[Union[int, str]]
    order_id='',                        # type: Optional[str]
    abandon=0,                          # type: Optional[int]
    void_reason=None,                   # type: Optional[Any[int, Dict]]
    pos_ot=None,                        # type: Optional[OrderTaker]
    new_order_status=None,              # type: Optional[int]
    void_type="VOID_CURRENT_ORDER",     # type: Optional[str]
):
    # type: (...) -> bool

    model = sysactions.get_model(pos_id)
    if pos_ot is None:
        pos_ot = sysactions.get_posot(model)

    order_id = int(order_id) if order_id != "" else ""
    void_order_id = order_id
    if void_reason is None:
        void_reason = get_void_reason_id(
            pos_id=pos_id,
            selected_reason="UNDEFINED_REASON",
            authentication_user="SYSTEM",
            void_type=void_type,
        )
    elif not isinstance(void_reason, dict):
        void_reason = get_fixed_void_reason(
            pos_id=pos_id,
            reason=void_reason,
            canceled_by="SYSTEM",
            void_type=void_type,
        )

    order_service = get_order_service(pos_id=pos_id)
    order_service.void_order(
        order_id=void_order_id,
        abandon=abandon != 0,
        order_status=new_order_status,
        void_reason=json.dumps(void_reason),
        blk_notify=pos_ot.blkopnotify,
    )

    clean_customer_dict = build_clean_customer_props_dict()

    pos_ot.setOrderCustomProperties(
        properties=clean_customer_dict,
        orderid=order_id,
    )

    return True


def void_fiscal_order(
    pos_id,                     # type: int
    order_id,                   # type: int
    justification,              # type: str
):
    # type: (...) -> None

    data = "|".join([str(pos_id), str(order_id), str(justification)])
    msg = mb_context.MB_EasySendMessage("FiscalWrapper", TK_FISCALWRAPPER_CANCEL_ORDER, format=FM_PARAM, data=data)
    if msg.token != TK_SYS_ACK:
        sysactions.show_messagebox(
            posid=pos_id,
            message="$ERROR_CANCEL_FISCAL_ORDER|{}".format(msg.data),
        )
        raise sysactions.StopAction(msg.data)


def get_void_reason_id(
    pos_id,                     # type: str
    selected_reason=None,       # type: Optional[str]
    void_type=None,             # type: Optional[str]
    authentication_user=None,   # type: Optional[str]
    is_void_line=False,         # type: Optional[bool]
):
    # type: (...) -> Optional[Dict]

    if not selected_reason:
        void_reason_options = _get_void_reason_to_show(pos_id=pos_id, void_type=void_type)

        total_void_reasons = len(void_reason_options)
        if total_void_reasons > 1:
            title = "$SELECT_REASON"
            selected_reason = show_filtered_list_box_dialog(
                pos_id=pos_id,
                options=void_reason_options,
                title=title,
                mask="NOFILTER",
            )
        elif total_void_reasons == 1:
            selected_reason = 0
            do_confirm_void(
                pos_id=pos_id,
                message="$CONFIRM_VOID_LINE" if is_void_line else "$CONFIRM_VOID_ORDER",
            )

        if selected_reason is None:
            return None

        selected_reason = int(selected_reason)
        void_reason_id = void_reason_options[selected_reason].split('-')[0].strip()
        void_reason_description = void_reason_options[selected_reason].split('-')[1].strip()
        void_reason_dict = {
            "VOID_REASON_ID": void_reason_id,
            "VOID_REASON_DESCR": void_reason_description,
            "VOID_TYPE": void_type,
        }
        if authentication_user not in [None, "None"]:
            void_reason_dict.update({"VOID_AUTH_USER": authentication_user})
    else:
        void_reason_dict = get_fixed_void_reason(
            pos_id=pos_id,
            reason=selected_reason,
            canceled_by=authentication_user,
            void_type=void_type,
        )

    return void_reason_dict


def do_confirm_void(
    pos_id,                         # type: str
    message="$CONFIRM_VOID_ORDER",  # type: str
    ignore_timeout=False,           # type: bool
):
    # type: (...) -> None

    confirmation = _get_confirmation_void_order(
        pos_id=pos_id,
        message=message,
    )
    accept_confirmations = [1] if ignore_timeout else [None, 1]
    if confirmation in accept_confirmations:
        raise sysactions.StopAction()


def get_fixed_void_reason(
    pos_id,         # type: str
    reason,         # type: str
    canceled_by,    # type: str
    void_type,      # type: str
):
    # type: (...) -> Dict

    void_reason = get_void_reason_dict().get(reason)
    void_reason_id = void_reason.get("id")
    void_reason_description = void_reason.get("reasonKey")

    model = sysactions.get_model(posid=pos_id)
    void_reason_description = sysactions.translate_message(
        model=model,
        message_key=void_reason_description.replace("$", ""),
    )

    void_reason_dict = {
        "VOID_REASON_ID": void_reason_id,
        "VOID_REASON_DESCR": void_reason_description,
        "VOID_TYPE": void_type,
    }
    if canceled_by not in [None, "None"]:
        void_reason_dict.update({"VOID_AUTH_USER": canceled_by})

    return void_reason_dict


def void_reason_list_to_ordered_dict(void_reasons):
    # type: (...) -> OrderedDict

    void_reasons = sorted(
        void_reasons,
        key=lambda k: k["id"]
    )
    ordered_dict = OrderedDict()
    for item in void_reasons:
        ordered_dict[item["reasonKey"].replace("$", "")] = item

    return ordered_dict


def get_void_reason_dict(
    filtered_items=None,  # type: Optional[List]
):
    # type: (...) -> Dict

    void_reason_list = [
        {"id": 101, "reasonKey": "$CHANGED_YOUR_MIND"},
        {"id": 102, "reasonKey": "$DUPLICATE"},
        {"id": 103, "reasonKey": "$WRONG_SALE"},
        {"id": 104, "reasonKey": "$CANCELLATION"},
        {"id": 105, "reasonKey": "$USER_CANCELLATION"},
        {"id": 106, "reasonKey": "$USER_FISCAL_CANCELLATION"},
        {"id": 107, "reasonKey": "$TIMEOUT_CANCELLATION"},
        {"id": 108, "reasonKey": "$UNDEFINED_REASON"},
        {"id": 109, "reasonKey": "$RECALL_ORDER_TO_OLD"},
        {"id": 110, "reasonKey": "$UNKNOWN_FISCAL_ERROR"},
        {"id": 111, "reasonKey": "$POS_MOBILE_ON_PAUSE"},
        {"id": 112, "reasonKey": "$MANAGER_CLEAR_STORED_ORDERS"},
    ]

    void_reason_list = void_reason_list_to_ordered_dict(void_reason_list)

    return _filter_void_reason_items(void_reason_list, filtered_items)


def _filter_void_reason_items(
    void_reasons,           # type: OrderedDict
    filtered_items=None,    # type: Optional[List]
):
    # type: (...) -> Dict

    if filtered_items:
        return {k: v for k, v in void_reasons.iteritems() if v.get("id") in filtered_items}

    return void_reasons


def _get_void_reason_to_show(
    pos_id,     # type: str
    void_type,  # type: str
):
    # type: (...) -> List[str]

    void_reason_dict = get_void_reason_dict(filtered_items=[101, 102, 103, 104])
    cancel_json = get_cancel_json()
    void_reason_key = {
        "VOID_CURRENT_ORDER": "orderReasons",
        "VOID_STORED_ORDER": "storedOrderReasons",
        "VOID_PAID_ORDER": "couponReasons",
        "VOID_LINE": "lineReasons",
        "VOID_LINE_ON_STORED_ORDER": "lineStoredOrderReasons"
    }
    if cancel_json and void_reason_key[void_type] in cancel_json:
        void_reason_dict = void_reason_list_to_ordered_dict(cancel_json[void_reason_key[void_type]])

    return _translate_array(pos_id, void_reason_dict)


def _translate_array(
    pos_id,    # type: str
    messages,  # type: dict
):
    # type: (...) -> List[str]

    model = sysactions.get_model(pos_id)
    result = []
    for key, item in messages.items():
        reason_id = item["id"]
        reason = item["reasonKey"].encode('utf-8')
        if reason.startswith("$"):
            reason = sysactions.translate_message(
                model=model,
                message_key=reason.replace("$", ""),
            )
        result.append("{} - {}".format(
            reason_id,
            reason,
        ))

    return result


def get_canceled_by(
    pos_id,       # type: str
    model,        # type: eTree.XML
    auth_user,    # type: Union[str]
):
    # type: (...) -> str

    authenticated_manager = get_menu_manager_authenticated_user(int(pos_id))
    if authenticated_manager is not None:
        return str(authenticated_manager)

    if auth_user:
        return str(auth_user)

    operator = model.find("Operator")
    return operator.get("id", "UNDEFINED") if operator is not None else "UNDEFINED"


def _get_confirmation_void_order(
    pos_id,     # type: str
    message,    # type: str
):
    # type: (...) -> Optional[int]

    return sysactions.show_messagebox(
        posid=pos_id,
        message=message,
        buttons="$OK|$CANCEL",
    )
