<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

ini_set('log_errors', 1);
ini_set('error_log', 'erro.log');

header("Content-Type: application/json");

set_error_handler(function ($errno, $errstr, $errfile, $errline) {
    http_response_code(500);
    echo json_encode(["erro" => "Erro capturado: [$errno] $errstr - Arquivo: $errfile, Linha: $errline"]);
    exit;
});

set_exception_handler(function ($exception) {
    http_response_code(500);
    echo json_encode(["erro" => "Exceção capturada: " . $exception->getMessage()]);
    exit;
});

date_default_timezone_set('America/Sao_Paulo');

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(["erro" => "Método não permitido. Use POST."]);
    exit;
}

$input = json_decode(file_get_contents("php://input"), true);

if (!isset($input['arquivoCertificado'], $input['senhaCertificado'], $input['assinante'])) {
    http_response_code(400);
    echo json_encode(["erro" => "Parâmetros obrigatórios ausentes."]);
    exit;
}

$arquivoCertificado = $input['arquivoCertificado'];
$senhaCertificado = $input['senhaCertificado'];
$assinante = $input['assinante'];

$destinatario = [
    'numero' => '17422651000172',
    'nome' => 'CF RJ CONTABILIDADE LTDA',
    'tipo' => 'PJ',
    'papel' => 'contratante'
];

function carregarCertificado($arquivo, $senha)
{
    $certs = [];
    if (!file_exists($arquivo)) {
        throw new Exception("Erro: Certificado não encontrado.");
    }
    $conteudo = file_get_contents($arquivo);
    if (!openssl_pkcs12_read($conteudo, $certs, $senha)) {
        throw new Exception("Erro: Não foi possível carregar o certificado digital. Senha errada?");
    }
    return $certs;
}

function obterPrivateKey($certificados)
{
    return $certificados["pkey"];
}

function obterx509Certificate($certificados)
{
    $x509Certificate = $certificados["cert"];
    return trim(str_replace(["-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----", "\r", "\n"], '', $x509Certificate));
}

function montarTermoAutorizacao($destinatario, $assinante)
{
    $xml = new SimpleXMLElement("<?xml version='1.0' encoding='UTF-8'?><termoDeAutorizacao></termoDeAutorizacao>");
    $dados = $xml->addChild('dados');

    $sistema = $dados->addChild('sistema');
    $sistema->addAttribute('id', 'API Integra Contador');

    $termo = $dados->addChild('termo');
    $termo->addAttribute('texto', 'Autorizo a empresa CONTRATANTE, identificada neste termo de autorização como DESTINATÁRIO, a executar as requisições dos serviços web disponibilizados pela API INTEGRA CONTADOR, onde terei o papel de AUTOR PEDIDO DE DADOS no corpo da mensagem enviada na requisição do serviço web. Esse termo de autorização está assinado digitalmente com o certificado digital do PROCURADOR ou OUTORGADO DO CONTRIBUINTE responsável, identificado como AUTOR DO PEDIDO DE DADOS.');

    $avisoLegal = $dados->addChild('avisoLegal');
    $avisoLegal->addAttribute('texto', 'O acesso a estas informações foi autorizado pelo próprio PROCURADOR ou OUTORGADO DO CONTRIBUINTE, responsável pela informação, via assinatura digital. É dever do destinatário da autorização e consumidor deste acesso observar a adoção de base legal para o tratamento dos dados recebidos conforme artigos 7º ou 11º da LGPD (Lei n.º 13.709, de 14 de agosto de 2018), aos direitos do titular dos dados (art. 9º, 17 e 18, da LGPD) e aos princípios que norteiam todos os tratamentos de dados no Brasil (art. 6º, da LGPD).');

    $finalidade = $dados->addChild('finalidade');
    $finalidade->addAttribute('texto', 'A finalidade única e exclusiva desse TERMO DE AUTORIZAÇÃO, é garantir que o CONTRATANTE apresente a API INTEGRA CONTADOR esse consentimento do PROCURADOR ou OUTORGADO DO CONTRIBUINTE assinado digitalmente, para que possa realizar as requisições dos serviços web da API INTEGRA CONTADOR em nome do AUTOR PEDIDO DE DADOS (PROCURADOR ou OUTORGADO DO CONTRIBUINTE).');

    $dados->addChild('dataAssinatura')->addAttribute('data', date("Ymd"));
    $dados->addChild('vigencia')->addAttribute('data', date('Ymd', strtotime("+30 days")));

    $destNode = $dados->addChild('destinatario');
    foreach ($destinatario as $key => $value) {
        $destNode->addAttribute($key, $value);
    }

    $assinNode = $dados->addChild('assinadoPor');
    foreach ($assinante as $key => $value) {
        $assinNode->addAttribute($key, $value);
    }

    return $xml;
}

