import iso8601
import datetime
import base64

from xml.etree import ElementTree as eTree
from common import TipBuilderCommon
from helper import (
    remove_accents,
    custom_encode_cp860,
)
from old_helper import (
    remove_xml_namespace,
    round_half_away_from_zero,
)
from sysactions import translate_message
from typing import (
    Dict,
    Optional,
    Tuple,
)
from models import LoyaltyConfigurations


class PrinterCommon(object):
    def __init__(
        self,
        tip_config,                 # type: Dict
        tip_config_range,           # type: Dict
        tip_builder_common,         # type: TipBuilderCommon
        customer_call_by_field,     # type: str
    ):
        # type: (...) -> None

        self.tip_config = tip_config
        self.tip_config_range = tip_config_range
        self.tip_builder_common = tip_builder_common
        self.customer_call_by_field = customer_call_by_field

    def get_tip_order(
        self,
        order,              # type: eTree
        is_mobile=False     # type: bool
    ):
        # type: (...) -> str

        if len(self.tip_config_range) == 0 and len(self.tip_config) == 0:
            return ""

        order_tip = float(order.get("tip"))
        if len(self.tip_config_range) == 0 and len(self.tip_config) > 0:
            return self._get_tip_line(self.tip_config['TipCode'], self.tip_config['TipName'], order_tip, is_mobile)

        report = ""

        total_gross = float(order.get('totalGross'))
        tip_ranges = self.tip_builder_common.get_percentage_by_tip(total_gross, order_tip)
        for key, value in tip_ranges.items():
            report += self._get_tip_line(key, value.get("TipName"), value.get("TipValue"), is_mobile)

        return report

    def get_coupon_call_text(
        self,
        model,  # type: eTree.Element
    ):
        # type: (...) -> str

        if self.customer_call_by_field:
            call_by = custom_encode_cp860(
                text=self.customer_call_by_field,
            )
            coupon_call_text = "{}".format(call_by)
        else:
            panel_call_text = custom_encode_cp860(
                text=translate_message(
                    model=model,
                    message_key="CALL_PANEL",
                ),
            )
            will_be_called_text = custom_encode_cp860(
                text=translate_message(
                    model=model,
                    message_key="YOU_WILL_BE_CALLED",
                ),
            )

            coupon_call_text = "{} - {}".format(
                panel_call_text,
                will_be_called_text,
            )

        return coupon_call_text

    @staticmethod
    def value_tip_by_percentage(percent, order_value):
        # type: (float, float) -> float

        return (percent * order_value) / 100.0

    @staticmethod
    def _get_tip_line(
        code,           # type: str
        description,    # type: str
        value,          # type: float
        is_mobile       # type: bool
    ):
        # type: (...) -> str

        format_mobile = "%-7.7s|%-14.14s|%6.3f|%-3.2s|%9.2f|"
        format_desktop = "%-7.7s %-18.18s%6.3f%2.2s%9.02f"
        order_tip_format = format_mobile if is_mobile else format_desktop

        tip_line = remove_accents(order_tip_format % (code, description, float(1), "un", value))

        format_mobile = "%-39.40s|%7.02f\n"
        format_desktop = "%-46.46s %9.02f\n"
        order_tip_format = format_mobile if is_mobile else format_desktop

        return order_tip_format % (tip_line, value)

    @staticmethod
    def get_coupon_call_id(
        order,                      # type: eTree.Element
        order_ref_ids,              # type: list
        customer_name_limit=None,   # type: Optional[int]
    ):
        # type: (...) -> Optional[str]

        if order_ref_ids[0] == "HIDE":
            return None

        pager_number = order.find("CustomOrderProperties/OrderProperty[@key='PAGER_NUMBER']")
        if pager_number is not None:
            pager_number = pager_number.get("value")

        customer_name = order.find("CustomOrderProperties/OrderProperty[@key='CUSTOMER_NAME']")
        if customer_name is not None:
            customer_name = customer_name.get("value")

        for ref in order_ref_ids:
            if pager_number and ref == "PAGER_NUMBER":
                return pager_number
            elif customer_name and ref == "CUSTOMER_NAME":
                if customer_name_limit:
                    customer_name = customer_name[:customer_name_limit]
                return custom_encode_cp860(text=customer_name)
            elif ref == "ORDER_ID":
                return order.get('orderId')[-3:]

        return None

    @staticmethod
    def get_sat_params_to_print_loyalty_coupon(
        fiscal_request,     # type: eTree.Element
        order,              # type: eTree.Element
    ):
        # type: (...) -> Tuple[datetime, str]

        fiscal_request_xml = eTree.XML(base64.b64decode(fiscal_request.text.strip()))
        date = fiscal_request_xml.find(".//dEmi").text
        hour = fiscal_request_xml.find(".//hEmi").text
        protocol_date = datetime.datetime.strptime(
            "{}{}".format(
                date,
                hour,
            ),
            "%Y%m%d%H%M%S",
        )
        sat_id = fiscal_request_xml.find(".//infCFe").get("Id")[3:]
        qr_code_text = "{}|{:.2f}".format(
            sat_id,
            round_half_away_from_zero(
                number=order.get("totalTender"),
                precision=2,
            )
        )

        return protocol_date, qr_code_text

    @staticmethod
    def get_nfce_params_to_print_loyalty_coupon(
        fiscal_request,     # type: eTree.Element
        order,              # type: eTree.Element
    ):
        # type: (...) -> Tuple[datetime, str]

        fiscal_request_xml = remove_xml_namespace(
            eTree.tostring(
                element=fiscal_request,
                encoding='utf8',
                method='xml',
            ),
        )
        protocol_date = iso8601.parse_date(fiscal_request_xml.find(".//dhEmi").text)
        nfce_id = fiscal_request_xml.find(".//infNFe").get("Id")[3:]
        qr_code_text = "{}|{:.2f}".format(
            nfce_id,
            round_half_away_from_zero(
                number=order.get("totalTender"),
                precision=2,
            )
        )

        return protocol_date, qr_code_text

    @staticmethod
    def should_print_loyalty_coupon(
        order,                   # type: eTree.Element
        loyalty_configurations,  # type: LoyaltyConfigurations
    ):
        # type: (...) -> bool

        if not loyalty_configurations.print_loyalty_coupon_after_fiscalization:
            return False

        has_loyalty_id = order.find("CustomOrderProperties/OrderProperty[@key='LOYALTY_ID']") is not None
        if not has_loyalty_id:
            return True

        return False
