# -*- 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 Order, TenderType, OrderTender

from models import PosTenderToNfeTender, NfeTender
from _NfceXmlPartBuilder import NfceXmlPartBuilder

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 ""

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

            tender_type = self._get_tender_type(tender)
            xml += "<tPag>{:>02}</tPag>".format(tender_type)

            if tender_type == 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)
            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
    ):
        # type: (...) -> str

        if self._is_tef_tender(tender):
            if not validar_cnpj(tender.cnpj_auth):
                return CARD_XML_INVALID_CNPJ

            card_brand = self._get_card_brand(tender)

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

        tender_description = str(remove_accents(tender.descr)).upper()
        if tender_description in self.tenders_type:
            tender_type_data_base = self.tenders_type[tender_description]
            if self._tender_database_is_valid(tender_type_data_base):
                card_brand = tender_type_data_base.get("tBand", None)
                card_brand = card_brand if self._is_not_blank(card_brand) else "99"
                cnpj_auth = remove_punctuation(tender_type_data_base.get("cnpj", None))
                not_integrated = 2
                return CARD_XML.format(not_integrated, cnpj_auth, card_brand, "")

        return ""

    @staticmethod
    def _get_card_brand(
        tender,     # type: OrderTender
    ):
        card_brand = tender.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

    def _get_tender_type(
        self,
        tender  # type: OrderTender
    ):
        # type: (...) -> int

        if self._is_tef_tender(tender):
            return int(tender.t_pag or 99)

        tender_description = str(remove_accents(tender.descr)).upper()
        if tender_description in self.tenders_type:
            tender_type_data_base = self.tenders_type[tender_description]
            t_pag = tender_type_data_base.get("tPag", None)
            if self._tender_database_is_valid(tender_type_data_base) and self._is_not_blank(t_pag):
                return int(t_pag)

        tender_type = PosTenderToNfeTender.convert(tender.type)
        if tender_type not in [NfeTender.dinheiro, NfeTender.cash_back]:
            return NfeTender.outros

        return tender_type

    @staticmethod
    def _is_tef_tender(
        tender  # type: OrderTender
    ):
        # type: (...) -> bool

        tef_tenders = [
            TenderType.credit,
            TenderType.debit,
            TenderType.voucher,
            TenderType.gift_card,
            TenderType.digital_wallet,
        ]
        return tender.type in tef_tenders

    @staticmethod
    def _tender_database_is_valid(
        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_not_blank(
        value   # type: str
    ):
        # type: (...) -> bool

        if value and value.strip():
            return True
        return False
