import json
import logging

from xml.etree import cElementTree as eTree

from bustoken import TK_ELECTRONIC_PAYMENT_CANCEL_PAYMENT_ORDER
from msgbus import (
    FM_PARAM,
    TK_SYS_ACK,
)
from mw_helper import show_message_options_dialog
from sysactions import (
    get_posot,
    mbcontext,
    show_messagebox,
)
from typing import (
    Optional,
    Tuple,
)

from actions.eft import (
    get_electronic_payment_component_name,
    get_electronic_tenders,
    save_cancel_receipt,
)
from application.interactor import (
    OrderInteractor,
    PrinterInteractor,
)
from utilfunctions import is_electronic_payment

logger = logging.getLogger("EftActions")


def cancel_eft_transaction(
    pos_id,                     # type: str
    model,                      # type: eTree.XML
    order_id,                   # type: str
    tender,                     # type: eTree.XML
):
    # type: (...) -> Optional[str]

    order_taker = get_posot(model)
    order_picture = order_taker.orderPicture(
        posid=pos_id,
        orderid=order_id or ""
    )

    order_xml = eTree.XML(order_picture)
    order = order_xml.find("Order") if order_xml.find("Order") else order_xml

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

    return _cancel_eft_tender(
        pos_id=pos_id,
        order=order,
        tender=tender,
        operator_id=operator_id,
    )


def clear_multiple_tenders(
    pos_id,                 # type: str
    order,                  # type: eTree.XML
    order_id,               # type: str
    is_mobile,              # type: bool
    operator_id=None,       # type: str
):
    # type: (...) -> None

    print_customer_receipt_response = None
    tenders = get_electronic_tenders(order=order)
    order_interactor = OrderInteractor(pos_id=pos_id)
    printer_interactor = PrinterInteractor(
        pos_id=pos_id,
        mb_context=mbcontext,
    )
    for tender in tenders:
        tender_amount = float(tender.get("amount"))
        if tender_amount == 0:
            continue

        cancel_eft_data = None
        if is_electronic_payment(tender):
            cancel_eft_data = _cancel_eft_tender_with_retry(
                pos_id=pos_id,
                order=order,
                tender=tender,
                operator_id=operator_id,
            )

            if cancel_eft_data is not None:
                print_customer_receipt_response = printer_interactor.print_tef_tender_receipt(
                    is_mobile=is_mobile,
                    order=order,
                    tender_detail=json.loads(cancel_eft_data),
                    print_customer_receipt=print_customer_receipt_response,
                )

        save_cancel_receipt(
            pos_id=pos_id,
            order=order,
            tender_id=tender.get('id'),
            data=cancel_eft_data or '{}',
        )

        order_interactor.clear_order_tender(
            order_tender_id=tender.get("id"),
            order_id=order_id or order.get("orderId"),
        )

    return None


def _cancel_eft_tender(
    pos_id,             # type: str
    order,              # type: eTree.XML
    tender,             # type: eTree.XML
    operator_id,        # type: str
):
    # type: (...) -> Optional[str]

    data, tender_id = _get_cancel_eft_tender_data(
        pos_id=pos_id,
        order=order,
        tender=tender,
        operator_id=operator_id,
    )
    try:
        component_name = get_electronic_payment_component_name(pos_id=pos_id)
        if component_name is None:
            return None

        timeout = 600000000
        token = TK_ELECTRONIC_PAYMENT_CANCEL_PAYMENT_ORDER
        result = mbcontext.MB_EasySendMessage(component_name, token, FM_PARAM, data, timeout)
        if result.token == TK_SYS_ACK:
            return result.data

        logger.error("Error cancel payment: {} - Tender: {}".format(result.data, tender_id))
    except Exception as _:
        logger.exception("Error cancel payment: Tender: {}".format(tender_id))
        show_messagebox(
            posid=pos_id,
            message="$ERROR_CANCEL_ELECTRONIC_PAYMENTS_ORDER",
        )

    return None


def _cancel_eft_tender_with_retry(
    pos_id,             # type: str
    order,              # type: eTree.XML
    tender,             # type: eTree.XML
    operator_id,        # type: str
    retry_count=1,      # type: Optional[int]
):
    # type: (...) -> Optional[str]

    for attempt in range(retry_count + 1):
        cancel_response = _cancel_eft_tender(
            pos_id=pos_id,
            order=order,
            tender=tender,
            operator_id=operator_id,
        )

        if cancel_response is not None:
            return cancel_response

        if attempt >= retry_count:
            show_messagebox(
                posid=pos_id,
                message="$ERROR_CANCEL_ELECTRONIC_PAYMENTS_ORDER",
                timeout=5000,
            )
            return None

        response = show_message_options_dialog(
            pos_id=pos_id,
            buttons="$CONTINUE_REFUND|$TRY_AGAIN_REFUND",
            title="$INFORMATION",
            message="$ERROR_CANCEL_EFT_TRANSACTION_RETRY",
            timeout=60000,
        )
        if response in [0, None, '']:
            return None


def _get_cancel_eft_tender_data(
    pos_id,             # type: str
    order,              # type: eTree.XML
    tender,             # type: eTree.XML
    operator_id         # type: str
):
    # type: (...) -> Tuple[str, str]

    is_table_service = order.find("CustomOrderProperties/OrderProperty/[@key='TABLE_ID']")
    default_tender_id = tender.get("tenderId") or tender.get("id", "")
    tender_id = "" if is_table_service is None else default_tender_id
    tender_type = tender.get("type") or tender.get("tenderType")
    tender_amount = float(tender.get("amount") or tender.get("tenderAmount"))
    detail = tender.get("detail") or json.loads(tender.get("tenderDetail", "{}"))
    last_totaled = order.findall("StateHistory/State[@state='TOTALED']")[-1]
    last_totaled_date = last_totaled.get("timestamp").replace("-", "").replace(":", "")
    electronic_type = int(tender.get("electronicType") or tender.get("eletronicType"))

    data = json.dumps(
        {
            "posId": pos_id,
            "orderId": order.get("orderId"),
            "tenderId": tender_id,
            "tenderType": tender_type,
            "tenderAmount": tender_amount,
            "fiscalDate": last_totaled_date[:8],
            "fiscalTime": last_totaled_date[9:15],
            "tenderDetail": detail,
            "operatorId": operator_id or "",
            "electronicType": electronic_type
        }
    )

    return data, default_tender_id


