<?php

declare(strict_types=1);

namespace App\Integrations;

use Exception;

/**
 * Integração com API do Banco Inter
 * Documentação: https://developers.bancointer.com.br/reference/cobranca
 */
class BancoInter
{
    private string $clientId;
    private string $clientSecret;
    private string $contaCorrente;
    private bool $isProducao;
    private ?string $certificadoPath = null;
    private ?string $chavePrivadaPath = null;
    private ?string $certificadoSenha = null;

    /**
     * URLs da API - Banco Inter
     * Documentação: https://developers.bancointer.com.br/
     *
     * ℹ️ IMPORTANTE: O Banco Inter usa a MESMA URL para sandbox e produção.
     * A diferença está apenas nas CREDENCIAIS (Client ID/Secret):
     * - Credenciais de HOMOLOGAÇÃO: Geram boletos de teste
     * - Credenciais de PRODUÇÃO: Geram boletos reais
     *
     * 🔐 AUTENTICAÇÃO: O Banco Inter exige certificado digital mTLS (mutual TLS)
     */
    private const URL_TOKEN_SANDBOX = 'https://cdpj.partners.bancointer.com.br/oauth/v2/token';
    private const URL_TOKEN_PRODUCAO = 'https://cdpj.partners.bancointer.com.br/oauth/v2/token';

    private const URL_BOLETO_SANDBOX = 'https://cdpj.partners.bancointer.com.br/cobranca/v3/cobrancas';
    private const URL_BOLETO_PRODUCAO = 'https://cdpj.partners.bancointer.com.br/cobranca/v3/cobrancas';

    public function __construct(
        string $clientId,
        string $clientSecret,
        string $contaCorrente,
        bool $isProducao = false,
        ?string $certificadoPath = null,
        ?string $chavePrivadaPath = null,
        ?string $certificadoSenha = null
    ) {
        $this->clientId = $clientId;
        $this->clientSecret = $clientSecret;
        $this->contaCorrente = $contaCorrente;
        $this->isProducao = $isProducao;
        $this->certificadoPath = $certificadoPath;
        $this->chavePrivadaPath = $chavePrivadaPath;
        $this->certificadoSenha = $certificadoSenha;
    }

    /**
     * Obter token de acesso OAuth 2.0
     */
    public function getAccessToken(): string
    {
        $url = $this->isProducao ? self::URL_TOKEN_PRODUCAO : self::URL_TOKEN_SANDBOX;

        $ch = curl_init();

        $curlOptions = [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/x-www-form-urlencoded'
            ],
            CURLOPT_POSTFIELDS => http_build_query([
                'client_id' => $this->clientId,
                'client_secret' => $this->clientSecret,
                'grant_type' => 'client_credentials',
                'scope' => 'boleto-cobranca.read boleto-cobranca.write'
            ]),
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2
        ];

        // 🔐 Configuração mTLS (mutual TLS) - OBRIGATÓRIO para Banco Inter
        if ($this->certificadoPath && $this->chavePrivadaPath) {
            if (file_exists($this->certificadoPath)) {
                $curlOptions[CURLOPT_SSLCERT] = $this->certificadoPath;
                error_log("Banco Inter: Usando certificado mTLS: {$this->certificadoPath}");
            } else {
                throw new Exception("Certificado digital não encontrado: {$this->certificadoPath}");
            }

            if (file_exists($this->chavePrivadaPath)) {
                $curlOptions[CURLOPT_SSLKEY] = $this->chavePrivadaPath;

                // Se a chave privada tiver senha
                if (!empty($this->certificadoSenha)) {
                    $curlOptions[CURLOPT_SSLKEYPASSWD] = $this->certificadoSenha;
                    error_log("Banco Inter: Chave privada com senha configurada");
                }
            } else {
                throw new Exception("Chave privada não encontrada: {$this->chavePrivadaPath}");
            }
        } else {
            error_log("Banco Inter: AVISO - Certificado mTLS não configurado! A autenticação falhará.");
        }

        // CA Bundle para verificação do servidor
        $cacertPath = null;
        $possiblePaths = [
            'C:/xampp/apache/bin/curl-ca-bundle.crt',
            'C:/xampp/php/extras/ssl/cacert.pem',
            dirname(__DIR__, 2) . '/storage/cacert.pem',
        ];

