from abc import abstractmethod
from typing import (
    Any,
    Dict,
    List,
)
from xml.etree import cElementTree as eTree

from common import FiscalOrderTotals
from common.builder_functions import (
    format_datetime,
    optional,
    pad_access_key,
)
from nreports.builders import DataBuilder
from nreports_api.shared import JSON


class BaseFiscalSat(DataBuilder):

    @abstractmethod
    def build_data(
        self,
        data_to_build: Dict[str, JSON],
        **kwargs: Any,
    ) -> Dict[str, JSON]:

        raise NotImplementedError()

    def _build_header(
        self,
        data_to_build: JSON,
        fiscal_xml: eTree.Element,
    ) -> None:

        data_to_build["STORE_NAME"] = fiscal_xml.find(".//emit/xNome").text
        data_to_build["STORE_UF"] = fiscal_xml.find(".//ide/cUF").text
        data_to_build["STORE_STATE_INSCRIPTION"] = fiscal_xml.find(".//emit/IE").text
        data_to_build["STORE_DOC"] = fiscal_xml.find(".//emit/CNPJ").text
        data_to_build["STORE_ADDRESS"] = fiscal_xml.find(".//enderEmit/xLgr").text
        data_to_build["STORE_NUMBER"] = optional(element=fiscal_xml.find(".//enderEmit/nro")) or ""
        data_to_build["STORE_COMPLEMENT"] = optional(element=fiscal_xml.find(".//enderEmit/xCpl")) or ""
        data_to_build["STORE_NEIGHBORHOOD"] = fiscal_xml.find(".//enderEmit/xBairro").text
        data_to_build["STORE_CITY"] = fiscal_xml.find(".//enderEmit/xMun").text
        data_to_build["STORE_ZIP_CODE"] = fiscal_xml.find(".//emit/enderEmit/CEP").text

    @staticmethod
    def _build_body(
        data_to_build: JSON,
        fiscal_xml: eTree.Element,
        fiscal_order_totals: FiscalOrderTotals,
        customer_document: str,
        products: List[Dict[str, str]],
        tenders: List[Dict[str, str]],
    ) -> None:

        data_to_build["ORDER_ITEMS_QTY"] = len(fiscal_xml.findall(".//det"))
        data_to_build["ORDER_TOTAL_GROSS"] = fiscal_order_totals.total_value
        data_to_build["ORDER_DISCOUNT"] = fiscal_order_totals.total_discount
        data_to_build["ORDER_DUE_AMOUNT"] = fiscal_order_totals.total_paid
        data_to_build["ORDER_CHANGE_AMOUNT"] = fiscal_order_totals.amount_change
        data_to_build["CUSTOMER_DOCUMENT"] = customer_document
        data_to_build["ORDER_ADDITION"] = fiscal_order_totals.total_addition or None

        data_to_build["ITEM_PRODUCT"] = products
        data_to_build["TENDER"] = tenders

    def _build_footer(
        self,
        data_to_build: JSON,
        fiscal_xml: eTree.Element,
    ) -> None:

        sat_emission_date = fiscal_xml.find(".//ide/dEmi").text
        sat_emission_time = fiscal_xml.find(".//ide/hEmi").text
        sat_key = fiscal_xml.find(".//infCFe").attrib["Id"][3:]

        data_to_build["SAT_DOC_NUMBER"] = fiscal_xml.find(".//ide/nCFe").text
        data_to_build["SAT_SERIE_NUMBER"] = fiscal_xml.find(".//ide/nserieSAT").text
        data_to_build["SAT_ACCESS_KEY"] = pad_access_key(access_key=sat_key)
        data_to_build["SAT_EMISSION_DATETIME"] = format_datetime(
            datetime_string=sat_emission_date + sat_emission_time,
            input_mask="%Y%m%d%H%M%S",
        )
        inf_cpl = optional(element=fiscal_xml.find(".//infCpl")) or ""
        inf_cpl = "\n\n".join(inf_cpl.split("///"))
        data_to_build["SAT_ADDITIONAL_INFORMATION"] = inf_cpl
        data_to_build["SAT_BARCODE"] = sat_key
        cpf = optional(element=fiscal_xml.find(".//dest/CPF"))
        cnpj = optional(element=fiscal_xml.find(".//dest/CNPJ"))
        data_to_build["SAT_QRCODE"] = "|".join([
            sat_key,
            sat_emission_date + sat_emission_time,
            fiscal_xml.find(".//total/vCFe").text,
            cpf or cnpj or "",
            fiscal_xml.find(".//ide/assinaturaQRCODE").text,
        ])
