<?php

namespace App\Services\Sri;

use SoapClient;
use SoapFault;

/**
 * Envoltorio delgado sobre los dos web services SOAP que expone el SRI:
 * Recepción (valida y encola el XML firmado) y Autorización (consulta el
 * resultado final por clave de acceso). Requiere la extensión php-soap.
 */
class SriSoapClient
{
    public function __construct(private int $ambiente)
    {
    }

    /** Envía el XML firmado (como texto) al web service de Recepción. */
    public function enviarRecepcion(string $xmlFirmado): array
    {
        $client = $this->cliente('recepcion');

        try {
            $respuesta = $client->validarComprobante(['xml' => base64_encode($xmlFirmado)]);
        } catch (SoapFault $e) {
            throw new SriException('Error de comunicación con Recepción SRI: '.$e->getMessage());
        }

        $resultado = $respuesta->RespuestaRecepcionComprobante ?? $respuesta;
        $estado = $resultado->estado ?? 'DESCONOCIDO';

        return [
            'estado' => (string) $estado,
            'mensajes' => $this->extraerMensajes($resultado),
        ];
    }

    /** Consulta el estado final (autorizado / no autorizado) por clave de acceso. */
    public function consultarAutorizacion(string $claveAcceso): array
    {
        $client = $this->cliente('autorizacion');

        try {
            $respuesta = $client->autorizacionComprobante(['claveAccesoComprobante' => $claveAcceso]);
        } catch (SoapFault $e) {
            throw new SriException('Error de comunicación con Autorización SRI: '.$e->getMessage());
        }

        $resultado = $respuesta->RespuestaAutorizacionComprobante ?? $respuesta;
        $autorizaciones = $resultado->autorizaciones->autorizacion ?? null;

        if (! $autorizaciones) {
            return ['estado' => 'EN_PROCESO', 'mensajes' => [], 'numeroAutorizacion' => null, 'fechaAutorizacion' => null, 'comprobante' => null];
        }

        // Puede venir un solo objeto o un arreglo si hubo más de un intento de autorización.
        $autorizacion = is_array($autorizaciones) ? end($autorizaciones) : $autorizaciones;

        return [
            'estado' => (string) ($autorizacion->estado ?? 'DESCONOCIDO'),
            'numeroAutorizacion' => (string) ($autorizacion->numeroAutorizacion ?? null),
            'fechaAutorizacion' => (string) ($autorizacion->fechaAutorizacion ?? null),
            'comprobante' => $autorizacion->comprobante ?? null,
            'mensajes' => $this->extraerMensajes($autorizacion),
        ];
    }

    private function cliente(string $servicio): SoapClient
    {
        $wsdl = config("sri.wsdl.{$this->ambiente}.{$servicio}");

        if (! $wsdl) {
            throw new SriException("No hay una URL de WSDL configurada para el servicio '{$servicio}' en el ambiente {$this->ambiente}.");
        }

        $opciones = [
            'trace' => true,
            'exceptions' => true,
            'connection_timeout' => (int) config('sri.timeout', 25),
            'cache_wsdl' => WSDL_CACHE_NONE,
            'stream_context' => stream_context_create([
                'ssl' => [
                    'verify_peer' => (bool) config('sri.verificar_ssl', true),
                    'verify_peer_name' => (bool) config('sri.verificar_ssl', true),
                ],
            ]),
        ];

        try {
            return new SoapClient($wsdl, $opciones);
        } catch (SoapFault $e) {
            throw new SriException("No se pudo conectar al WSDL de {$servicio} del SRI: ".$e->getMessage());
        }
    }

    private function extraerMensajes($nodo): array
    {
        $mensajes = $nodo->comprobantes->comprobante->mensajes->mensaje ?? ($nodo->mensajes->mensaje ?? null);

        if (! $mensajes) {
            return [];
        }

        $lista = is_array($mensajes) ? $mensajes : [$mensajes];

        return array_map(function ($m) {
            $identificador = $m->identificador ?? '';
            $mensaje = $m->mensaje ?? '';
            $info = $m->informacionAdicional ?? '';

            return trim("[{$identificador}] {$mensaje} {$info}");
        }, $lista);
    }
}