        foreach ($possiblePaths as $path) {
            if (file_exists($path)) {
                $cacertPath = $path;
                break;
            }
        }

        if ($cacertPath) {
            $curlOptions[CURLOPT_CAINFO] = $cacertPath;
        } else {
            // Em desenvolvimento sem CA bundle, desabilitar verificação
            $curlOptions[CURLOPT_SSL_VERIFYPEER] = false;
            $curlOptions[CURLOPT_SSL_VERIFYHOST] = 0;
        }

        curl_setopt_array($ch, $curlOptions);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) {
            throw new Exception("Erro ao conectar com Banco Inter: {$error}");
        }

        if ($httpCode !== 200) {
            $errorData = json_decode($response, true);
            $errorMsg = $errorData['error_description'] ?? $errorData['message'] ?? $errorData['error'] ?? 'Erro desconhecido';

            // Log detalhado para debug
            error_log("Banco Inter - Erro ao obter token:");
            error_log("  HTTP Code: {$httpCode}");
            error_log("  Response: {$response}");
            error_log("  Client ID: " . substr($this->clientId, 0, 10) . "...");
            error_log("  Client Secret: " . (strlen($this->clientSecret) > 0 ? '[DEFINIDO]' : '[VAZIO]'));

            throw new Exception("Erro ao obter token (HTTP {$httpCode}): {$errorMsg}");
        }

        $data = json_decode($response, true);

        if (!isset($data['access_token'])) {
            throw new Exception("Token não retornado pela API");
        }

        return $data['access_token'];
    }

    /**
     * Criar boleto/cobrança
     *
     * @param array $dados Dados da cobrança
     * @return array Dados do boleto criado
     */
    public function criarCobranca(array $dados): array
    {
        $token = $this->getAccessToken();
        $url = $this->isProducao ? self::URL_BOLETO_PRODUCAO : self::URL_BOLETO_SANDBOX;

        // Processar documento do pagador
        $documento = preg_replace('/\D/', '', $dados['pagador']['documento']);
        $tipoPessoa = strlen($documento) === 11 ? 'FISICA' : 'JURIDICA';

        // Processar telefone (separar DDD)
        $telefoneCompleto = preg_replace('/\D/', '', $dados['pagador']['telefone'] ?? '');
        $ddd = '';
        $telefone = '';

        if (strlen($telefoneCompleto) >= 10) {
            $ddd = substr($telefoneCompleto, 0, 2);
            $telefone = substr($telefoneCompleto, 2);
        } else {
            $telefone = $telefoneCompleto;
        }

        // Montar payload conforme documentação do Inter
        $payload = [
            'seuNumero' => $dados['numero_documento'] ?? '',
            'valorNominal' => (float)$dados['valor'],
            'dataVencimento' => $dados['data_vencimento'], // Formato: YYYY-MM-DD
            'numDiasAgenda' => $dados['dias_agenda'] ?? 30,
            'pagador' => [
                'cpfCnpj' => $documento,
                'tipoPessoa' => $tipoPessoa,
                'nome' => $dados['pagador']['nome'],
                'email' => $dados['pagador']['email'] ?? '',
                'ddd' => $ddd,
                'telefone' => $telefone,
                'endereco' => $dados['pagador']['endereco'] ?? '',
                'numero' => $dados['pagador']['numero'] ?? '',
                'complemento' => $dados['pagador']['complemento'] ?? '',
                'bairro' => $dados['pagador']['bairro'] ?? '',
                'cidade' => $dados['pagador']['cidade'] ?? '',
                'uf' => $dados['pagador']['uf'] ?? '',
                'cep' => preg_replace('/\D/', '', $dados['pagador']['cep'] ?? '')
            ]
        ];

        // Adicionar multa se informada
        if (!empty($dados['multa'])) {
            $payload['multa'] = [
                'codigoMulta' => 'PERCENTUAL',
                'taxa' => (float)$dados['multa']['percentual'],
                'valor' => 0.00
            ];
        }

        // Adicionar juros/mora se informado
        if (!empty($dados['juros'])) {
            $payload['mora'] = [
                'codigoMora' => 'TAXAMENSAL',
                'taxa' => (float)$dados['juros']['percentual'],
                'valor' => 0.00
            ];
        }

        // Adicionar desconto se informado
        if (!empty($dados['desconto'])) {
            $payload['desconto'] = [
                'codigoDesconto' => 'PERCENTUALDATAINFORMADA',
                'taxa' => (float)$dados['desconto']['percentual'],
                'valor' => 0.00,
                'data' => $dados['desconto']['data_limite'] ?? $dados['data_vencimento']
            ];
        }

        // Log do payload para debug
        error_log("Banco Inter - Payload da cobrança: " . json_encode($payload, JSON_PRETTY_PRINT));

        $ch = curl_init();

        $headers = [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $token,
            'x-conta-corrente: ' . $this->contaCorrente
        ];

        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_POSTFIELDS => json_encode($payload),
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            // Adicionar certificado mTLS também na criação
            CURLOPT_SSLCERT => $this->certificadoPath,
            CURLOPT_SSLKEY => $this->chavePrivadaPath,
        ]);

        // Se tiver senha na chave
        if (!empty($this->certificadoSenha)) {
            curl_setopt($ch, CURLOPT_SSLKEYPASSWD, $this->certificadoSenha);
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) {
            error_log("Banco Inter - Erro cURL ao criar cobrança: {$error}");
            throw new Exception("Erro ao criar cobrança: {$error}");
        }

        if ($httpCode !== 200 && $httpCode !== 201) {
            $errorData = json_decode($response, true);
            $errorMsg = $errorData['message'] ?? $errorData['title'] ?? 'Erro desconhecido';

            // Construir mensagem de erro detalhada
            $errorDetails = [
                'http_code' => $httpCode,
                'response_raw' => $response,
                'response_json' => $errorData,
                'payload_enviado' => $payload
            ];

            // Log detalhado do erro
            error_log("Banco Inter - Erro na criação da cobrança:");
            error_log("  HTTP Code: {$httpCode}");
            error_log("  Response: " . $response);

            // Tentar extrair mensagens de erro mais detalhadas
            if (is_array($errorData)) {
                // Violações de validação
                if (isset($errorData['violacoes']) && is_array($errorData['violacoes'])) {
                    $violations = array_map(function($v) {
                        return ($v['propriedade'] ?? 'campo') . ': ' . ($v['razao'] ?? $v['mensagem'] ?? 'erro');
                    }, $errorData['violacoes']);
                    $errorMsg = implode('; ', $violations);
                }

                // Detail (mensagens mais descritivas)
                if (isset($errorData['detail'])) {
                    $errorMsg = $errorData['detail'];
                }
            }

            // Criar exceção com detalhes
            throw new Exception("Erro ao criar cobrança (HTTP {$httpCode}): {$errorMsg}");
        }

        // Log da resposta RAW para debug
        error_log("Banco Inter - Resposta RAW (HTTP {$httpCode}): " . ($response ?: '[VAZIO]'));
        error_log("Banco Inter - Tamanho da resposta: " . strlen($response) . " bytes");

        $boleto = json_decode($response, true);

        // Se a resposta é vazia mas HTTP 201/200, consultar pelo seuNumero (API v3 retorna vazia na criação)
        if (empty($boleto) || empty($boleto['nossoNumero'])) {
            if ($httpCode === 200 || $httpCode === 201) {
                error_log("Banco Inter - Resposta vazia, consultando boleto pelo seuNumero: " . $payload['seuNumero']);

                // Aguardar 1 segundo para a API processar
                sleep(1);

                try {
                    // Consultar o boleto recém-criado pelo seuNumero
                    $boletoConsultado = $this->consultarCobrancaPorSeuNumero($payload['seuNumero'], $token);
                    if ($boletoConsultado && !empty($boletoConsultado)) {
                        // Buscar PDF se tiver código de solicitação
                        $pdfBase64 = null;
                        if (!empty($boletoConsultado['codigoSolicitacao'])) {
                            try {
                                $pdfBase64 = $this->obterPdfBoleto($boletoConsultado['codigoSolicitacao']);
                            } catch (Exception $e) {
                                error_log("Erro ao obter PDF: " . $e->getMessage());
                            }
                        }

                        // Usar dados da consulta
                        return [
                            'codigo_solicitacao' => $boletoConsultado['codigoSolicitacao'] ?? '',
                            'nosso_numero' => $boletoConsultado['nossoNumero'] ?? '',
                            'codigo_barras' => $boletoConsultado['codigoBarras'] ?? '',
                            'linha_digitavel' => $boletoConsultado['linhaDigitavel'] ?? '',
                            'pdf_base64' => $pdfBase64,
                            'qrcode' => $boletoConsultado['pixCopiaECola'] ?? '',
                            'tx_id' => $boletoConsultado['txId'] ?? '',
                            'status' => 'EMITIDO'
                        ];
                    }
                } catch (Exception $e) {
                    error_log("Banco Inter - Erro ao consultar boleto após criação: " . $e->getMessage());
                    // Continuar - retornar vazio e marcar como pendente
                }
            }
        }

        // Se chegou aqui e tem dados na resposta da criação, usar eles
        // (improvável na API v3, mas mantemos por compatibilidade)
        return [
            'nosso_numero' => $boleto['nossoNumero'] ?? '',
            'codigo_barras' => $boleto['codigoBarras'] ?? '',
            'linha_digitavel' => $boleto['linhaDigitavel'] ?? '',
            'pdf_url' => $boleto['pdf'] ?? '',
            'qrcode' => $boleto['pixCopiaECola'] ?? '',
            'tx_id' => $boleto['txId'] ?? '',
            'status' => 'EMITIDO'
        ];
    }

    /**
     * Consultar boleto pelo nosso número
     */
    public function consultarCobranca(string $nossoNumero): array
    {
        $token = $this->getAccessToken();
        $url = ($this->isProducao ? self::URL_BOLETO_PRODUCAO : self::URL_BOLETO_SANDBOX) . "/{$nossoNumero}";

        $ch = curl_init();

        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $token,
                'x-conta-corrente: ' . $this->contaCorrente
            ],
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_SSLCERT => $this->certificadoPath,
            CURLOPT_SSLKEY => $this->chavePrivadaPath,
        ]);

        if (!empty($this->certificadoSenha)) {
            curl_setopt($ch, CURLOPT_SSLKEYPASSWD, $this->certificadoSenha);
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            throw new Exception("Erro ao consultar cobrança (HTTP {$httpCode})");
        }

        return json_decode($response, true) ?: [];
    }

    /**
     * Consultar boleto pelo seuNumero (método interno)
     */
    private function consultarCobrancaPorSeuNumero(string $seuNumero, string $token): ?array
    {
        // A API do Inter EXIGE dataInicial e dataFinal
        // Vamos buscar nos últimos 30 dias
        $dataFinal = date('Y-m-d');
        $dataInicial = date('Y-m-d', strtotime('-30 days'));

        $url = ($this->isProducao ? self::URL_BOLETO_PRODUCAO : self::URL_BOLETO_SANDBOX)
             . "?dataInicial={$dataInicial}&dataFinal={$dataFinal}&seuNumero={$seuNumero}";

        error_log("Banco Inter - Consultando boleto pelo seuNumero: {$seuNumero}");
        error_log("Banco Inter - URL de consulta: {$url}");

        $ch = curl_init();

        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $token,
                'x-conta-corrente: ' . $this->contaCorrente
            ],
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_SSLCERT => $this->certificadoPath,
            CURLOPT_SSLKEY => $this->chavePrivadaPath,
        ]);

        if (!empty($this->certificadoSenha)) {
            curl_setopt($ch, CURLOPT_SSLKEYPASSWD, $this->certificadoSenha);
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        error_log("Banco Inter - Resposta da consulta HTTP {$httpCode}");
        error_log("Banco Inter - Resposta body: " . ($response ?: '[VAZIO]'));

        if ($error) {
            error_log("Banco Inter - Erro cURL na consulta: {$error}");
        }

        if ($httpCode === 200 && !empty($response)) {
            $dados = json_decode($response, true);

            error_log("Banco Inter - Dados decodificados: " . json_encode($dados));

            // Estrutura correta da API: cobrancas[0].boleto e cobrancas[0].pix
            if (isset($dados['cobrancas']) && is_array($dados['cobrancas']) && !empty($dados['cobrancas'])) {
                error_log("Banco Inter - Encontrado " . count($dados['cobrancas']) . " boleto(s)");

                // Pegar o primeiro boleto (ou o mais recente se houver múltiplos)
                $primeiraCobranca = $dados['cobrancas'][0];

                // Mesclar dados da cobrança, boleto e pix em um único objeto
                $resultado = [
                    'codigoSolicitacao' => $primeiraCobranca['cobranca']['codigoSolicitacao'] ?? '',
                    'seuNumero' => $primeiraCobranca['cobranca']['seuNumero'] ?? '',
                    'situacao' => $primeiraCobranca['cobranca']['situacao'] ?? '',
                    'dataVencimento' => $primeiraCobranca['cobranca']['dataVencimento'] ?? '',
                    'valorNominal' => $primeiraCobranca['cobranca']['valorNominal'] ?? '',
                    'nossoNumero' => $primeiraCobranca['boleto']['nossoNumero'] ?? '',
                    'linhaDigitavel' => $primeiraCobranca['boleto']['linhaDigitavel'] ?? '',
                    'codigoBarras' => $primeiraCobranca['boleto']['codigoBarras'] ?? '',
                    'pixCopiaECola' => $primeiraCobranca['pix']['pixCopiaECola'] ?? '',
                    'txId' => $primeiraCobranca['pix']['txid'] ?? '',
                ];

                error_log("Banco Inter - Boleto encontrado: nossoNumero=" . $resultado['nossoNumero'] . ", codigoSolicitacao=" . $resultado['codigoSolicitacao']);
                return $resultado;
            }

            error_log("Banco Inter - Formato de resposta não reconhecido ou vazio");
        }

        error_log("Banco Inter - Consulta não retornou dados válidos");
        return null;
    }

    /**
     * Obter PDF do boleto
     * @param string $codigoSolicitacao - Código de solicitação do boleto (não nossoNumero!)
     */
    public function obterPdfBoleto(string $codigoSolicitacao): ?string
    {
        $token = $this->getAccessToken();
        $url = ($this->isProducao ? self::URL_BOLETO_PRODUCAO : self::URL_BOLETO_SANDBOX) . "/{$codigoSolicitacao}/pdf";


        $ch = curl_init();

        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $token,
                'x-conta-corrente: ' . $this->contaCorrente
            ],
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_SSLCERT => $this->certificadoPath,
            CURLOPT_SSLKEY => $this->chavePrivadaPath,
        ]);

        if (!empty($this->certificadoSenha)) {
            curl_setopt($ch, CURLOPT_SSLKEYPASSWD, $this->certificadoSenha);
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($httpCode === 200) {
            // Se é PDF direto (binário)
            if (strpos($contentType, 'application/pdf') !== false) {
                error_log("Banco Inter - PDF obtido com sucesso (binário - " . strlen($response) . " bytes)");
                return base64_encode($response);
            }

            // Se é JSON (API retorna PDF em base64 dentro de JSON)
            if (strpos($contentType, 'application/json') !== false) {
                $dados = json_decode($response, true);

                // Verificar se tem o PDF em base64 no campo 'pdf'
                if (isset($dados['pdf'])) {
                    error_log("Banco Inter - PDF obtido com sucesso em JSON (base64 - " . strlen($dados['pdf']) . " caracteres)");
                    return $dados['pdf']; // Já está em base64
                }

                // Verificar se a resposta inteira é o base64
                if (is_string($response) && strlen($response) > 1000 && preg_match('/^[A-Za-z0-9+\/=]+$/', substr($response, 0, 100))) {
                    error_log("Banco Inter - PDF obtido como base64 direto (" . strlen($response) . " caracteres)");
                    return $response;
                }
            }
        }

        error_log("Banco Inter - Não foi possível obter PDF (HTTP {$httpCode}, Content-Type: {$contentType})");
        return null;
    }

    /**
     * Cancelar boleto
     */
    public function cancelarCobranca(string $nossoNumero, string $motivoCancelamento = 'ACERTOS'): bool
    {
        $token = $this->getAccessToken();
        $url = ($this->isProducao ? self::URL_BOLETO_PRODUCAO : self::URL_BOLETO_SANDBOX) . "/{$nossoNumero}/cancelar";

        $ch = curl_init();

        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'Authorization: Bearer ' . $token,
                'x-conta-corrente: ' . $this->contaCorrente
            ],
            CURLOPT_POSTFIELDS => json_encode([
                'motivoCancelamento' => $motivoCancelamento
            ]),
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return $httpCode === 204 || $httpCode === 200;
    }
}

