<?php

namespace App\Core;

use NFePHP\Common\Certificate;
use Exception;

/**
 * Função wrapper no namespace App\Core para interceptar chamadas a shell_exec
 * Redireciona para a função global, evitando erro "Call to undefined function App\Core\shell_exec()"
 */
if (!function_exists(__NAMESPACE__ . '\\shell_exec')) {
    function shell_exec(string $command): ?string
    {
        // Se shell_exec global não estiver disponível, retornar null sem erro fatal
        if (!\function_exists('\\shell_exec')) {
            return null;
        }

        // Chamar função global explicitamente
        return \call_user_func('\\shell_exec', $command);
    }
}

/**
 * Helper para carregar certificados digitais
 * Compatível com OpenSSL 1.x e 3.x
 */
class CertificateHelper
{
    /**
     * Carrega certificado digital a partir de arquivo .pfx/.p12
     * Compatível com OpenSSL 3.x (resolve erro error:0308010C)
     *
     * @param string $certPath Caminho do arquivo .pfx
     * @param string $senha Senha do certificado
     * @return Certificate
     * @throws Exception
     */
    public static function loadPfx(string $certPath, string $senha): Certificate
    {
        try {
            if (!\file_exists($certPath)) {
                throw new Exception("Certificado não encontrado: {$certPath}");
            }

            error_log("CertificateHelper::loadPfx - Iniciando carregamento do certificado: {$certPath}");

            $certContent = \file_get_contents($certPath);
            if ($certContent === false) {
                throw new Exception("Não foi possível ler o conteúdo do certificado: {$certPath}");
            }

            // Detectar versão do OpenSSL através da extensão PHP (sem shell_exec)
            $opensslVersionString = '';
            if (defined('OPENSSL_VERSION_TEXT')) {
                $opensslVersionString = \OPENSSL_VERSION_TEXT;
            } elseif (function_exists('openssl_version_text')) {
                $opensslVersionString = \openssl_version_text();
            }
            $isOpenSSL3 = !empty($opensslVersionString) && \strpos($opensslVersionString, 'OpenSSL 3') !== false;

            error_log("CertificateHelper::loadPfx - OpenSSL detectado: {$opensslVersionString}");
            error_log("CertificateHelper::loadPfx - OpenSSL 3.x: " . ($isOpenSSL3 ? 'SIM' : 'NÃO'));

            // Caminho do openssl-legacy.cnf (pode estar em src/Integrations/NFe/ ou na raiz)
            $basePath = \dirname(\dirname(__DIR__));
            $opensslConfPath = $basePath . '/src/Integrations/NFe/openssl-legacy.cnf';
            if (!\file_exists($opensslConfPath)) {
                $opensslConfPath = $basePath . '/openssl-legacy.cnf';
            }

            error_log("CertificateHelper::loadPfx - Base path: {$basePath}");
            error_log("CertificateHelper::loadPfx - Openssl config path: {$opensslConfPath}");
            error_log("CertificateHelper::loadPfx - Config existe: " . (\file_exists($opensslConfPath) ? 'SIM' : 'NÃO'));

            $opensslConfOriginal = \getenv('OPENSSL_CONF');

            // MÉTODO 1: Tentar openssl_pkcs12_read (função PHP nativa - funciona mesmo sem shell_exec)
            // Se for OpenSSL 3.x, configurar OPENSSL_CONF antes
            if ($isOpenSSL3 && \file_exists($opensslConfPath)) {
                \putenv('OPENSSL_CONF=' . $opensslConfPath);
                error_log("CertificateHelper::loadPfx - OPENSSL_CONF configurado para: {$opensslConfPath}");
            }

            try {
                $certData = [];
                if (\openssl_pkcs12_read($certContent, $certData, $senha)) {
                    $pemCert = $certData['cert'];
                    $pemKey = $certData['pkey'];

                    // Restaurar OPENSSL_CONF original
                    if ($opensslConfOriginal !== false) {
                        \putenv('OPENSSL_CONF=' . $opensslConfOriginal);
                    } else {
                        \putenv('OPENSSL_CONF');
                    }

                    error_log("CertificateHelper::loadPfx - ✅ Certificado carregado via método 1 (openssl_pkcs12_read PHP nativo)");
                    return Certificate::readPem($pemCert, $pemKey);
                } else {
                    $opensslError = \openssl_error_string();
                    error_log("CertificateHelper::loadPfx - Método 1 falhou - openssl_pkcs12_read retornou false");
                    if ($opensslError) {
                        error_log("CertificateHelper::loadPfx - Erro OpenSSL: " . $opensslError);
                    }
                }
            } catch (Exception $e1) {
                error_log("CertificateHelper::loadPfx - Método 1 falhou com exceção: " . $e1->getMessage());
                // Continuar para método 2
            }

            // MÉTODO 2: Fallback para NFePHP::Certificate::readPfx (método mais direto)
            // Restaurar OPENSSL_CONF antes de tentar
            if ($opensslConfOriginal !== false) {
                \putenv('OPENSSL_CONF=' . $opensslConfOriginal);
            } else {
                \putenv('OPENSSL_CONF');
            }

            try {
                error_log("CertificateHelper::loadPfx - Tentando método 2 (Certificate::readPfx)");
                $certificado = Certificate::readPfx($certContent, $senha);

                error_log("CertificateHelper::loadPfx - ✅ Certificado carregado via método 2 (Certificate::readPfx)");
                return $certificado;
            } catch (Exception $e2) {
                error_log("CertificateHelper::loadPfx - Método 2 falhou: " . $e2->getMessage());

                // Se for OpenSSL 3.x, dar mensagem mais específica
                if ($isOpenSSL3) {
                    throw new Exception(
                        "Erro ao carregar certificado no OpenSSL 3.x. " .
                        "Certificado pode usar algoritmos legados não suportados. " .
                        "Soluções possíveis: " .
                        "1) Configure OPENSSL_CONF no servidor para usar openssl-legacy.cnf, " .
                        "2) Ou atualize o certificado para usar algoritmos compatíveis. " .
                        "Erro detalhado: " . $e2->getMessage()
                    );
                }

                throw new Exception("Erro ao carregar certificado: " . $e2->getMessage());
            }
        } catch (Exception $e) {
            error_log("CertificateHelper::loadPfx - Erro capturado: " . $e->getMessage());
            error_log("CertificateHelper::loadPfx - Stack trace: " . $e->getTraceAsString());
            error_log("CertificateHelper::loadPfx - Arquivo: " . $e->getFile() . ":" . $e->getLine());
            throw $e;
        } catch (\Throwable $e) {
            error_log("CertificateHelper::loadPfx - Erro fatal: " . $e->getMessage());
            error_log("CertificateHelper::loadPfx - Stack trace: " . $e->getTraceAsString());
            throw new Exception("Erro ao carregar certificado: " . $e->getMessage(), 0, $e);
        }
    }
}
