import base64
import json
from xml.etree import cElementTree as eTree

import sysactions
from helper import remove_accents
from modelcommon.printer import SeparatorTag
from msgbus import (
    FM_PARAM,
    TK_PRN_PRINT,
    TK_SYS_ACK,
)
from sysactions import (
    get_storewide_config,
    get_model,
    get_current_order,
    show_confirmation,
    get_podtype,
)
from typing import (
    Dict,
    Optional,
    List,
)

from constants import (
    TXT_BOLD_ON,
    CTL_LF,
    TXT_BOLD_OFF,
    TXT_ALIGN_CT,
    TXT_FONT_A,
    TXT_2WIDTH,
    TXT_2HEIGHT,
    HW_INIT,
    TXT_NORMAL,
    TXT_ALIGN_LT,
)

from utilfunctions import (
    get_device_service,
    is_table_service,
)


print_merchant_receipt = None
print_customer_receipt = None
ask_print_customer_receipt = None


class PrintReceipt(object):
    def __init__(self, mb_context, logger):
        global print_merchant_receipt
        global print_customer_receipt
        global ask_print_customer_receipt

        self.mb_context = mb_context
        self.logger = logger

        if print_merchant_receipt is None:
            print_merchant_receipt = self._get_store_config("Store.PrintMerchantReceipt", "False")
        self.print_merchant_receipt = print_merchant_receipt

        if print_customer_receipt is None:
            print_customer_receipt = self._get_store_config("Store.PrintCustomerReceipt", "False")
        self.print_customer_receipt = print_customer_receipt

        if ask_print_customer_receipt is None:
            ask_print_customer_receipt = self._get_store_config("Store.AskPrintCustomerReceipt", "True")
        self.ask_print_customer_receipt = ask_print_customer_receipt

    @staticmethod
    def _get_store_config(
        config,         # type: str
        default_value,  # type: str
    ):
        # type: (...) -> bool

        return get_storewide_config(config, defval=default_value).lower() == "true"

    def print_tef_receipts(
        self,
        pos_id,                     # type: str
        fiscalization_type="",      # type: Optional[str]
        order_id=None,              # type: Optional[str]
    ):
        # type: (...) -> None

        try:
            model = get_model(pos_id)
            if not order_id:
                order_id = get_current_order(model).get("orderId")

            order_taker = sysactions.get_posot(model)
            order_pict = order_taker.orderPicture(orderid=order_id)
            order = eTree.XML(order_pict).find("Order")
            tenders = order.findall("TenderHistory/Tender")
            print_customer_receipt_response = None
            sector_name = None
            sector = order.find(".//CustomOrderProperties/OrderProperty[@key='{}']".format("SECTOR"))
            if sector is not None:
                sector_name = sector.get("value", None)

            total_electronic_tenders = 1
            for tender in tenders:
                if int(tender.get("eletronicType")) not in (0, 9):

                    if float(tender.get("tenderAmount")) <= 0:
                        continue

                    tender_detail = json.loads(tender.get("tenderDetail"))
                    print_customer_receipt_response = self.print_tef_tender_receipt(
                        pos_id=pos_id,
                        tender_detail=tender_detail,
                        fiscalization_type=fiscalization_type,
                        print_customer_receipt=print_customer_receipt_response,
                        sector=sector_name,
                        total_electronic_tenders=total_electronic_tenders,
                    )
                    total_electronic_tenders += 1
        except Exception:  # noqa
            self.logger.exception("Error printing TEF receipts")

    def print_tef_tender_receipt(
        self,
        pos_id,                         # type: str
        tender_detail,                  # type: Dict
        fiscalization_type="",          # type: Optional[str]
        print_customer_receipt=None,    # type: Optional[bool]
        sector=None,                    # type: str
        total_electronic_tenders=1,     # type: Optional[int]
    ):
        # type: (...) -> Optional[bool]

        has_merchant_receipt = "ReceiptMerchant" in tender_detail and tender_detail["ReceiptMerchant"] not in ['', None]
        has_customer_receipt = "ReceiptCustomer" in tender_detail and tender_detail["ReceiptCustomer"] not in ['', None]

        if has_merchant_receipt or has_customer_receipt:
            try:
                model = get_model(pos_id)
                pod_type = get_podtype(model)
                is_table = is_table_service(model=model)
                current_printer = get_device_service(
                    pos_id=pos_id,
                    device_type="printer",
                    sector=sector,
                )

                if has_merchant_receipt and self.print_merchant_receipt and pod_type != "TT":
                    self.print_tender_receipt(
                        pos_id=pos_id,
                        current_printer=current_printer,
                        receipt_data=tender_detail,
                        receipt_type="ReceiptMerchant",
                        fiscalization_type=fiscalization_type,
                        is_table=is_table,
                        total_electronic_tenders=total_electronic_tenders,
                    )

                if has_customer_receipt:
                    ignore_confirmation_message = False
                    if self.ask_print_customer_receipt:
                        if print_customer_receipt is None or total_electronic_tenders == 1:
                            print_customer_receipt = show_confirmation(pos_id, "$WANT_TO_PRINT_CUSTOMER_RECEIPT")
                            ignore_confirmation_message = print_customer_receipt
                    else:
                        print_customer_receipt = self.print_customer_receipt

                    if print_customer_receipt:
                        self.print_tender_receipt(
                            pos_id=pos_id,
                            current_printer=current_printer,
                            receipt_data=tender_detail,
                            receipt_type="ReceiptCustomer",
                            fiscalization_type=fiscalization_type,
                            is_table=is_table,
                            total_electronic_tenders=total_electronic_tenders,
                            ignore_confirmation_message=ignore_confirmation_message,
                        )
            except Exception:  # noqa
                self.logger.exception("Error printing TEF receipt for tender")

            return print_customer_receipt

    def print_tender_receipt(
        self,
        pos_id,                                 # type: str
        current_printer,                        # type: str
        receipt_data,                           # type: Dict | str
        receipt_type=None,                      # type: Optional[str]
        fiscalization_type="",                  # type: Optional[str]
        reprint_text=None,                      # type: Optional[str]
        is_table=False,                         # type: Optional[bool]
        total_electronic_tenders=1,             # type: Optional[int]
        ignore_confirmation_message=False,      # type: Optional[bool]
    ):
        # type: (...) -> bool

        if fiscalization_type.startswith("APOS") and not is_table and not ignore_confirmation_message:
            self._show_confirmation_message(
                pos_id=pos_id,
                receipt_type=receipt_type,
                total_electronic_tenders=total_electronic_tenders,
            )

        receipt_data = receipt_data if receipt_type is None else receipt_data[receipt_type]
        receipt = self._decode_receipt(
            receipt_data=receipt_data,
            fiscalization_type=fiscalization_type,
            reprint_text=reprint_text,
        )

        resp = self.mb_context.MB_EasySendMessage(
            dest_name=current_printer,
            token=TK_PRN_PRINT,
            format=FM_PARAM,
            data=receipt,
            timeout=30000000,
        )

        return resp.token == TK_SYS_ACK

    def reprint_receipt(
        self,
        pos_id,                     # type: str
        receipts,                   # type: List[str]
        model=None,                 # type: Optional[eTree]
        fiscalization_type="",      # type: str
        print_reprint_text=True,    # type: bool
    ):

        # type: (...) -> bool

        if model is None:
            model = sysactions.get_model(posid=pos_id)

        current_printer = get_device_service(
            pos_id=pos_id,
            device_type="printer",
        )

        reprint_text = ""
        if print_reprint_text:
            reprint_text = sysactions.translate_message(
                model=model,
                message_key="REPRINT_TEXT_MESSAGE",
            )

        reprinted = True
        for receipt in receipts:
            reprinted &= self.print_tender_receipt(
                pos_id=pos_id,
                current_printer=current_printer,
                receipt_data=receipt,
                fiscalization_type=fiscalization_type,
                reprint_text=reprint_text,
            )

        return reprinted

    @staticmethod
    def _show_confirmation_message(
        pos_id,                         # type: str
        receipt_type,                   # type: str
        total_electronic_tenders,       # type: int
    ):
        # type: (...) -> None

        if total_electronic_tenders > 1 or receipt_type == "ReceiptMerchant":
            customer_message = "$CONFIRMATION_MESSAGE_PRINT_RECEIPT_CUSTOMER"
            merchant_message = "$CONFIRMATION_MESSAGE_PRINT_RECEIPT_MERCHANT"
            merchant_next_message = "$CONFIRMATION_MESSAGE_NEXT_PRINT_RECEIPT_MERCHANT"

            is_next_message_merchant = total_electronic_tenders > 1 and receipt_type == "ReceiptMerchant"
            merchant_message = merchant_next_message if is_next_message_merchant else merchant_message

            message = customer_message if receipt_type == "ReceiptCustomer" else merchant_message

            show_confirmation(
                posid=pos_id,
                message=message,
                buttons="$OK",
            )

    def _decode_receipt(
        self,
        receipt_data,           # type: str
        fiscalization_type,     # type: str
        reprint_text,           # type: str
    ):
        # type: (...) -> str

        receipt_data = base64.b64decode(receipt_data)

        if reprint_text not in ['', None]:
            reprint_text = self._get_reprint_text(
                reprint_text=reprint_text,
                fiscalization_type=fiscalization_type,
            )

            reprint_text = remove_accents(
                text=reprint_text,
            )

            receipt_data = "{}{}{}".format(
                receipt_data,
                CTL_LF,
                reprint_text,
            )

        append_cut_command = not fiscalization_type or not fiscalization_type.startswith("APOS")
        cut_tag = b"{}SpaceCutReceiptCard{}"
        space_cut_receipt_card = cut_tag.format(SeparatorTag.start, SeparatorTag.end) if append_cut_command else ""
        receipt = "{}{}{}".format(
            TXT_ALIGN_CT,
            receipt_data,
            space_cut_receipt_card,
        )

        return receipt

    @staticmethod
    def _get_reprint_text(
        reprint_text,            # type: str
        fiscalization_type,      # type: str
    ):
        # type: (...) -> str

        if fiscalization_type.startswith("APOS"):
            max_width = 38
            pre_space = int((max_width - len(reprint_text)) / 2)
            post_space = max_width - len(reprint_text) - pre_space
            return (" " * pre_space) + reprint_text + (" " * post_space)

        rollback_printer_default_font = "{}{}{}{}{}".format(
            HW_INIT,
            TXT_NORMAL,
            TXT_BOLD_OFF,
            TXT_FONT_A,
            TXT_ALIGN_LT,
        )

        return b"{}{}{}{}{}{}{}".format(
            CTL_LF,
            TXT_2HEIGHT,
            TXT_2WIDTH,
            TXT_BOLD_ON,
            reprint_text,
            CTL_LF,
            rollback_printer_default_font,
        )