function assinar($xmlNaoAssinado, $certificados)
{
   // Leitura do XML que contém o documento Termo de Autorização ainda não assinado
	$xml = new DOMDocument();

	// Preservando os espaços em branco
	$xml->preserveWhiteSpace = true;
	$xml->formatOutput = false;

	$xml->loadXML($xmlNaoAssinado->asXML());

	// Canonizar o conteúdo, exclusivo e sem comentários
	if (!$xml->documentElement) {
		throw new UnexpectedValueException('Indefindo o elemento documentElement');
	}

	$canonicalData = $xml->documentElement->C14N(true, false);

	// Calcular o digest
	$digestValue = openssl_digest($canonicalData, "sha256", true);
	if ($digestValue === false) {
		throw new UnexpectedValueException('Invalid digest value');
	}

	// Codificar para base64 (encode)
	$digestValue = base64_encode($digestValue);

	// Adiciona os elementos que vai compor a tag Signature com a assinatura digital
	$signatureElement = $xml->createElement('Signature');
	$signatureElement->setAttribute('xmlns', 'http://www.w3.org/2000/09/xmldsig#');
	$xml->documentElement->appendChild($signatureElement);

	$signedInfoElement = $xml->createElement('SignedInfo');
	$signatureElement->appendChild($signedInfoElement);

	$canonicalizationMethodElement = $xml->createElement('CanonicalizationMethod');
	$canonicalizationMethodElement->setAttribute('Algorithm', 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315');
	$signedInfoElement->appendChild($canonicalizationMethodElement);

	$signatureMethodElement = $xml->createElement('SignatureMethod');
	$signatureMethodElement->setAttribute('Algorithm', 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256');
	$signedInfoElement->appendChild($signatureMethodElement);

	$referenceElement = $xml->createElement('Reference');
	$referenceElement->setAttribute('URI', '');
	$signedInfoElement->appendChild($referenceElement);

	$transformsElement = $xml->createElement('Transforms');
	$referenceElement->appendChild($transformsElement);

	$transformElement = $xml->createElement('Transform');
	$transformElement->setAttribute('Algorithm', 'http://www.w3.org/2000/09/xmldsig#enveloped-signature');
	$transformsElement->appendChild($transformElement);

	$transformElement = $xml->createElement('Transform');
	$transformElement->setAttribute('Algorithm', 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315');
	$transformsElement->appendChild($transformElement);

	$digestMethodElement = $xml->createElement('DigestMethod');
	$digestMethodElement->setAttribute('Algorithm', 'http://www.w3.org/2001/04/xmlenc#sha256');
	$referenceElement->appendChild($digestMethodElement);

	$digestValueElement = $xml->createElement('DigestValue', $digestValue);
	$referenceElement->appendChild($digestValueElement);

	$signatureValueElement = $xml->createElement('SignatureValue', '');
	$signatureElement->appendChild($signatureValueElement);

	$keyInfoElement = $xml->createElement('KeyInfo');
	$signatureElement->appendChild($keyInfoElement);

	$X509Data = $xml->createElement('X509Data');
	$keyInfoElement->appendChild($X509Data);

	$X509Certificate = $xml->createElement('X509Certificate', obterx509Certificate($certificados));
	$X509Data->appendChild($X509Certificate);

	$c14nSignedInfo = $signedInfoElement->C14N(true, false);
	
	$status = openssl_sign($c14nSignedInfo, $signatureValue, obterPrivateKey($certificados), OPENSSL_ALGO_SHA256);

	if (!$status) {
		throw new XmlSignerException('Falha no cálculo da assinatura.');
	}

	$xpath = new DOMXpath($xml);
	$signatureValueElement = $xpath->query('//SignatureValue', $signatureElement)->item(0);
	$signatureValueElement->nodeValue = base64_encode($signatureValue);
	
	return $xml->saveXML(); 
}

try {
    $certificados = carregarCertificado($arquivoCertificado, $senhaCertificado);
    $xmlNaoAssinado = montarTermoAutorizacao($destinatario, $assinante);
    $xmlAssinado = assinar($xmlNaoAssinado, $certificados);

    echo json_encode([
        "xml" => $xmlNaoAssinado,
        "xml_base64" => base64_encode($xmlAssinado)
    ]);
} catch (Exception $e) {
    http_response_code(500);
    echo json_encode(["erro" => $e->getMessage()]);
}
?>