# -*- coding: utf-8 -*-
import datetime
import json
import sysactions

from plistlib import Dict
from _AposA8PrintLine import AposA8PrintLine
from _AposA8Align import AposA8Align
from _AposA8CommandType import AposA8CommandType
from _AposA8FiscalPrinter import AposA8FiscalPrinter
from common import PrinterCommon
from dateutil import tz
from msgbus import MBEasyContext
from _AposA8Helpers import (
    APOS_DASH,
    APOS_NEWLINE,
    append_tax_data,
    split_lines,
    append_lines_from_array,
    build_call_id,
)
from xml.etree import cElementTree as eTree

from old_helper import remove_xml_namespace
from clichehelper import ClicheHelper
from typing import (
    Optional,
    List,
)

from _AposA8FontSize import AposA8FontSize
from models import LoyaltyConfigurations


class NfceAposA8FiscalPrinter(AposA8FiscalPrinter):
    def __init__(
        self,
        mb_context,                    # type: MBEasyContext
        order_ref_ids,                 # type: list
        cliche_helper,                 # type: ClicheHelper
        qrcode_url,                    # type: str
        customer_text_field,           # type: str
        printer_common,                # type: PrinterCommon
        coupon_fiscal_parameters,      # type: Dict[str, bool]
        font_size_withdrawal_panel,    # type: str
        danfe_font_size_mobile,        # type: str
        order_ref_position,            # type: str
        loyalty_configurations,        # type: LoyaltyConfigurations
    ):
        # type: (...) -> None

        super(NfceAposA8FiscalPrinter, self).__init__(
            mb_context=mb_context,
            order_ref_ids=order_ref_ids,
            cliche_helper=cliche_helper,
            customer_text_field=customer_text_field,
            coupon_fiscal_parameters=coupon_fiscal_parameters,
            font_size_withdrawal_panel=font_size_withdrawal_panel,
            danfe_font_size_mobile=danfe_font_size_mobile,
            order_ref_position=order_ref_position,
            loyalty_configurations=loyalty_configurations,
        )

        self.qrcode_check_url = qrcode_url
        self.satisfaction_survey_text = None
        self.customer_text_field = customer_text_field
        self.printer_common = printer_common
        self.danfe_font_size_mobile = danfe_font_size_mobile

    def print_fiscal(
        self,               # type: NfceAposA8FiscalPrinter
        pos_id,             # type: str
        order,              # type: eTree.Element
        tenders,            # type: list
        fiscal_request,     # type: eTree.Element
        fiscal_response,    # type: eTree.Element
        total_taxes,        # type: dict
        url_sefaz="",       # type: str
        is_reprint=False,   # type: Optional[bool]
    ):
        # type: (...) -> str

        model = sysactions.get_model(pos_id)
        fiscal_xml = remove_xml_namespace(eTree.tostring(fiscal_request))
        is_contingency = fiscal_xml.find(".//tpEmis").text == "9"

        if is_reprint and is_contingency and fiscal_response is not None:
            is_contingency = False

        report = []

        if is_contingency and not is_reprint:
            report = self._fiscal_report(
                order=order,
                tenders=tenders,
                fiscal_request=fiscal_request,
                fiscal_response=fiscal_response,
                total_taxes=total_taxes,
                via="Via Estabelecimento",
                url_sefaz=self.qrcode_check_url,
                model=model,
                is_contingency=is_contingency,
            )
            report.append(APOS_NEWLINE)

        via = "Via Consumidor" if is_reprint or is_contingency else ""

        report.extend(
            self._fiscal_report(
                order=order,
                tenders=tenders,
                fiscal_request=fiscal_request,
                fiscal_response=fiscal_response,
                total_taxes=total_taxes,
                via=via,
                url_sefaz=self.qrcode_check_url,
                model=model,
                is_contingency=is_contingency,
            )
        )

        print_loyalty_coupon = PrinterCommon.should_print_loyalty_coupon(
            order=order,
            loyalty_configurations=self.loyalty_configurations,
        )
        if print_loyalty_coupon:
            report.extend(
                self._print_loyalty(
                    pos_id=pos_id,
                    order=order,
                    fiscal_request=fiscal_request,
                )
            )

        return json.dumps(report, default=lambda x: x.__dict__)

    def print_cancel(self, pos_id, data):
        # type: (str, str) -> None
        pass

    def _print_loyalty(
        self,                    # type: AposA8FiscalPrinter
        pos_id,                  # type: str
        order,                   # type: eTree.Element
        fiscal_request,          # type: eTree.Element
    ):
        # type: (...) -> List

        protocol_date, qr_code_text = PrinterCommon.get_nfce_params_to_print_loyalty_coupon(
            fiscal_request=fiscal_request,
            order=order,
        )
        report = self._build_loyalty_report(
            model=sysactions.get_model(posid=pos_id),
            qr_code_text=qr_code_text,
            protocol_date=protocol_date,
        )

        return report

    def _fiscal_report(
        self,
        order,                  # type: eTree.Element
        tenders,                # type: list
        fiscal_request,         # type: eTree.Element
        fiscal_response,        # type: eTree.Element
        total_taxes,            # type: dict
        via,                    # type: str
        url_sefaz,              # type: str
        model=None,             # type: Optional[eTree.Element]
        is_contingency=False,   # type: Optional[bool]
    ):
        # type: (...) -> list

        report = []

        if self.order_ref_position == "BEFORE_FISCAL_DATA":
            build_call_id(
                model=model,
                report=report,
                order=order,
                font_size_withdrawal_panel=self.font_size_withdrawal_panel,
                printer_common=self.printer_common,
                order_ref_ids=self.order_ref_ids,
                position="before",
            )

        self._append_cliche(report=report)

        if is_contingency:
            report.append(APOS_NEWLINE)
            self._append_contingency_warning(report=report)
            report.append(APOS_DASH)

        self._append_coupon_body(
            model=model,
            order=order,
            report=report,
            tenders=tenders,
            printer_common=self.printer_common,
        )

        fiscal_xml = remove_xml_namespace(eTree.tostring(fiscal_request))

        self._append_sefaz_url_and_key(
            report=report,
            fiscal_xml=fiscal_xml,
            url_sefaz=url_sefaz,
        )

        self._append_cpf_cnpj(
            report=report,
            fiscal_xml=fiscal_xml,
        )

        self._append_nfce_data(
            report=report,
            fiscal_xml=fiscal_xml,
        )

        if is_contingency:
            report.append(self._get_text_line(text=via))

        response_xml = None
        if fiscal_response is not None:
            response_xml = remove_xml_namespace(eTree.tostring(fiscal_response))

        self._append_auth_protocol(
            report=report,
            response_xml=response_xml,
        )

        self._append_homolog_warning(
            report=report,
            fiscal_xml=fiscal_xml,
        )

        if is_contingency:
            self._append_contingency_warning(report=report)

        self._append_qr_code(
            report=report,
            fiscal_xml=fiscal_xml,
        )

        self._append_response_protocol_information(
            report=report,
            response_xml=response_xml,
        )

        append_tax_data(
            report=report,
            total_taxes=total_taxes,
            font_size=self.danfe_font_size_mobile,
        )

        self._append_customer_text_field(report=report)

        return report

    def _append_response_protocol_information(
        self,
        report,  # type: List[AposA8PrintLine]
        response_xml,  # type: eTree
    ):
        # type: (...) -> None

        if response_xml is not None:
            message_code_tag = response_xml.find(".//infProt/cMsg")
            message_code = message_code_tag.text if message_code_tag is not None else None
            message_text_tag = response_xml.find(".//infProt/xMsg")
            message_text = message_text_tag.text if message_text_tag is not None else None
            if message_code == '200' and message_text not in [None, '', ' ']:
                text_to_print = message_text.replace('|', '\n').strip()
                report.append(self._get_text_line(text=text_to_print))

    def _append_auth_protocol(
        self,
        report,             # type: List[AposA8PrintLine]
        response_xml,       # type: eTree
    ):
        # type: (...) -> None

        if response_xml is not None:
            protocolo_element = response_xml.find(".//nProt").text
            if protocolo_element is not None:
                auth_protocol_text = "Protocolo de autorizacao: " + protocolo_element
                report.append(self._get_text_line(text=auth_protocol_text))

                protocol_date = response_xml.find(".//dhRecbto").text
                protocol_date_text = "Data de autorizacao: " + self._formata_data(protocol_date)
                report.append(self._get_text_line(text=protocol_date_text))

    def _append_homolog_warning(
        self,
        report,         # type: List[AposA8PrintLine]
        fiscal_xml,     # type: eTree
    ):
        # type: (...) -> None

        tp_amb = fiscal_xml.find(".//tpAmb").text
        if tp_amb == "2":
            text_to_print = ["EMITIDA EM HOMOLOGACAO - SEM VALOR FISCAL"]

            if self.danfe_font_size_mobile != AposA8FontSize.SMALL:
                text_to_print = ["EMITIDA EM HOMOLOGACAO", 'SEM VALOR FISCAL']

            for text in text_to_print:
                report.append(self._get_text_line(text=text))

    @staticmethod
    def _append_qr_code(report, fiscal_xml):
        url = fiscal_xml.find(".//qrCode").text.strip()
        report.append(AposA8PrintLine(AposA8Align.CENTER, url, AposA8CommandType.PRINT_QR_CODE))

    def _append_nfce_data(
        self,
        report,         # type: List[AposA8PrintLine]
        fiscal_xml,     # type: eTree
    ):
        # type: (...) -> None

        numero = fiscal_xml.find(".//nNF").text
        serie = fiscal_xml.find(".//serie").text
        data_emissao_xml = fiscal_xml.find(".//dhEmi").text
        data_emissao_str = self._formata_data(data_emissao_xml)
        nfce_numero = "NFC-e Numero %09d " % (int(numero))
        nfce_serie = "Serie %03d " % int(serie)
        nfce_data_emissao = "%s" % data_emissao_str

        font_is_small = True if self.danfe_font_size_mobile == AposA8FontSize.SMALL else False

        small_lines = ["{}{}".format(nfce_numero, nfce_serie), nfce_data_emissao]
        large_lines = [nfce_numero, "{}{}".format(nfce_serie, nfce_data_emissao)]
        lines = small_lines if font_is_small else large_lines

        append_lines_from_array(
            report=report,
            lines=lines,
            font_size=self.danfe_font_size_mobile,
        )

    def _append_cpf_cnpj(
        self,
        report,         # type: List[AposA8PrintLine]
        fiscal_xml,     # type: eTree
    ):
        # type: (...) -> None

        cpf_cnpj = ""
        cpf = fiscal_xml.find(".//dest/CPF")
        if cpf is not None:
            cpf_cnpj = cpf.text
        else:
            cnpj = fiscal_xml.find(".//dest/CNPJ")
            if cnpj is not None:
                cpf_cnpj = cnpj.text
        cpf_cnpj_text = "CONSUMIDOR %s" % ("NAO INFORMADO" if not cpf_cnpj else ("- CPF " + cpf_cnpj))

        report.append(self._get_text_line(text=cpf_cnpj_text))

    def _append_sefaz_url_and_key(
        self,
        report,		    # type: List[AposA8PrintLine]
        fiscal_xml,     # type: eTree
        url_sefaz,      # type: str
    ):
        # type: (...) -> None

        text_to_print = "Consulte pela Chave de Acesso em"
        report.append(self._get_text_line(text=text_to_print))
        split_lines(
            report=report,
            text=url_sefaz,
            font_size=self.danfe_font_size_mobile,
            separator="/",
        )

        self._append_danfe_key(
            report=report,
            key=fiscal_xml.find(".//infNFe").attrib['Id'][3:],
        )

    def _append_contingency_warning(
        self,
        report,		# type: List[AposA8PrintLine]
    ):
        # type: (...) -> None

        report.append(self._get_text_line(text="EMITIDA EM CONTINGENCIA"))
        report.append(self._get_text_line(text="Pendente de autorizacao"))

    def _append_header(
        self,
        report,		# type: List[AposA8PrintLine]
    ):
        # type: (...) -> None

        text = "Documento Auxiliar da Nota Fiscal de Consumidor Eletronica"
        split_lines(
            report=report,
            text=text,
            font_size=self.danfe_font_size_mobile,
        )
        report.append(APOS_DASH)

    @staticmethod
    def _formata_data(data, mascara="%d/%m/%Y %H:%M:%S"):
        data_emissao_date_utc = data[:19]
        data_emissao_date_utc = datetime.datetime.strptime(data_emissao_date_utc, "%Y-%m-%dT%H:%M:%S")
        data_emissao_offset = int(data[20:22]) * 60 + int(data[23:])
        if data[19] == "+":
            data_emissao_offset *= -1
        data_emissao_date_utc = data_emissao_date_utc + datetime.timedelta(minutes=data_emissao_offset)
        from_zone = tz.tzutc()
        to_zone = tz.tzlocal()

        data_emissao_date = data_emissao_date_utc.replace(tzinfo=from_zone)
        # Convert time zone
        data_emissao_date = data_emissao_date.astimezone(to_zone)
        data_emissao_str = data_emissao_date.strftime(mascara)
        return data_emissao_str
