# -*- coding: utf-8 -*-

import logging

from typing import (
    Any,
    Dict,
)

from old_helper import (
    remove_punctuation,
    validar_cnpj,
)
from helper import remove_accents
from pos_model import OrderTender

from models import NfeTender
from _NfceXmlPartBuilder import NfceXmlPartBuilder
from services import TPagService
from utils import is_tef_tender

logger = logging.getLogger("FiscalWrapper")
CARD_XML = "<card><tpIntegra>{0}</tpIntegra><CNPJ>{1}</CNPJ><tBand>{2}</tBand>{3}</card>"
CARD_XML_INVALID_CNPJ = "<card><tpIntegra>2</tpIntegra></card>"


class PagBuilder(NfceXmlPartBuilder):

    def __init__(self, versao_ws, tenders_type):
        # type: (int, Dict) -> None

        self.versao_ws = versao_ws
        self.tenders_type = tenders_type

    def build_xml(self, order, context):
        # type: (Order, Dict[unicode, Any]) -> unicode

        xml = "<pag>" if self.versao_ws not in (1, 3) else ""
        t_pag_service = TPagService(tenders_type=self.tenders_type)
        for tender in order.tenders:
            xml += "<detPag>" if self.versao_ws not in (1, 3) else "<pag>"

            t_pag = t_pag_service.get_t_pag(tender=tender)

            xml += "<tPag>{:>02}</tPag>".format(t_pag)

            if t_pag == NfeTender.outros:
                tender_desc = tender.descr or tender.type
                xml += "<xPag>{}</xPag>".format(remove_accents(tender_desc))

            total_amount = tender.amount - tender.change if self.versao_ws in (1, 3) else tender.amount
            xml += "<vPag>{:>0.2f}</vPag>".format(total_amount)

            xml += self._get_card_xml(
                tender=tender,
                t_pag=t_pag,
            )
            xml += "</detPag>" if self.versao_ws not in (1, 3) else "</pag>"

            if self.versao_ws not in (1, 3) and tender.change > 0.0:
                xml += "<vTroco>{:>0.2f}</vTroco>".format(tender.change)

        xml += "</pag>" if self.versao_ws not in (1, 3) else ""

        return xml

    def _get_card_xml(
        self,
        tender,         # type: OrderTender
        t_pag,          # type: int
    ):
        # type: (...) -> str

        if not self._is_valid_t_pag_to_create_card_xml(t_pag=int(t_pag)):
            return ""

        if is_tef_tender(tender=tender):
            if not validar_cnpj(tender.cnpj_auth):
                return CARD_XML_INVALID_CNPJ

            card_brand = self._get_card_brand(tender.t_band)
            cnpj_auth = remove_punctuation(tender.cnpj_auth.strip())
            auth_code = "<cAut>{0}</cAut>".format(tender.auth_code.strip()[-20:])
            return CARD_XML.format(1, cnpj_auth, card_brand, auth_code)

        tender_description = str(remove_accents(tender.descr)).upper()
        if tender_description not in self.tenders_type:
            return ""

        tender_type_data_base = self.tenders_type[tender_description]
        if self._tender_has_valid_cnpj(tender_type_data_base):
            t_band = tender_type_data_base.get("tBand", None)
            card_brand = self._get_card_brand(t_band)
            cnpj_auth = remove_punctuation(tender_type_data_base.get("cnpj", None))
            return CARD_XML.format(2, cnpj_auth, card_brand, "")

        return CARD_XML_INVALID_CNPJ

    @staticmethod
    def _get_card_brand(
        t_band,     # type: Union[str, int]
    ):
        card_brand = t_band or "99"
        try:
            int(card_brand)
        except ValueError:
            logger.warning("Invalid Card Brand {}".format(str(card_brand)))
            card_brand = "99"

        return card_brand

    @staticmethod
    def _tender_has_valid_cnpj(
        tender_type_data_base   # type: Dict
    ):
        # type: (...) -> bool

        card_cnpj = tender_type_data_base.get("cnpj", None)
        cnpj = validar_cnpj(card_cnpj)
        return True if cnpj is not False else False

    @staticmethod
    def _is_valid_t_pag_to_create_card_xml(
        t_pag,      # type: int
    ):
        # type: (...) -> bool

        tef_tenders = [
            NfeTender.cartao_credito,
            NfeTender.cartao_debito,
            NfeTender.pagamento_instantaneo_pix,
        ]
        return t_pag in tef_tenders
