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

import sysactions

from collections import OrderedDict
from bustoken import TK_FISCALWRAPPER_CANCEL_ORDER
from msgbus import (
    FM_PARAM,
    TK_SYS_ACK,
    TK_SLCTRL_OMGR_VOIDORDER
)
from mw_helper import show_filtered_list_box_dialog
from posot import OrderTaker
from utilconfigurations import get_cancel_json
from typing import (
    Any,
    Dict,
    List,
    Optional,
    Union
)

from .. import mb_context

void_reason_list = None     # type: Optional[Dict]


def void_order(
    pos_id,                 # type: Any[Union[int, str]]
    last_paid_order="0",    # type: Optional[str]
    order_id='',            # type: Optional[str]
    abandon=0,              # type: Optional[int]
    void_reason=9,          # type: Optional[int]
    pos_ot=None,            # type: Optional[OrderTaker]
    new_order_status=None,  # type: Optional[int]
):
    # 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 (type(void_reason) == int) or not void_reason:
        void_reason = get_void_reason_id(pos_id, void_reason)

    params = (
        '1' if pos_ot.blkopnotify else '0',
        pos_id,
        '1' if int(last_paid_order) else '0',
        void_order_id,
        '1' if abandon else '0',
        new_order_status,
        json.dumps(void_reason),
    )
    params = "\0".join(str(p) for p in params)
    msg = mb_context.MB_EasySendMessage(
        dest_name="ORDERMGR%d" % int(pos_id),
        token=TK_SLCTRL_OMGR_VOIDORDER,
        format=FM_PARAM,
        data=params,
        timeout=600000000
    )

    return msg.token == TK_SYS_ACK


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


def get_void_reason_id(
    pos_id,
    selected_reason=None,
    void_type=None,
    operator=None,
):
    # type: (...) -> Optional[Dict]

    void_reason_options = get_void_reason_options(
        pos_id=pos_id,
    )

    if not selected_reason:
        void_reason_options = _get_void_reason_to_show(pos_id=pos_id, void_type=void_type)
        title = "$SELECT_REASON"
        selected_reason = show_filtered_list_box_dialog(
            pos_id=pos_id,
            options=void_reason_options,
            title=title,
            mask="NOFILTER",
        )

    if selected_reason is None:
        return None

    selected_reason = int(selected_reason)
    void_reason_id = selected_reason + 1
    void_reason_description = void_reason_options[selected_reason]
    void_reason_dict = {'VOID_REASON_ID': void_reason_id, 'VOID_REASON_DESCR': void_reason_description}

    if operator is not None:
        void_reason_dict["VOID_REASON_OPERATOR"] = operator

    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

    global void_reason_list

    if void_reason_list is None:
        void_reason_list = [
            {"id": 1, "reasonKey": "$CHANGED_YOUR_MIND"},
            {"id": 2, "reasonKey": "$DUPLICATE"},
            {"id": 3, "reasonKey": "$WRONG_SALE"},
            {"id": 4, "reasonKey": "$CANCELLATION"},
            {"id": 5, "reasonKey": "$COUPON_CANCELED"},
            {"id": 6, "reasonKey": "$PAYMENT_CANCELED"},
            {"id": 7, "reasonKey": "$SAVED_ORDER_CANCELED"},
            {"id": 8, "reasonKey": "$ERROR_CREATING_SALE"},
            {"id": 9, "reasonKey": "$ERROR_PROCESSING_PAYMENT_SELECTION"},
            {"id": 10, "reasonKey": "$CANCELLATION_VIA_SYSTEM"},
            {"id": 11, "reasonKey": "$SYNC_ERROR"},
            {"id": 12, "reasonKey": "$MANAGER_AUTHORIZATION_CANCELLATION"},
        ]

        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_options(
    pos_id,  # type: str
):
    # type: (...) -> List[str]

    return _translate_array(pos_id, get_void_reason_dict())


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=[1, 2, 3, 4])
    cancel_json = get_cancel_json()
    void_reason_key = {
        "VOID_CURRENT_ORDER": "orderReasons",
        "VOID_PAID_ORDER": "couponReasons",
        "VOID_LINE": "lineReasons",
    }
    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
