<?php
// /var/www/html/terrapro/afip/AfipWsaa.php

class AfipWsaa
{
    protected $config;

    public function __construct(array $config)
    {
        $this->config = $config;
    }

    protected function getWsaaWsdl()
    {
        if ($this->config['modo'] === 'PROD') {
            return $this->config['wsaa_wsdl_prod'];
        }
        return $this->config['wsaa_wsdl_homo'];
    }

    /**
     * Devuelve el TA (SimpleXMLElement) válido.
     * Si el archivo existe y no está vencido, lo reutiliza.
     */
    public function getTA()
    {
        $taFile = $this->config['ta_file'];

        if (file_exists($taFile)) {
            $taXml = simplexml_load_file($taFile);
            if ($taXml !== false && isset($taXml->header->expirationTime)) {
                $expTime = strtotime((string)$taXml->header->expirationTime);
                if ($expTime && $expTime > time() + 60) {
                    // Aún válido (dejamos 60 seg de margen)
                    return $taXml;
                }
            }
        }

        // Generar TA nuevo
        $taXml = $this->loginCms();
        if ($taXml) {
            $dir = dirname($taFile);
            if (!is_dir($dir)) {
                mkdir($dir, 0775, true);
            }
            file_put_contents($taFile, $taXml->asXML());
        }

        return $taXml;
    }

    /**
     * Genera un loginTicketRequest, lo firma con openssl (CLI) y llama al WSAA.
     */
    protected function loginCms()
    {
        $uniqueId       = time();
        $generationTime = date('c', $uniqueId - 60);
        $expirationTime = date('c', $uniqueId + 60 * 60); // 1 hora

        $service = $this->config['service']; // 'wsfe'

        $loginTicketRequest = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<loginTicketRequest version="1.0">
    <header>
        <uniqueId>{$uniqueId}</uniqueId>
        <generationTime>{$generationTime}</generationTime>
        <expirationTime>{$expirationTime}</expirationTime>
    </header>
    <service>{$service}</service>
</loginTicketRequest>
XML;

        $certPath = $this->config['cert'];
        $keyPath  = $this->config['key'];
        $keyPass  = $this->config['key_pass'];

        if (!is_file($certPath)) {
            throw new \RuntimeException("No se encontró el certificado en: {$certPath}");
        }
        if (!is_file($keyPath)) {
            throw new \RuntimeException("No se encontró la clave privada en: {$keyPath}");
        }

        $tempDir = sys_get_temp_dir();
        $tempIn  = tempnam($tempDir, 'afip_ltr_');
        $tempOut = tempnam($tempDir, 'afip_cms_');

        file_put_contents($tempIn, $loginTicketRequest);

        // Construimos comando openssl smime -sign ...
        $cmd  = 'openssl smime -sign';
        $cmd .= ' -signer ' . escapeshellarg($certPath);
        $cmd .= ' -inkey ' . escapeshellarg($keyPath);
        $cmd .= ' -in ' . escapeshellarg($tempIn);
        $cmd .= ' -out ' . escapeshellarg($tempOut);
        $cmd .= ' -outform DER';
        $cmd .= ' -nodetach';

        // Si en algún momento tu clave tiene pass, habría que agregar:
        // if ($keyPass !== '') {
        //     $cmd .= ' -passin pass:' . escapeshellarg($keyPass);
        // }

        $cmd .= ' 2>&1';

        $output = [];
        $ret    = 0;
        exec($cmd, $output, $ret);

        if ($ret !== 0) {
            @unlink($tempIn);
            @unlink($tempOut);
            throw new \RuntimeException(
                "Error ejecutando openssl smime -sign (código {$ret}).\nComando:\n{$cmd}\nSalida:\n" . implode("\n", $output)
            );
        }

        $cmsDer = file_get_contents($tempOut);
        @unlink($tempIn);
        @unlink($tempOut);

        if ($cmsDer === false || $cmsDer === '') {
            throw new \RuntimeException("No se pudo leer el CMS generado por openssl (archivo vacío).");
        }

        // AFIP espera base64 del CMS binario (DER)
        $cms = base64_encode($cmsDer);

        $wsaaWsdl = $this->getWsaaWsdl();

        $client = new SoapClient($wsaaWsdl, [
            'soap_version' => SOAP_1_1,
            'location'     => str_replace('?WSDL', '', $wsaaWsdl),
            'trace'        => 1,
            'exceptions'   => true,
        ]);

        // Llamar a loginCms en AFIP con el CMS base64
        $result = $client->loginCms(['in0' => $cms]);

        if (!isset($result->loginCmsReturn)) {
            throw new \RuntimeException('Respuesta inesperada de loginCms (faltó loginCmsReturn)');
        }

        $taXml = simplexml_load_string($result->loginCmsReturn);
        if ($taXml === false) {
            throw new \RuntimeException('No se pudo parsear el TA devuelto por AFIP');
        }

        return $taXml;
    }
}
