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

import base64
import os
import time

import re

from modelcommon.model import SitefResponse
from typing import (
    Any,
    Union
)

from model import (
    CancelPaymentOrderData,
    ConfirmFinishPaymentData,
    FinishPaymentData,
    PaymentResponseMessage,
    PaymentResponseData,
    TestConnectionData
)
from paymentprocessor import PaymentProcessor
from _CapptaCallback import CapptaCallback

cappta_client = None


class CapptaProcessor(PaymentProcessor):
    def __init__(self, message_bus, dialog_manager, config, logger):
        super(CapptaProcessor, self).__init__(message_bus, dialog_manager, config, logger)
        self.env = config.find_value("Environment")
        self.cnpj = config.find_value("CnpjLoja")
        self.pos_id = config.find_value("PosId")
        self.auth_key = config.find_value("ChaveAutenticacao")
        self.callback = CapptaCallback(self.dialog_manager)
        self.cappta_client = self._get_cappta_client(config)

    def connection_status(
        self,
        test_connection_data    # type: TestConnectionData
    ):
        # type: (...) -> bool

        return True

    def add_payment(self, start_payment_data):
        from Cappta.Gp.Api.Com.Model import DetalhesCredito, IMensagem, IRespostaTransacaoPendente, \
            IRequisicaoParametro, IRespostaOperacaoAprovada, IRespostaOperacaoRecusada

        try:
            pos_id = self.pos_id if self.env == 1 else 2
            ret = self.cappta_client.AutenticarPdv(self.cnpj, pos_id, self.auth_key)
            if ret == 0:
                self.logger.debug("POS %s - Autenticação com sucesso." % (str(pos_id)))
                self.cappta_client.IniciarMultiCartoes(9)
                tender_type = int(start_payment_data.tender_type)

                is_credit = tender_type == 1
                is_debit = tender_type == 2
                if is_credit:
                    details = DetalhesCredito()
                    ret = self.cappta_client.PagamentoCredito(float(start_payment_data.amount), details)
                elif is_debit:
                    ret = self.cappta_client.PagamentoDebito(float(start_payment_data.amount))

                if ret != 0:
                    self.callback.callback(str(self.pos_id), ret, "")
                    return

                tef_loop_variable = self.cappta_client.IterarOperacaoTef()
                while True:
                    if isinstance(tef_loop_variable, IMensagem):
                        message = IMensagem(tef_loop_variable)
                        msg = message.Descricao.encode('utf-8')
                        self.callback.callback(str(self.pos_id), msg, "MENSAGEM")
                        time.sleep(0.5)

                    elif isinstance(tef_loop_variable, IRespostaTransacaoPendente):
                        pending_transactions = IRespostaTransacaoPendente(tef_loop_variable)
                        msg = pending_transactions.Mensagem.encode('utf-8')
                        callback_response = self.callback.callback(str(self.pos_id), msg, "REQUISITA_ACAO")
                        if callback_response == "CANCELA":
                            callback_response = "2"
                        ret = self.cappta_client.EnviarParametro(callback_response, 1)

                    elif isinstance(tef_loop_variable, IRequisicaoParametro):
                        req_parameter = IRequisicaoParametro(tef_loop_variable)
                        msg = req_parameter.Mensagem.encode('utf-8')
                        callback_response = self.callback.callback(str(self.pos_id), msg, "REQUISITA_ACAO")

                        if callback_response == "CANCELA":
                            ret = self.cappta_client.EnviarParametro("", 2)
                        else:
                            ret = self.cappta_client.EnviarParametro(callback_response, 1)

                    if isinstance(tef_loop_variable, IRespostaOperacaoAprovada) or isinstance(tef_loop_variable,
                                                                                              IRespostaOperacaoRecusada):
                        break

                    tef_loop_variable = self.cappta_client.IterarOperacaoTef()

                if isinstance(tef_loop_variable, IRespostaOperacaoAprovada):
                    approved_payment = IRespostaOperacaoAprovada(tef_loop_variable)
                    auth_code = str(approved_payment.CodigoAutorizacaoAdquirente)
                    nome_instituicao = str(approved_payment.NomeBandeiraCartao)
                    card_bin = str(approved_payment.NumeroCartaoCliente)[:6]
                    rede_autorizadora = ""
                    transaction_processor = ""
                    bandeira = str(approved_payment.CodigoBandeiraCartao)

                    customer_slip = approved_payment.CupomLojista
                    b64_receipt_merchant = base64.b64encode(str(customer_slip).replace("\"", ""))
                    client_slip = approved_payment.CupomCliente
                    b64_receipt_customer = base64.b64encode(str(client_slip).replace("\"", ""))

                    response = PaymentResponseData(
                        media=nome_instituicao,
                        result=ret,
                        card_number=card_bin,
                        auth_code=auth_code,
                        approved_amount=start_payment_data.amount,
                        id_auth=rede_autorizadora,
                        cnpj_auth="",
                        bandeira=bandeira,
                        receipt_merchant=b64_receipt_merchant,
                        receipt_customer=b64_receipt_customer,
                        expiration_date="",
                        nsu="",
                        transaction_processor=transaction_processor,
                        last_digits="",
                        sat_acquirer="",
                        nfce_brand="",
                        original_nfce_brand="",
                        nsu_host="",
                        electronic_type=start_payment_data.electronic_type,
                        transaction_identifier=""
                    )

                    msg = "POS %s - PaymentApproved. Order %s" % (str(self.pos_id), str(start_payment_data.order_id))
                    self.logger.debug(msg)
                    return response

                elif isinstance(tef_loop_variable, IRespostaOperacaoRecusada):
                    operation = IRespostaOperacaoRecusada(tef_loop_variable)
                    response = operation.Motivo.encode('utf-8')
                    self.logger.debug("POS %s - Operação recusada. Order: %s. Motivo: %s" % (
                    str(self.pos_id), str(start_payment_data.order_id), response))
                    response = "<EftXml Result=\"%s\" />" % response
                    return response
            else:
                message = "Falha ao autenticar POS %s - Código de erro %s" % (str(self.pos_id), str(ret))
                self.logger.error(message)
                return PaymentResponseMessage(message)
        finally:
            self.dialog_manager.close_dialogs(start_payment_data.pos_id)

    def cancel_payment_order(
        self,
        cancel_payment_order_data     # type: CancelPaymentOrderData
    ):
        # type: (...) -> Any[Union[SitefResponse,str]]

        # TODO: Check if need to implement
        pass

    def cancel_payment(self):
        return

    def finish_payment(
        self,
        finish_payment_data     # type: FinishPaymentData
    ):
        # type: (...) -> bool

        try:
            if finish_payment_data.status == 1:
                self.cappta_client.ConfirmarPagamentos()
            else:
                self.cappta_client.DesfazerPagamentos()
            return True
        except Exception as _:
            return False

    def confirm_finish_payment(
        self,
        confirm_finish_payment_data     # type: ConfirmFinishPaymentData
    ):
        # type: (...) -> None

        pass

    def get_name(self):
        return "Cappta"

    def stop(self):
        return

    def _get_cappta_client(self, config):
        import clr  # Linux (POS Mobile) does not have an implemented library to clr

        global cappta_client
        if cappta_client is not None:
            return cappta_client

        dll_path = self._get_dll_path(config.find_value("DLLPath"))
        old_dir = os.getcwd()
        os.chdir(dll_path)
        clr.AddReference(os.path.join(os.getcwd(), "Cappta.Gp.Api.Com.dll"))

        from Cappta.Gp.Api.Com import ClienteCappta

        cappta_client = ClienteCappta()
        os.chdir(old_dir)

        return cappta_client

    @staticmethod
    def _get_dll_path(dll_path):
        for var in re.findall("{[a-zA-Z0-9]*}", dll_path):
            dll_path = dll_path.replace(var, os.environ.get(var.replace('{', '').replace('}', ''), ""))
        return os.path.normpath(dll_path)
