from typing import (
    Dict,
    List,
    Optional,
)

import sysactions
from posot import OrderTaker


from utils import (
    get_void_reason_id,
    get_canceled_by,
    do_confirm_void,
    get_fixed_void_reason,
)
from custom_sysactions import user_authorized_to_execute_action
from manager.reports import justification_report
from utilconfigurations import must_require_void_reason
from utilfunctions import (
    is_stored_order,
    is_order_in_progress,
)


@sysactions.action
def do_void_lines(
    pos_id,             # type: str
    line_numbers=None,  # type: Optional[str]
    auth_data=None,     # type: Optional[str]
):
    # type: (...) -> bool

    model = sysactions.get_model(posid=pos_id)
    stored_order = is_stored_order(model=model)
    in_progress_order = is_order_in_progress(model=model)

    if stored_order:
        do_void_lines_stored_order(
            pos_id,
            line_numbers=line_numbers,
            auth_data=auth_data,
        )
    elif in_progress_order:
        do_void_lines_in_progress_order(
            pos_id,
            line_numbers=line_numbers,
            auth_data=auth_data,
        )

    return True


@user_authorized_to_execute_action
def do_void_lines_in_progress_order(
    pos_id,         # type: str
    line_numbers,   # type: str
    auth_data,      # type: str
):
    # type: (...) -> None

    _void_lines(
        pos_id=pos_id,
        line_numbers=line_numbers,
        auth_data=auth_data,
    )


@user_authorized_to_execute_action
def do_void_lines_stored_order(
    pos_id,         # type: str
    line_numbers,   # type: str
    auth_data,      # type: str
):
    # type: (...) -> None

    _void_lines(
        pos_id=pos_id,
        line_numbers=line_numbers,
        auth_data=auth_data,
        stored_order=True,
    )


def _void_lines(
    pos_id,              # type: str
    line_numbers,        # type: str
    auth_data,           # type: str
    stored_order=False,  # type: Optional[bool]
):
    # type: (...) -> bool

    if not line_numbers:
        return False

    model = sysactions.get_model(pos_id)
    sysactions.check_current_order(pos_id, model=model)

    if not sysactions.can_void_line(model, line_numbers):
        sysactions.show_messagebox(pos_id, "$NEED_TO_HAVE_ITEM_TO_VOID")
        return False

    auth_user = auth_data if auth_data is not None else None
    canceled_by = get_canceled_by(
        pos_id,
        model,
        auth_user
    )

    void_type = "VOID_LINE_ON_STORED_ORDER" if stored_order else "VOID_LINE"
    if must_require_void_reason(void_type):
        void_reason = get_void_reason_id(
            pos_id=pos_id,
            void_type=void_type,
            authentication_user=canceled_by,
            is_void_line=True,
        )
        if void_reason is None:
            return True
    else:
        do_confirm_void(
            pos_id=pos_id,
            message="$CONFIRM_VOID_LINE",
        )
        void_reason = get_fixed_void_reason(
            reason="USER_CANCELLATION",
            canceled_by=canceled_by,
            void_type=void_type,
        )

    pos_ot = sysactions.get_posot(model)
    order = model.find("CurrentOrder/Order")
    order_id = order.get("orderId")
    sale_lines = order.findall("SaleLine")

    do_void_lines_justification_report(
        pos_id=pos_id,
        void_reason=void_reason,
        canceled_by=canceled_by,
        order_id=order_id,
        stored_order=stored_order,
    )

    pos_ot.voidLine(int(pos_id), line_numbers)
    update_lines_properties(
        pos_ot=pos_ot,
        order_id=order_id,
        line_numbers=line_numbers,
        void_reason=void_reason,
        canceled_by=canceled_by,
        sale_lines=sale_lines,
    )

    return True


def do_void_lines_justification_report(
    pos_id,         # type: str
    void_reason,    # type: Dict
    canceled_by,    # type: str
    order_id,       # type: str
    stored_order,   # type: bool
):
    # type: (...) -> None

    justification_type = "VOID_LINE_ON_STORED_ORDER" if stored_order else "VOID_LINE"
    justification_report(
        pos_id=pos_id,
        justification_type=justification_type,
        justification=void_reason['VOID_REASON_DESCR'],
        authorized_by=canceled_by,
        order_id=order_id,
    )


def update_lines_properties(
    pos_ot,         # type: OrderTaker
    order_id,       # type: str
    line_numbers,   # type: str
    void_reason,    # type: Dict
    canceled_by,    # type: str
    sale_lines,     # type: List
):
    # type: (...) -> None

    for line_number in line_numbers.split('|'):
        custom_properties = {}
        custom_properties.update(void_reason or "")
        if void_reason is None:
            custom_properties.update({"VOID_AUTH_USER": canceled_by})

        current_line_sale_lines = [x for x in sale_lines if x.get("lineNumber") == line_number]
        for current_line in current_line_sale_lines:
            item_type = current_line.get("itemType")
            if item_type == "OPTION":
                continue

            voided_qty = float(current_line.get("qty", 0))
            if item_type != "PRODUCT":
                voided_qty = voided_qty - float(current_line.get("defaultQty", 0))
            custom_properties.update({"VOID_QTY": str(voided_qty)})

            pos_ot.setOrderCustomProperties(
                custom_properties,
                orderid=order_id,
                linenumber=line_number,
                itemid=current_line.get("itemId"),
                level=current_line.get("level"),
                partcode=current_line.get("partCode"),
            )
