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

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


class BaseFiscalNfce(DataBuilder):

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

        raise NotImplementedError()

    @staticmethod
    def _build_header(
        data_to_build: JSON,
        fiscal_xml: eTree.Element,
        logo_image: str = "",
    ) -> None:

        if logo_image:
            data_to_build["STORE_IMG_LOGO"] = logo_image

        data_to_build["STORE_NAME"] = fiscal_xml.find(".//emit/xNome").text
        data_to_build["STORE_DOC"] = fiscal_xml.find(".//emit/CNPJ").text
        data_to_build["STORE_STATE_INSCRIPTION"] = fiscal_xml.find(".//emit/IE").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_UF"] = fiscal_xml.find(".//enderEmit/UF").text
        data_to_build["STORE_ZIP_CODE"] = fiscal_xml.find(".//emit/enderEmit/CEP").text

    def _build_body(
        self,
        data_to_build: JSON,
        fiscal_xml: eTree.Element,
        fiscal_order_totals: FiscalOrderTotals,
        products: 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_ADDITION"] = fiscal_order_totals.total_addition
        data_to_build["ORDER_DUE_AMOUNT"] = fiscal_xml.find(".//ICMSTot/vNF").text
        data_to_build["ORDER_CHANGE"] = optional(element=fiscal_xml.find(".//vTroco"))
        data_to_build["CUSTOMER_DOCUMENT"] = get_optional_customer_doc(fiscal_xml=fiscal_xml)
        data_to_build["ITEM_PRODUCT"] = products
        data_to_build["TENDER"] = self._get_tenders(fiscal_xml=fiscal_xml)

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

        is_contingency_enabled = fiscal_xml.find(".//tpEmis").text != "1"
        inf_cpl = optional(element=fiscal_xml.find(".//infCpl")) or ""
        inf_cpl = "\n\n".join(inf_cpl.split("///"))
        data_to_build["NFCE_CONTINGENCY_ENABLED"] = is_contingency_enabled
        data_to_build["NFCE_ADDITIONAL_INFORMATION"] = inf_cpl
        data_to_build["NFCE_TEST_ENVIRONMENT"] = fiscal_xml.find(".//tpAmb").text == "2"
        data_to_build["NFCE_URL_CONSULT"] = fiscal_xml.find(".//urlChave").text
        data_to_build["NFCE_KEY"] = pad_access_key(access_key=fiscal_xml.find(".//infNFe").attrib["Id"][3:])
        data_to_build["NFCE_QRCODE_URL"] = fiscal_xml.find(".//qrCode").text
        data_to_build["NFCE_NUMBER"] = fiscal_xml.find(".//nNF").text
        data_to_build["NFCE_SERIES"] = fiscal_xml.find(".//serie").text
        data_to_build["NFCE_EMISSION_DATE"] = format_datetime(datetime_string=fiscal_xml.find(".//dhEmi").text)
        data_to_build["NFCE_EMISSION_OWNER"] = self._get_emission_owner(is_contingency_enabled=is_contingency_enabled)
        data_to_build["NFCE_AUTHORIZATION_PROTOCOL"] = optional(element=fiscal_xml.find(".//nProt"))
        data_to_build["NFCE_AUTHORIZATION_DATE"] = format_datetime(
            datetime_string=optional(
                element=fiscal_xml.find(".//dhRecbto"),
            ),
        )

    @staticmethod
    def _get_emission_owner(
        is_contingency_enabled: bool = False,
    ) -> Optional[List[str]]:

        if not is_contingency_enabled:
            return None

        return ["Consumidor", "Estabelecimento"]

    @staticmethod
    def _get_tenders(
        fiscal_xml: eTree.Element,
    ) -> List[Dict[str, str]]:

        tenders: List[Dict[str, str]] = []
        fiscal_tenders = fiscal_xml.findall(".//detPag")
        for tender in fiscal_tenders:
            tenders.append(
                {
                    "TENDER_TYPE": get_tender_description(tender_id=tender.find("tPag").text).upper(),
                    "TENDER_VALUE": tender.find("vPag").text,
                }
            )

        return tenders
