# -*- 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
from xml.etree import ElementTree as eTree

from old_helper import remove_xml_namespace
from clichehelper import ClicheHelper
from typing import Optional


class NfceAposA8FiscalPrinter(AposA8FiscalPrinter):
    def __init__(
        self,
        mb_context,                    # type: MBEasyContext
        print_customer_name,           # type: bool
        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
    ):
        # type: (...) -> None

        super(NfceAposA8FiscalPrinter, self).__init__(
            mb_context,
            print_customer_name,
            cliche_helper,
            customer_text_field,
            coupon_fiscal_parameters,
            font_size_withdrawal_panel,
        )
        self.qrcode_check_url = qrcode_url
        self.satisfaction_survey_text = None
        self.customer_text_field = customer_text_field
        self.printer_common = printer_common

    def print_fiscal(
            self,
            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
            customer_name,    # type: str
            url_sefaz="",     # type: str
    ):
        # type: (...) -> str

        model = sysactions.get_model(pos_id)
        fiscal_xml = remove_xml_namespace(eTree.tostring(fiscal_request))

        tp_emiss = fiscal_xml.find(".//tpEmis").text

        if tp_emiss == "9":
            report = self._fiscal_report(
                order=order,
                tenders=tenders,
                fiscal_request=fiscal_request,
                fiscal_response=fiscal_response,
                total_taxes=total_taxes,
                via="Via Estabelecimento",
                customer_name=customer_name,
                url_sefaz=self.qrcode_check_url,
                model=model,
            )
            report.append(APOS_NEWLINE)
            report.extend(
                self._fiscal_report(
                    order=order,
                    tenders=tenders,
                    fiscal_request=fiscal_request,
                    fiscal_response=fiscal_response,
                    total_taxes=total_taxes,
                    via="Via Consumidor",
                    customer_name=customer_name,
                    url_sefaz=self.qrcode_check_url,
                    model=model,
                )
            )
        else:
            report = self._fiscal_report(
                order=order,
                tenders=tenders,
                fiscal_request=fiscal_request,
                fiscal_response=fiscal_response,
                total_taxes=total_taxes,
                via="",
                customer_name=customer_name,
                url_sefaz=self.qrcode_check_url,
                model=model,
            )

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

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

    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
            customer_name,    # type: str
            url_sefaz,        # type: str
            model=None,       # type: Optional[eTree.Element]
    ):
        # type: (...) -> list

        fiscal_xml = remove_xml_namespace(eTree.tostring(fiscal_request))

        report = []

        self._append_cliche(report)

        self._append_header(report)

        is_contingency = fiscal_xml.find(".//tpEmis").text == "9"

        if is_contingency:
            self._append_contingency_warning(report)

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

        self._append_sefaz_url_and_key(report, fiscal_xml, url_sefaz)

        self._append_cpf_cnpj(report, fiscal_xml)

        self._append_nfce_data(report, fiscal_xml)

        if is_contingency:
            report.append(AposA8PrintLine(AposA8Align.CENTER, via, AposA8CommandType.PRINT_TEXT))

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

        self._append_auth_protocol(report, response_xml)

        self._append_homolog_warning(report, fiscal_xml)

        if is_contingency:
            report.append(AposA8PrintLine(AposA8Align.CENTER, "EMITIDA EM CONTINGENCIA", AposA8CommandType.PRINT_TEXT))
            report.append(AposA8PrintLine(AposA8Align.CENTER, "Pendente de autorizacao", AposA8CommandType.PRINT_TEXT))

        self._append_qr_code(report, fiscal_xml)

        self._append_response_protocol_information(report, response_xml)

        append_tax_data(report, total_taxes)

        self._append_customer_text_field(report)

        return report

    @staticmethod
    def _append_response_protocol_information(report, response_xml):
        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(AposA8PrintLine(AposA8Align.CENTER, text_to_print, AposA8CommandType.PRINT_TEXT))

    def _append_auth_protocol(self, report, response_xml):
        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(AposA8PrintLine(AposA8Align.CENTER, auth_protocol_text, AposA8CommandType.PRINT_TEXT))

                protocol_date = response_xml.find(".//dhRecbto").text
                protocol_date_text = "Data de autorizacao: " + self._formata_data(protocol_date)
                report.append(AposA8PrintLine(AposA8Align.CENTER, protocol_date_text, AposA8CommandType.PRINT_TEXT))

    @staticmethod
    def _append_homolog_warning(report, fiscal_xml):
        tp_amb = fiscal_xml.find(".//tpAmb").text
        if tp_amb == "2":
            text_to_print = "EMITIDA EM HOMOLOGACAO - SEM VALOR FISCAL"
            report.append(AposA8PrintLine(AposA8Align.CENTER, text_to_print, AposA8CommandType.PRINT_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, fiscal_xml):
        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_data = "NFC-e Numero %09d  Serie %03d  %s" % (int(numero), int(serie), data_emissao_str)
        report.append(AposA8PrintLine(AposA8Align.CENTER, nfce_data, AposA8CommandType.PRINT_TEXT))

    @staticmethod
    def _append_cpf_cnpj(report, fiscal_xml):
        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(AposA8PrintLine(AposA8Align.CENTER, cpf_cnpj_text, AposA8CommandType.PRINT_TEXT))

    @staticmethod
    def _append_sefaz_url_and_key(report, fiscal_xml, url_sefaz):
        text_to_print = "Consulte pela Chave de Acesso em"
        report.append(AposA8PrintLine(AposA8Align.CENTER, text_to_print, AposA8CommandType.PRINT_TEXT))
        report.append(AposA8PrintLine(AposA8Align.CENTER, url_sefaz, AposA8CommandType.PRINT_TEXT))
        key = fiscal_xml.find(".//infNFe").attrib['Id'][3:]
        key_text = ' '.join(key[i:i + 4] for i in range(0, 44, 4))
        split_key = key_text[:24] + '\n' + key_text[25:]
        report.append(AposA8PrintLine(AposA8Align.CENTER, split_key, AposA8CommandType.PRINT_TEXT))

    @staticmethod
    def _append_contingency_warning(report):
        report.append(APOS_NEWLINE)
        report.append(AposA8PrintLine(AposA8Align.CENTER, "EMITIDA EM CONTINGENCIA", AposA8CommandType.PRINT_TEXT))
        report.append(AposA8PrintLine(AposA8Align.CENTER, "Pendente de autorizacao", AposA8CommandType.PRINT_TEXT))
        report.append(APOS_DASH)

    @staticmethod
    def _append_header(report):
        header_message_1 = "Documento Auxiliar da Nota Fiscal"
        header_message_2 = "de Consumidor Eletronica"
        report.append(AposA8PrintLine(AposA8Align.CENTER, header_message_1, AposA8CommandType.PRINT_TEXT))
        report.append(AposA8PrintLine(AposA8Align.CENTER, header_message_2, AposA8CommandType.PRINT_TEXT))
        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
