# -*- 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,
                 print_customer_name,
                 cliche_helper,
                 qrcode_url,
                 customer_text_field,
                 printer_common):
        # type: (MBEasyContext, bool, ClicheHelper, str, int, PrinterCommon) -> None
        super(NfceAposA8FiscalPrinter, self).__init__(mb_context, print_customer_name, cliche_helper, customer_text_field)
        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,
            order,
            tenders,
            fiscal_request,
            fiscal_response,
            total_taxes,
            customer_name,
            url_sefaz=""
    ):
        # type: (str, eTree.Element, list, eTree.Element, eTree.Element, dict, str, str) -> 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,
                tenders,
                fiscal_request,
                fiscal_response,
                total_taxes,
                "Via Estabelecimento",
                customer_name,
                self.qrcode_check_url,
                model
            )
            report.append(APOS_NEWLINE)
            report.extend(
                self._fiscal_report(
                    order,
                    tenders,
                    fiscal_request,
                    fiscal_response,
                    total_taxes,
                    "Via Consumidor",
                    customer_name,
                    self.qrcode_check_url,
                    model
                )
            )
        else:
            report = self._fiscal_report(
                order,
                tenders,
                fiscal_request,
                fiscal_response,
                total_taxes,
                "",
                customer_name,
                self.qrcode_check_url,
                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,
            tenders,
            fiscal_request,
            fiscal_response,
            total_taxes,
            via,
            customer_name,
            url_sefaz,
            model=None
    ):
        # type: (eTree.Element, list, eTree.Element, eTree.Element, dict, str, str, str, Optional[eTree.Element]) -> 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, model, order, report, tenders, 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
