import json
import logging
import base64

import sysactions
from bustoken import TK_SITEF_FINISH_PAYMENT, TK_SITEF_NOT_ALL_PAYMENTS_REFUNDED, TK_SITEF_REFUND_CANCELED
from msgbus import FM_PARAM, TK_SYS_ACK
from sysactions import mbcontext

logger = logging.getLogger("TableActions")


def finish_eft_transactions(pos_id, order, status):
    totaled_states = order.findall("StateHistory/State[@state='TOTALED']")
    if len(totaled_states) == 0:
        return

    model = sysactions.get_model(pos_id)
    component_name = sysactions.get_used_service(model, "eft")
    if component_name is None:
        return

    last_totaled = totaled_states[-1]
    last_totaled_date = last_totaled.get("timestamp").replace("-", "").replace(":", "")

    order_id = order.get("orderId")
    fiscal_date = last_totaled_date[:8]
    fiscal_time = last_totaled_date[9:15]

    status = status if has_electronic_payment(order) else "0"
    tenders = get_electronic_tenders(order)
    tender_details_str = base64.b64encode(json.dumps(tenders))

    data = ";".join([str(pos_id), order_id, fiscal_date, fiscal_time, status, tender_details_str])
    for try_num in range(1, 4):
        try:
            msg = "[finish_eft_transactions] Trying to send a finish message. Order #: {}; Try #: {}; STATUS #: {}"
            logger.info(msg.format(order_id, try_num, status))
            ten_minutes_timeout = 600000000
            ret = mbcontext.MB_EasySendMessage(
                component_name,
                TK_SITEF_FINISH_PAYMENT,
                FM_PARAM,
                data,
                ten_minutes_timeout
            )
            if ret.token == TK_SYS_ACK:
                logger.info("[finish_eft_transactions] Success finished eft transaction. Order #: {}".format(order_id))
                return
            elif ret.token == TK_SITEF_NOT_ALL_PAYMENTS_REFUNDED:
                sysactions.show_messagebox(pos_id, "$SOME_PAYMENTS_WERE_NOT_REFUNDED")
            elif ret.token == TK_SITEF_REFUND_CANCELED:
                raise sysactions.StopAction()
            else:
                message = "[finish_eft_transactions] ACK not received. Order #: {} - {} - {}"
                logger.info(message.format(order_id, ret.token, ret.data))
                if status == "0":
                    sysactions.show_messagebox(pos_id, "$INTERNAL_ERROR_REFUNDING_ELECTRONIC_PAYMENTS")
                else:
                    sysactions.show_messagebox(pos_id, "$INTERNAL_ERROR_CONFIRMING_ELECTRONIC_PAYMENTS")

        except Exception as ex:
            if isinstance(ex, sysactions.StopAction):
                raise ex
            if status == "0":
                sysactions.show_messagebox(pos_id, "$INTERNAL_ERROR_REFUNDING_ELECTRONIC_PAYMENTS")
            else:
                sysactions.show_messagebox(pos_id, "$INTERNAL_ERROR_CONFIRMING_ELECTRONIC_PAYMENTS")
            msg = "[finish_eft_transactions] Error sending message. Order #: {} / Try #: {} / Status: {}"
            msg = msg.format(order_id, try_num, status)
            if try_num == 3:
                logger.exception(msg, exc_info=True)
            else:
                logger.info(msg)
    else:
        if status == "0":
            sysactions.show_messagebox(pos_id, "$ERROR_REFUNDING_ELECTRONIC_PAYMENTS_MANUAL_PROCESS_NEEDED")
        else:
            sysactions.show_messagebox(pos_id, "$ERROR_CONFIRMING_ELECTRONIC_PAYMENTS_MANUAL_PROCESS_NEEDED")


def has_electronic_payment(order):
    if not order:
        return False

    tenders = order.findall("TenderHistory/Tender")
    for tender in tenders:
        if int(tender.get("eletronicType")) not in (0, 9):
            return True

    return False


def get_electronic_tenders(order):
    tenders = order.findall("TenderHistory/Tender")
    ret_tenders = []
    for tender in tenders:
        if int(tender.get("eletronicType")) in (1, 2):
            ret_tenders.append({
                "id": int(tender.get("tenderId")),
                "type": int(tender.get("tenderType")),
                "amount": float(tender.get("tenderAmount")),
                "electronicType": int(tender.get("eletronicType")),
                "detail": json.loads(tender.get("tenderDetail", "{}"))
            })

    return ret_tenders
