import json
import logging
import sysactions

from bustoken import (
    TK_ELECTRONIC_PAYMENT_CACHE,
    TK_ELECTRONIC_PAYMENT_REPRINT_RECEIPT,
)

from msgbus import (
    FM_PARAM,
    TK_SYS_ACK,
    MBMessage,
    MBException,
)
from sysactions import mbcontext
from xml.etree import cElementTree as eTree
from application.interactor import OrderInteractor
from typing import (
    Dict,
    Optional,
    List,
)
from actions.mobile import use_function_mobile_reprint
from actions.eft import get_electronic_payment_component_name
from tef import PrintReceipt
from utilfunctions import get_order_tender

logger = logging.getLogger("PosActions")


def set_eft_cache(
    pos_id,             # type: str
    payment_data,       # type: Dict
    tender_id,          # type: int
    order_id,           # type: int
):
    # type: (...) -> None

    try:
        receipt_customer = payment_data.get("ReceiptCustomer", None)
        if receipt_customer is None:
            return

        receipt_merchant = payment_data.get("ReceiptMerchant", "")

        receipt_data = {
            "order_id": order_id,
            "tender_id": tender_id,
            "receipt_customer": receipt_customer,
            "receipt_merchant": receipt_merchant,
        }
        receipt_data = json.dumps(receipt_data)

        data = json.dumps(
            {
                "posId": pos_id,
                "eftProcessor": payment_data.get("TransactionProcessor"),
                "receiptData": receipt_data,
            }
        )
        response = _send_message(
            pos_id=pos_id,
            data=data,
        )
        if response.token != TK_SYS_ACK:
            message = "NACK reprint last eft receipt. {} - {} - {}"
            logger.info(message.format(pos_id, response.token, response.data))

    except (Exception,):
        logger.info("Error set last eft receipt")


@sysactions.action
def reprint_last_eft_receipt(
    pos_id,                     # type: str
    eft_processor="",           # type: Optional[str]
    fiscalization_type="",      # type: Optional[str]
    error_title="$EFT",         # type: Optional[str]
):
    # type: (...) -> None

    error_message = "$ERROR_REPRINT_TEF_RECEIPT"
    eft_check = _check_eft_value(
        pos_id=pos_id,
        eft_value=eft_processor,
        error_message=error_message,
        error_title=error_title,
    )
    if not eft_check:
        return

    try:
        use_mobile_device = use_mobile_device_reprint_tef_receipt(
            pos_id=pos_id,
            eft_processor=eft_processor,
            fiscalization_type=fiscalization_type,
        )

        if use_mobile_device:
            receipts = get_receipt_from_mobile_device(
                pos_id=pos_id,
                error_message=error_message,
                eft_processor=eft_processor,
            )
        else:
            receipts = _get_receipt_from_cache(
                pos_id=pos_id,
                eft_processor=eft_processor,
                error_message=error_message,
            )

        reprint_receipt(
            pos_id=pos_id,
            receipts=receipts,
            fiscalization_type=fiscalization_type,
            print_reprint_text=not use_mobile_device,
        )
    except (MBException,):
        logger.exception("Error reprint last eft receipt: PosId/Processor/FiscalType {}/{}/{}".format(
            pos_id,
            eft_processor,
            fiscalization_type
        ))
        sysactions.show_messagebox(
            posid=pos_id,
            message=error_message,
            title="$EFT",
        )
    except (Exception,):
        logger.info("Error reprint last eft receipt")


def use_mobile_device_reprint_tef_receipt(
    pos_id,                 # type: str
    eft_processor,          # type: str
    fiscalization_type,     # type: str
):
    # type: (...) -> bool

    if eft_processor.upper() != "SITEF":
        return False

    if not fiscalization_type.startswith("APOS"):
        return False

    return use_function_mobile_reprint(pos_id=pos_id)


def reprint_receipt(
    pos_id,                     # type: str
    receipts,                   # type: List[str]
    fiscalization_type="",      # type: str
    print_reprint_text=True,    # type: bool
):
    # type: (...) -> bool

    reprinted = True
    for receipt in receipts:
        if not receipt:
            reprinted = False
            continue

        printer_receipt = PrintReceipt(
            mb_context=mbcontext,
            logger=logger,
        )

        reprinted &= printer_receipt.reprint_receipt(
            pos_id=pos_id,
            receipts=[receipt],
            fiscalization_type=fiscalization_type,
            print_reprint_text=print_reprint_text,
        )

    return reprinted


