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 sysactions import (
    get_posot,
    mbcontext,
    show_messagebox,
)
from typing import (
    Optional,
    Tuple
)

from actions.eft import (
    get_electronic_payment_component_name,
    has_electronic_payment,
    get_electronic_tenders,
    save_cancel_receipt
)
from application.interactor import OrderInteractor

logger = logging.getLogger("EftActions")


def cancel_eft_transactions(
    pos_id,                     # type: str
    model,                      # type: eTree.XML
    order_id,                   # type: str
    tender=None,                # type: Optional[eTree.XML]
    is_table_service=False,     # type: bool
    is_mobile=False,            # type: bool
    operator_id=None,           # type: Optional[str]
    order=None,                 # type: eTree.Element
):
    # type: (...) -> bool

    if order is None:
        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

    if not has_electronic_payment(order):
        return False

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

    if tender is not None:
        return _cancel_eft_tender(
            pos_id,
            order,
            tender,
            is_table_service,
            is_mobile,
            operator_id
        )

    results = []
    tenders = get_electronic_tenders(order)
    order_interactor = OrderInteractor(pos_id)
    for tender in tenders:
        tender_amount = float(tender.get("amount"))
        if tender_amount == 0:
            continue

        result = _cancel_eft_tender(
            pos_id,
            order,
            tender,
            is_table_service,
            is_mobile,
            operator_id
        )
        results.append(result)
        order_interactor.clear_order_tender(
            order_tender_id=tender.get("id"),
            order_id=order_id or order.get("orderId"),
        )

    return all(results)


def _cancel_eft_tender(
    pos_id,             # type: str
    order,              # type: eTree.XML
    tender,             # type: eTree.XML
    is_table_service,   # type: bool
    is_mobile,          # type: bool
    operator_id         # type: str
):
    # type: (...) -> bool

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

        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:
            if is_mobile:
                return True

            success_update_tenders = save_cancel_receipt(
                pos_id,
                order,
                tender_id,
                result.data
            )
            if success_update_tenders:
                return True

        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(pos_id, "$ERROR_CANCEL_ELECTRONIC_PAYMENTS_ORDER")

    return False


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

    default_tender_id = tender.get("tenderId") or tender.get("id", "")
    tender_id = default_tender_id if is_table_service else ""
    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