def get_receipt_from_mobile_device(
    pos_id,                  # type: str
    error_message,           # type: str
    tender=None,             # type: Optional[Dict[str, str]]
    eft_processor=None,      # type: Optional[str]
):
    # type: (...) -> Optional[List[str]]

    data = _get_reprint_data(
        pos_id=pos_id,
        tender=tender,
        eft_processor=eft_processor,
    )

    if data is None:
        sysactions.show_messagebox(
            posid=pos_id,
            title="$EFT",
            message=error_message,
        )
        return

    response = _send_message(
        pos_id=pos_id,
        data=data,
        token=TK_ELECTRONIC_PAYMENT_REPRINT_RECEIPT,
    )

    receipts = _parse_response_from_get_receipt(
        pos_id=pos_id,
        response=response,
        error_message=error_message,
    )

    return receipts


def _get_receipt_from_cache(
    pos_id,                 # type: str
    eft_processor,          # type: str
    error_message,          # type: str
):
    # type: (...) -> Optional[List[str]]

    response = _get_json_receipt_cache(
        pos_id=pos_id,
        eft_processor=eft_processor,
    )

    receipts = _parse_response_from_get_receipt(
        pos_id=pos_id,
        response=response,
        error_message=error_message,
    )
    return receipts


def _send_message(
    pos_id,                                 # type: str
    data,                                   # type: str
    token=TK_ELECTRONIC_PAYMENT_CACHE,      # type: Optional[int]
):
    # type: (...) -> MBMessage

    sitef_comp_name = get_electronic_payment_component_name(pos_id=pos_id)
    return mbcontext.MB_EasySendMessage(
        dest_name=sitef_comp_name,
        token=token,
        format=FM_PARAM,
        data=data,
        timeout=600000000,
    )


def _check_eft_value(
    pos_id,                 # type: str
    eft_value,              # type: str
    error_message,          # type: str
    error_title="#$EFT",    # type: Optional[str]
):
    # type (...) -> bool

    if eft_value in ['', None]:
        sysactions.show_messagebox(
            posid=pos_id,
            message=error_message,
            title=error_title,
            buttons="$OK",
        )
        return False

    return True


def _parse_response_from_get_receipt(
    pos_id,             # type: str
    response,           # type: MBMessage
    error_message,      # type: str
):
    # type: (...) -> Optional[List[str]]

    receipts = []
    if response.token != TK_SYS_ACK or response.data in ["", None]:
        message = "NACK reprint eft receipt. {} - {} - {}"
        logger.info(message.format(pos_id, response.token, response.data))
        sysactions.show_messagebox(
            posid=pos_id,
            title="$EFT",
            message=error_message,
        )
        return receipts

    response_dict = json.loads(response.data)
    receipt_customer = response_dict.get("receipt_customer", None)
    receipt_merchant = response_dict.get("receipt_merchant", None)

    if not receipt_customer and not receipt_merchant:
        return receipts

    if receipt_customer:
        receipts.append(receipt_customer)

    if receipt_merchant:
        receipts.append(receipt_merchant)

    return receipts


def _get_reprint_data(
    pos_id,                 # type: str
    tender=None,            # type: Optional[Dict[str, str]]
    eft_processor=None,     # type: Optional[str]
):
    # type: (...) -> Optional[str]

    is_reprint_last_receipt = tender is None
    if tender is None:
        tender = _get_tender_from_cache(
            pos_id=pos_id,
            eft_processor=eft_processor,
        )

    if tender is None:
        return None

    reprint_date = tender.get("timestamp").replace("-", "").replace(":", "")
    return json.dumps(
        {
            "tenderDetail": tender.get("tenderDetail", "{}"),
            "reprintDate": reprint_date[:8],
            "reprintTime": reprint_date[9:15],
            "reprintType": "last" if is_reprint_last_receipt else ""
        }
    )


def _get_tender_from_cache(
    pos_id,                 # type: str
    eft_processor=None,     # type: Optional[str]
):
    # type: (...) -> Optional[eTree.Element]

    response = _get_json_receipt_cache(
        pos_id=pos_id,
        eft_processor=eft_processor,
    )

    if not response:
        return None

    order_picture = OrderInteractor(pos_id=pos_id).get_order_from_order_picture(
        order_id=response.get("order_id", ""),
        use_order_id_model=False,
    )
    return get_order_tender(
        order_picture=order_picture,
        tender_id=str(response.get("tender_id", "")),
    )


def _get_json_receipt_cache(
    pos_id,             # type: str
    eft_processor,      # type: str
):
    # type: (...) -> Optional[Dict[str, str]]

    data = json.dumps(
        {
            "posId": pos_id,
            "eftProcessor": eft_processor,
        }
    )
    response = _send_message(
        pos_id=pos_id,
        data=data,
    )

    if response.token != TK_SYS_ACK or response.data in ["", None]:
        return None

    return json.loads(response.data)
