I N T E G R A R    A P I     D E    S U N A T  
------------------------------------------------------------------
==================================================================

------------------------------------------------------------------
PASO 1 .env 
------------------------------------------------------------------
SUNAT_CLIENT_ID=513aafd5-4dac-4adf-8c32-70cd7aa32fee
SUNAT_CLIENT_SECRET=fQhefGNIhQdUzNXc2wfqyg==
SUNAT_RUC_CONSULTANTE=20613010433


------------------------------------------------------------------
PASO 2 web.php 
------------------------------------------------------------------
// Rutas para la gestión de rendidores
Route::get('/admin/gastos/details', function () { return view('admin.rendidor.gastos.details-gastos');})->name('admin.gastos.details');
Route::post('/qr/read-upload', [QrReaderController::class, 'readQrFromUpload'])->name('qr.read.upload');

Route::post('/sunat/validar-comprobante', [SunatController::class, 'validarComprobante']);

------------------------------------------------------------------
PASO 3 SunatController.php 
------------------------------------------------------------------

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class SunatController extends Controller
{
    public function validarComprobante(Request $request)
    {
        $validated = $request->validate([
            'numRuc' => ['required','digits:11'],
            'codComp' => ['required','string','size:2'],
            'numeroSerie' => ['required','string','max:4'],
            'numero' => ['required','integer','min:1'],
            'fechaEmision' => ['required','date_format:d/m/Y'],
            'monto' => ['required','numeric','min:0'],
        ]);

        try {
            $token = $this->getSunatToken();

            // Endpoint validar: /contribuyente/contribuyentes/RUC/validarcomprobante :contentReference[oaicite:4]{index=4}
            $rucConsultante = config('services.sunat.ruc_consultante');
            $url = "https://api.sunat.gob.pe/v1/contribuyente/contribuyentes/{$rucConsultante}/validarcomprobante";

            $resp = Http::withToken($token)
                ->acceptJson()
                ->asJson()
                ->post($url, $validated);

            if (!$resp->ok()) {
                return response()->json([
                    'success' => false,
                    'message' => 'SUNAT respondió con error HTTP',
                    'detail' => $resp->body(),
                ], 502);
            }

            $json = $resp->json();

            // Normalizamos campos (por si vienen con Observaciones vs observaciones)
            $data = $json['data'] ?? [];
            if (isset($data['Observaciones']) && !isset($data['observaciones'])) {
                $data['observaciones'] = $data['Observaciones'];
            }

            return response()->json([
                'success' => true,
                'message' => $json['message'] ?? 'OK',
                'data' => $data,
            ]);

        } catch (\Throwable $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error interno al validar en SUNAT',
                'detail' => $e->getMessage(),
            ], 500);
        }
    }

    private function getSunatToken(): string
    {
        return Cache::remember('sunat_access_token', 3300, function () {
            $clientId = config('services.sunat.client_id');
            $clientSecret = config('services.sunat.client_secret');

            // Token endpoint :contentReference[oaicite:5]{index=5}
            $tokenUrl = "https://api-seguridad.sunat.gob.pe/v1/clientesextranet/{$clientId}/oauth2/token/";

            $resp = Http::asForm()->post($tokenUrl, [
                'grant_type' => 'client_credentials',
                'scope' => 'https://api.sunat.gob.pe/v1/contribuyente/contribuyentes',
                'client_id' => $clientId,
                'client_secret' => $clientSecret,
            ]);

            if (!$resp->ok()) {
                throw new \RuntimeException("No se pudo obtener token SUNAT: " . $resp->body());
            }

            $json = $resp->json();
            if (empty($json['access_token'])) {
                throw new \RuntimeException("Token SUNAT inválido: " . $resp->body());
            }

            return $json['access_token'];
        });
    }
}

------------------------------------------------------------------
PASO 4 services.php 
------------------------------------------------------------------
'sunat' => [
        'client_id' => env('SUNAT_CLIENT_ID'),
        'client_secret' => env('SUNAT_CLIENT_SECRET'),
        'ruc_consultante' => env('SUNAT_RUC_CONSULTANTE'),
    ],


------------------------------------------------------------------
PASO 5 QrReaderController.php
------------------------------------------------------------------
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Zxing\QrReader;

class QrReaderController extends Controller
{
    /**
     * Leer QR desde imagen subida o captura de cámara
     */
    public function readQrFromUpload(Request $request)
    {
        $request->validate([
            'qr_image' => 'required|image|mimes:jpeg,png,jpg,gif,webp|max:5120' // Max 5MB
        ]);

        try {
            // Obtener la imagen subida
            $image = $request->file('qr_image');

            // Guardar temporalmente la imagen
            $path = $image->store('temp', 'public');
            $fullPath = storage_path('app/public/' . $path);

            // Leer el código QR
            $qrcode = new QrReader($fullPath);
            $text = $qrcode->text();

            // Eliminar la imagen temporal
            @unlink($fullPath);

            if ($text) {
                // Parsear los datos de la factura SUNAT
                $facturaData = $this->parseFacturaSunat($text);

                return response()->json([
                    'success' => true,
                    'message' => 'QR leído correctamente',
                    'raw_data' => $text,
                    'data' => $facturaData
                ]);
            } else {
                return response()->json([
                    'success' => false,
                    'message' => 'No se pudo detectar un código QR en la imagen'
                ], 400);
            }
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error al procesar el QR: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Parsear datos del QR de factura electrónica SUNAT
     * Formato: RUC|TIPO_DOC|SERIE|NUMERO|IGV|TOTAL|FECHA|TIPO_DOC_RECEPTOR|NUM_DOC_RECEPTOR|HASH|
     */
    private function parseFacturaSunat($text)
    {
        // Dividir por el separador "|"
        $parts = explode('|', $text);

        // Si no tiene el formato esperado, devolver raw
        if (count($parts) < 9) {
            return [
                'raw' => $text,
                'formato_valido' => false
            ];
        }

        // Mapear tipo de documento
        $tiposDoc = [
            '01' => 'Factura',
            '03' => 'Boleta de Venta',
            '07' => 'Nota de Crédito',
            '08' => 'Nota de Débito',
            '09' => 'Guía de Remisión',
            '12' => 'Ticket de Máquina Registradora',
            '13' => 'Documento autorizado por Régimen Especial'
        ];

        $tiposDocReceptor = [
            '1' => 'DNI',
            '4' => 'Carnet de Extranjería',
            '6' => 'RUC',
            '7' => 'Pasaporte',
            'A' => 'Cédula Diplomática'
        ];

        $tipoDoc = $parts[1] ?? '';
        $tipoDocReceptor = $parts[7] ?? '';

        return [
            'formato_valido' => true,
            'ruc_emisor' => $parts[0] ?? '',
            'tipo_documento' => $tipoDoc,
            'tipo_documento_nombre' => $tiposDoc[$tipoDoc] ?? 'Desconocido',
            'serie' => $parts[2] ?? '',
            'numero' => $parts[3] ?? '',
            'igv' => $parts[4] ?? '0.00',
            'total' => $parts[5] ?? '0.00',
            'fecha_emision' => $parts[6] ?? '',
            'tipo_doc_receptor' => $tipoDocReceptor,
            'tipo_doc_receptor_nombre' => $tiposDocReceptor[$tipoDocReceptor] ?? 'Desconocido',
            'num_doc_receptor' => $parts[8] ?? '',
            'hash_sunat' => $parts[9] ?? '',
        ];
    }
}

------------------------------------------------------------------
PASO 6 details-gastos.blade.php
------------------------------------------------------------------

@extends('admin.layouts.master')

@section('title', 'Detalle de Gasto')

@section('content')
<div class="content">

    <!-- Start Content-->
    <div class="container-fluid">

        <!-- end page title -->
        <div class="row mt-2">
            <div class="col-12">
                <div class="card">
                    <div class="card-body">

                        <div class="container-fluid">
                            <div class="text-center mb-4">
                                <h5 class="text-primary fw-bold">Liquidación de Gastos | Viaje</h5>
                                <hr>
                            </div>

                            <div class="row">
                                <div class="col-lg-5 border-end">
                                    <h5 class="mb-3 text-primary">Datos generales</h5>

                                    <div class="mb-3">
                                        <label class="form-label">RUC Emisor</label>
                                        <input id="ruc_emisor" type="text"
                                            class="form-control bg-light"
                                            placeholder="Ej: 20123456789">
                                    </div>

                                    <div class="row mb-3">
                                        <div class="col-md-6">
                                            <label class="form-label">Tipo Comprobante</label>
                                            <!-- Tipo comprobante (mejor que guarde codComp) -->
                                            <select id="cod_comp" class="form-select bg-light">
                                                <option value="">Seleccione</option>
                                                <option value="01">Factura</option>
                                                <option value="03">Boleta</option>
                                                <option value="07">Nota de Crédito</option>
                                                <option value="08">Nota de Débito</option>
                                            </select>
                                        </div>

                                        <div class="col-md-3">
                                            <label class="form-label">Serie</label>
                                            <input id="serie" type="text" class="form-control bg-light" placeholder="Ej: F001">
                                        </div>

                                        <div class="col-md-3">
                                            <label class="form-label">Número</label>
                                            <input id="numero" type="text" class="form-control bg-light" placeholder="Ej: 00012345">
                                        </div>
                                    </div>

                                    <div class="row mb-3">
                                        <div class="col-md-4">
                                            <label class="form-label">IGV</label>
                                            <input id="igv" type="number"
                                                step="0.01"
                                                class="form-control bg-light"
                                                placeholder="Ej: 180.00">
                                        </div>

                                        <div class="col-md-4">
                                            <label class="form-label">Total</label>
                                            <input id="total" type="number" step="0.01" class="form-control bg-light" placeholder="Ej: 1180.00">
                                        </div>

                                        <div class="col-md-4">
                                            <label class="form-label">Fecha de Emisión</label>
                                            <div class="input-group">
                                                <input id="fecha_emision" type="text"
                                                    class="form-control bg-light"
                                                    placeholder="DD/MM/YYYY">
                                                <span class="input-group-text">
                                                    <i class="mdi mdi-calendar"></i>
                                                </span>
                                            </div>
                                        </div>
                                    </div>

                                    <div class="row mb-3">
                                        <div class="col-md-6">
                                            <label class="form-label">Doc. Receptor</label>
                                            <input id="doc-receptor" type="text"
                                                class="form-control bg-light"
                                                placeholder="DNI / RUC del receptor">
                                        </div>

                                        <div class="col-md-6">
                                            <label class="form-label">Hash SUNAT</label>
                                            <input id="hash" type="text"
                                                class="form-control bg-light"
                                                placeholder="Hash generado por SUNAT">
                                        </div>
                                    </div>

                                    <h5 class="mt-4 mb-1 text-primary">Datos personalizados</h5>
                                    <p class="text-muted small mb-3">Estos datos son solicitados por tu
                                        empresa.</p>

                                    <div class="mb-3">
                                        <label class="form-label">Categoría</label>
                                        <select class="form-select bg-light">
                                            <option selected>Selecciona una categoría</option>
                                        </select>
                                    </div>

                                    <div class="mb-3">
                                        <label class="form-label">RUC Proveedor</label>
                                        <input type="text" class="form-control bg-light"
                                            placeholder="RUC Proveedor">
                                    </div>

                                    <div class="row">
                                        <div class="col-md-6 mb-3">
                                            <label class="form-label">Número de Documento</label>
                                            <input type="text" class="form-control bg-light"
                                                placeholder="Número de Documento">
                                        </div>
                                        <div class="col-md-6 mb-3">
                                            <label class="form-label">Tipo de Documento</label>
                                            <select class="form-select bg-light">
                                                <option>Tipo de Documento</option>
                                            </select>
                                        </div>
                                    </div>

                                    <div class="row">
                                        <div class="col-md-6 mb-3">
                                            <label class="form-label">Monto Detracción</label>
                                            <input type="text" class="form-control bg-light"
                                                placeholder="Monto Detracción">
                                        </div>
                                        <div class="col-md-6 mb-3">
                                            <label class="form-label">Retención 3%</label>
                                            <input type="text" class="form-control bg-light"
                                                placeholder="Retención 3%">
                                        </div>
                                    </div>

                                    <div class="mb-3">
                                        <label class="form-label">Nro. Operación</label>
                                        <small class="d-block text-muted mb-1">Solo aplica para "Devolución
                                            en Caja"</small>
                                        <input type="text" class="form-control bg-light"
                                            placeholder="Nro. Operación">
                                    </div>

                                    <div class="mb-3">
                                        <label class="form-label">Nota</label>
                                        <textarea class="form-control bg-light" rows="3"
                                            placeholder="Escribe una nota o comentario"></textarea>
                                    </div>

                                </div>

                                <div class="col-lg-7 ps-lg-4">
                                    <div id="viewerControls"
                                        class="d-flex justify-content-between align-items-center mb-2 d-none">
                                        <span class="text-muted small fw-bold"><i
                                                class="mdi mdi-eye-outline me-1"></i>VISTA PREVIA</span>
                                        <div class="viewer-toolbar bg-light rounded-pill px-2 border">
                                            <button type="button" class="btn btn-link btn-sm text-dark"
                                                onclick="zoomImg(1.2)"><i
                                                    class="mdi mdi-plus font-18"></i></button>
                                            <button type="button" class="btn btn-link btn-sm text-dark"
                                                onclick="zoomImg(0.8)"><i
                                                    class="mdi mdi-minus font-18"></i></button>
                                            <button type="button" class="btn btn-link btn-sm text-dark"
                                                onclick="rotateImg()"><i
                                                    class="mdi mdi-rotate-right font-18"></i></button>
                                            <button type="button" class="btn btn-link btn-sm text-dark"
                                                onclick="resetImg()"><i
                                                    class="mdi mdi-fullscreen-exit font-18"></i></button>
                                            <span class="text-muted mx-1">|</span>
                                            <button type="button" class="btn btn-link btn-sm text-dark"
                                                onclick="downloadImg()"><i
                                                    class="mdi mdi-download font-18"></i></button>
                                            <button type="button" class="btn btn-link btn-sm text-danger"
                                                onclick="removeImg()"><i
                                                    class="mdi mdi-delete-outline font-18"></i></button>
                                        </div>
                                    </div>


                                    <!-- Botón para abrir cámara -->
                                    <div class="mb-3 text-center">
                                        <button type="button" class="btn btn-primary me-2" id="btnOpenCamera">
                                            <i class="mdi mdi-camera me-1"></i> Escanear con Cámara
                                        </button>
                                        <small class="text-muted d-block mt-2">O arrastra una imagen abajo</small>
                                    </div>

                                    <!-- Modal de Cámara -->
                                    <div class="modal fade" id="cameraModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static">
                                        <div class="modal-dialog modal-dialog-centered modal-xl">
                                            <div class="modal-content">
                                                <div class="modal-header bg-primary text-white">
                                                    <h5 class="modal-title">
                                                        <i class="mdi mdi-qrcode-scan me-2"></i>Escanear Código QR
                                                    </h5>
                                                    <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
                                                </div>
                                                <div class="modal-body p-3" style="background: #000;">
                                                    <!-- Contenedor del video -->
                                                    <div id="cameraContainer" class="position-relative rounded overflow-hidden" style="aspect-ratio: 4/3; max-height: 70vh; background: #1a1a1a;">

                                                        <!-- Video de la cámara -->
                                                        <video id="cameraVideo" autoplay playsinline muted class="w-100 h-100 position-absolute top-0 start-0" style="display: none; object-fit: cover;"></video>
                                                        <canvas id="cameraCanvas" class="d-none"></canvas>

                                                        <!-- Overlay con guía de escaneo -->
                                                        <div id="scanGuide" class="position-absolute w-100 h-100 top-0 start-0 d-flex align-items-center justify-content-center" style="display: none !important; pointer-events: none; z-index: 10;">
                                                            <div class="position-relative">
                                                                <!-- Marco del QR -->
                                                                <div class="qr-frame border border-3 border-primary rounded-3 bg-transparent position-relative"
                                                                    style="width: min(280px, 70vw); height: min(280px, 70vw); box-shadow: 0 0 0 99999px rgba(0, 0, 0, 0.6);">

                                                                    <!-- Esquinas animadas -->
                                                                    <div class="corner corner-tl"></div>
                                                                    <div class="corner corner-tr"></div>
                                                                    <div class="corner corner-bl"></div>
                                                                    <div class="corner corner-br"></div>

                                                                    <!-- Línea de escaneo -->
                                                                    <div class="scan-line"></div>
                                                                </div>

                                                                <!-- Instrucción -->
                                                                <div class="text-center mt-3 px-3">
                                                                    <p class="text-white fw-bold mb-1" style="text-shadow: 0 2px 4px rgba(0,0,0,0.8); font-size: 16px;">
                                                                        <i class="mdi mdi-target"></i> Coloca el QR dentro del marco
                                                                    </p>
                                                                    <p class="text-white-50 small mb-0" style="text-shadow: 0 2px 4px rgba(0,0,0,0.8);">
                                                                        Asegúrate de que haya buena iluminación
                                                                    </p>
                                                                </div>
                                                            </div>
                                                        </div>

                                                        <!-- Estado inicial (cargando cámara) -->
                                                        <div id="cameraInitial" class="position-absolute w-100 h-100 top-0 start-0 d-flex flex-column align-items-center justify-content-center">
                                                            <div class="spinner-border text-primary mb-3" style="width: 3rem; height: 3rem;" role="status">
                                                                <span class="visually-hidden">Cargando...</span>
                                                            </div>
                                                            <i class="mdi mdi-camera text-white-50 mb-3" style="font-size: 48px;"></i>
                                                            <p class="text-white fw-bold mb-1">Iniciando cámara...</p>
                                                            <p class="text-white-50 small">Por favor, permite el acceso a la cámara</p>
                                                        </div>

                                                        <!-- Estado de éxito -->
                                                        <div id="scanSuccess" class="position-absolute w-100 h-100 top-0 start-0 d-flex flex-column align-items-center justify-content-center d-none" style="background: rgba(0,0,0,0.9); z-index: 20;">
                                                            <div class="success-animation mb-3">
                                                                <div class="bg-success rounded-circle d-flex align-items-center justify-content-center mx-auto" style="width: 80px; height: 80px;">
                                                                    <i class="mdi mdi-check text-white" style="font-size: 48px;"></i>
                                                                </div>
                                                            </div>
                                                            <h4 class="text-white fw-bold mb-2">¡QR Detectado!</h4>
                                                            <p class="text-white-50">Procesando datos del comprobante...</p>
                                                            <div class="spinner-border spinner-border-sm text-primary mt-2" role="status"></div>
                                                        </div>

                                                        <!-- Estado de error -->
                                                        <div id="scanError" class="position-absolute w-100 h-100 top-0 start-0 d-flex flex-column align-items-center justify-content-center d-none" style="background: rgba(0,0,0,0.85); z-index: 20;">
                                                            <div class="bg-danger rounded-circle d-flex align-items-center justify-content-center mx-auto mb-3" style="width: 80px; height: 80px;">
                                                                <i class="mdi mdi-close text-white" style="font-size: 48px;"></i>
                                                            </div>
                                                            <h5 class="text-white fw-bold mb-2">No se detectó un QR válido</h5>
                                                            <p class="text-white-50 mb-3">Intenta nuevamente con mejor iluminación</p>
                                                            <button type="button" class="btn btn-light btn-sm" onclick="retryQRScan()">
                                                                <i class="mdi mdi-refresh me-1"></i> Intentar de nuevo
                                                            </button>
                                                        </div>
                                                    </div>

                                                    <!-- Instrucciones adicionales -->
                                                    <div class="text-center mt-3">
                                                        <small class="text-white-50">
                                                            <i class="mdi mdi-information-outline me-1"></i>
                                                            Tip: Mantén el teléfono estable y asegúrate de tener buena luz
                                                        </small>
                                                    </div>
                                                </div>

                                                <div class="modal-footer bg-light">
                                                    <button type="button" class="btn btn-light" id="btnCloseCamera">
                                                        <i class="mdi mdi-close me-1"></i> Cerrar
                                                    </button>
                                                    <button type="button" class="btn btn-primary" id="btnCaptureQR">
                                                        <i class="mdi mdi-camera-iris me-1"></i> Capturar QR
                                                    </button>
                                                </div>
                                            </div>
                                        </div>
                                    </div>

                                    <style>
                                        /* ========================================
   ESTILOS DEL MODAL DE CÁMARA
   ======================================== */

                                        #cameraModal .modal-content {
                                            border: none;
                                            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
                                        }

                                        #cameraVideo {
                                            object-fit: cover;
                                            width: 100%;
                                            height: 100%;
                                        }

                                        /* Marco del QR con esquinas */
                                        .qr-frame {
                                            transition: all 0.3s ease;
                                        }

                                        .corner {
                                            position: absolute;
                                            width: 30px;
                                            height: 30px;
                                            border: 3px solid #727cf5;
                                        }

                                        .corner-tl {
                                            top: -3px;
                                            left: -3px;
                                            border-right: none;
                                            border-bottom: none;
                                            border-radius: 8px 0 0 0;
                                        }

                                        .corner-tr {
                                            top: -3px;
                                            right: -3px;
                                            border-left: none;
                                            border-bottom: none;
                                            border-radius: 0 8px 0 0;
                                        }

                                        .corner-bl {
                                            bottom: -3px;
                                            left: -3px;
                                            border-right: none;
                                            border-top: none;
                                            border-radius: 0 0 0 8px;
                                        }

                                        .corner-br {
                                            bottom: -3px;
                                            right: -3px;
                                            border-left: none;
                                            border-top: none;
                                            border-radius: 0 0 8px 0;
                                        }

                                        /* Línea de escaneo animada */
                                        .scan-line {
                                            position: absolute;
                                            width: 100%;
                                            height: 2px;
                                            background: linear-gradient(90deg, transparent, #727cf5, transparent);
                                            top: 0;
                                            animation: scanMove 2s ease-in-out infinite;
                                        }

                                        @keyframes scanMove {

                                            0%,
                                            100% {
                                                top: 0;
                                                opacity: 0;
                                            }

                                            10% {
                                                opacity: 1;
                                            }

                                            90% {
                                                opacity: 1;
                                            }

                                            100% {
                                                top: 100%;
                                                opacity: 0;
                                            }
                                        }

                                        /* Animación de pulso para el marco */
                                        #scanGuide.scanning .qr-frame {
                                            animation: framePulse 2s ease-in-out infinite;
                                        }

                                        @keyframes framePulse {

                                            0%,
                                            100% {
                                                border-color: #727cf5;
                                                box-shadow: 0 0 0 99999px rgba(0, 0, 0, 0.6);
                                            }

                                            50% {
                                                border-color: #5a67d8;
                                                box-shadow: 0 0 0 99999px rgba(0, 0, 0, 0.7), 0 0 20px rgba(114, 124, 245, 0.5);
                                            }
                                        }

                                        /* Animación de éxito */
                                        .success-animation {
                                            animation: successPop 0.5s ease-out;
                                        }

                                        @keyframes successPop {
                                            0% {
                                                transform: scale(0);
                                                opacity: 0;
                                            }

                                            50% {
                                                transform: scale(1.2);
                                            }

                                            100% {
                                                transform: scale(1);
                                                opacity: 1;
                                            }
                                        }

                                        /* Responsive */
                                        @media (max-width: 768px) {
                                            #cameraModal .modal-xl {
                                                margin: 0.5rem;
                                                max-width: calc(100% - 1rem);
                                            }

                                            #cameraContainer {
                                                aspect-ratio: 3/4;
                                                max-height: 60vh;
                                            }

                                            .qr-frame {
                                                width: min(240px, 65vw) !important;
                                                height: min(240px, 65vw) !important;
                                            }

                                            .corner {
                                                width: 25px;
                                                height: 25px;
                                            }

                                            #cameraModal .modal-body {
                                                padding: 1rem !important;
                                            }
                                        }

                                        @media (max-width: 576px) {
                                            .qr-frame {
                                                width: min(200px, 60vw) !important;
                                                height: min(200px, 60vw) !important;
                                            }

                                            .corner {
                                                width: 20px;
                                                height: 20px;
                                            }
                                        }

                                        /* Notificaciones mejoradas */
                                        .qr-notification {
                                            position: fixed;
                                            top: 80px;
                                            right: 20px;
                                            z-index: 10000;
                                            min-width: 320px;
                                            max-width: 400px;
                                            box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
                                            border: none;
                                            border-radius: 12px;
                                            animation: slideInRight 0.3s ease-out;
                                        }

                                        @keyframes slideInRight {
                                            from {
                                                transform: translateX(100%);
                                                opacity: 0;
                                            }

                                            to {
                                                transform: translateX(0);
                                                opacity: 1;
                                            }
                                        }

                                        @media (max-width: 576px) {
                                            .qr-notification {
                                                top: 70px;
                                                right: 10px;
                                                left: 10px;
                                                min-width: auto;
                                                max-width: none;
                                            }
                                        }
                                    </style>

                                    <style>
                                        /* Toast con barra de progreso */
                                        .toast-with-progress {
                                            overflow: hidden;
                                        }

                                        .toast-progress {
                                            height: 3px;
                                            width: 100%;
                                            opacity: .9;
                                            animation-name: toastProgress;
                                            animation-timing-function: linear;
                                            animation-fill-mode: forwards;
                                        }

                                        @keyframes toastProgress {
                                            from {
                                                transform: scaleX(1);
                                            }

                                            to {
                                                transform: scaleX(0);
                                            }
                                        }
                                    </style>

                                    <div id="dropzoneArea"
                                        class="image-viewer-big d-flex align-items-center justify-content-center border-dashed rounded"
                                        onclick="if(!currentFile) document.getElementById('fileInput').click()">
                                        <input type="file" id="fileInput" class="d-none" accept="image/*"
                                            onchange="previewImage(event)">

                                        <div id="emptyState" class="text-center p-5">
                                            <i class="mdi mdi-file-image-outline text-primary mb-2"
                                                style="font-size: 64px;"></i>
                                            <h4 class="fw-bold">Visualizar Comprobante</h4>
                                            <p class="text-muted">Arrastra el archivo aquí o haz clic para
                                                subirlo.</p>
                                            <button type="button"
                                                class="btn btn-outline-primary btn-sm rounded-pill">Seleccionar
                                                imagen</button>
                                        </div>

                                        <div id="imagePreviewContainer"
                                            class="d-none w-100 h-100 overflow-hidden d-flex align-items-center justify-content-center"
                                            style="background: #f1f3fa;">
                                            <img id="previewImg" src="#" alt="Comprobante"
                                                style="transition: transform 0.2s ease; max-width: 90%; max-height: 90%;">
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <div class="d-flex gap-2 mt-3">
                                <div id="sunatSpinner" class="d-none align-items-center">
                                    <div class="spinner-border spinner-border-sm text-primary me-2" role="status"></div>
                                    <small class="text-muted">Consultando SUNAT...</small>
                                </div>
                            </div>

                            <div id="sunatResult" class="mt-3 d-none">
                                <div class="card border-0 shadow-sm">
                                    <div class="card-body">
                                        <div class="d-flex align-items-start justify-content-between">
                                            <div>
                                                <h6 class="mb-1 fw-bold">
                                                    <i class="mdi mdi-file-check-outline text-primary me-1"></i> Estado en SUNAT
                                                </h6>
                                                <small class="text-muted" id="sunatMsg"></small>
                                            </div>
                                            <span class="badge" id="badgeEstadoCp"></span>
                                        </div>

                                        <hr class="my-3">

                                        <div class="row g-3">
                                            <div class="col-md-4">
                                                <div class="p-2 rounded bg-light">
                                                    <small class="text-muted d-block">Estado RUC</small>
                                                    <span class="fw-bold" id="sunatEstadoRuc"></span>
                                                </div>
                                            </div>
                                            <div class="col-md-4">
                                                <div class="p-2 rounded bg-light">
                                                    <small class="text-muted d-block">Condición Domicilio</small>
                                                    <span class="fw-bold" id="sunatCondDomi"></span>
                                                </div>
                                            </div>
                                            <div class="col-md-4">
                                                <div class="p-2 rounded bg-light">
                                                    <small class="text-muted d-block">Código</small>
                                                    <span class="fw-bold" id="sunatCodigo"></span>
                                                </div>
                                            </div>
                                        </div>

                                        <div class="mt-3">
                                            <small class="text-muted d-block mb-1">Observaciones</small>
                                            <ul class="mb-0" id="sunatObs"></ul>
                                        </div>


                                    </div>
                                </div>
                            </div>

                            <button type="button" class="btn btn-light  px-4 me-2 m-2"
                                data-bs-dismiss="modal">Cancelar</button>
                            <button type="button" id="btnGuardarCambios" class="btn btn-primary px-4 me-2 m-2">
                                Guardar cambios
                            </button>


                        </div>
                    </div> <!-- end col -->
                </div>
                <!-- end row -->

            </div>



        </div>
        <!-- content -->

        <!-- Footer Start -->
        <footer class="footer">
            <div class="container-fluid">
                <div class="row">
                    <div class="col-md-6">
                        <script>
                            document.write(new Date().getFullYear())
                        </script> © Rensar Consulting
                    </div>
                    <div class="col-md-6">
                        <div class="text-md-end footer-links d-none d-md-block">
                            <a href="javascript: void(0);">Sobre Nosotros</a>
                            <a href="javascript: void(0);">Soporte</a>
                            <a href="javascript: void(0);">Contactenos</a>
                        </div>
                    </div>
                </div>
            </div>
        </footer>
        <!-- end Footer -->

    </div>

    <div id="toastContainer" class="toast-container position-fixed top-0 end-0 p-3" style="z-index: 1080;"></div>

    <!-- ============================================================== -->
    <!-- End Page content -->
    <!-- ============================================================== -->

</div>
<!-- END wrapper -->

<script>
    // Reemplaza la función previewImage existente con esta versión mejorada
    let scale = 1;
    let rotation = 0;
    let currentFile = false;

    // =============================
    // ESTADO VALIDACION SUNAT
    // =============================
    let sunatValidation = {
        validated: false, // ya validó y es vigente para los datos actuales
        lastPayloadHash: null, // para saber si cambiaron datos
        lastResult: null // guarda data devuelta por SUNAT si quieres
    };

    function stablePayloadHash(payload) {
        // hash simple para detectar cambios (stringify estable)
        return JSON.stringify(payload);
    }

    function invalidateSunatValidation() {
        sunatValidation.validated = false;
        sunatValidation.lastPayloadHash = null;
        sunatValidation.lastResult = null;
    }

    async function previewImage(event) {
        const file = event.target.files[0];
        if (file) {
            // Mostrar la imagen primero
            const reader = new FileReader();
            reader.onload = function(e) {
                const output = document.getElementById('previewImg');
                output.src = e.target.result;

                document.getElementById('emptyState').classList.add('d-none');
                document.getElementById('imagePreviewContainer').classList.remove('d-none');
                document.getElementById('viewerControls').classList.remove('d-none');
                document.getElementById('dropzoneArea').style.cursor = 'default';

                currentFile = true;
                resetImg();
            }
            reader.readAsDataURL(file);

            // Leer el QR automáticamente
            await readQRFromImage(file);
        }
    }

    // Nueva función para leer el QR y llenar el formulario
    async function readQRFromImage(file) {
        // Mostrar loading (opcional)
        showLoading();

        const formData = new FormData();
        formData.append('qr_image', file);

        try {
            const response = await fetch('/qr/read-upload', {
                method: 'POST',
                headers: {
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
                },
                body: formData
            });

            const result = await response.json();

            if (result.success && result.data.formato_valido) {
                // Llenar los campos del formulario con los datos del QR
                fillFormWithQRData(result.data);
                showNotification('success', 'Datos del QR cargados correctamente');

                await validateSunatIfNeeded(true); // forzamos validación por QR
            } else {
                showNotification('warning', 'No se detectó un QR válido en la imagen');
            }
        } catch (error) {
            console.error('Error al leer QR:', error);
            showNotification('error', 'Error al procesar el código QR');
        } finally {
            hideLoading();
        }
    }

    // Llenar el formulario con los datos del QR
    function fillFormWithQRData(data) {
        // RUC Emisor
        const rucInput = document.querySelector('input[placeholder="Ej: 20123456789"]');
        if (rucInput) rucInput.value = data.ruc_emisor || '';

        // Tipo Comprobante
        const tipoSelect = document.querySelector('select.form-select');
        if (tipoSelect) {
            const options = tipoSelect.options;
            for (let i = 0; i < options.length; i++) {
                if (options[i].text === data.tipo_documento_nombre) {
                    tipoSelect.selectedIndex = i;
                    break;
                }
            }
        }

        // Serie
        const serieInput = document.querySelector('input[placeholder="Ej: F001"]');
        if (serieInput) serieInput.value = data.serie || '';

        // Número
        const numeroInput = document.querySelector('input[placeholder="Ej: 00012345"]');
        if (numeroInput) numeroInput.value = data.numero || '';

        // IGV
        const igvInput = document.querySelector('input[placeholder="Ej: 180.00"]');
        if (igvInput) igvInput.value = data.igv || '';

        // Total
        const totalInput = document.querySelector('input[placeholder="Ej: 1180.00"]');
        if (totalInput) totalInput.value = data.total || '';

        // Fecha de Emisión
        const fechaInput = document.querySelector('input[placeholder="DD/MM/YYYY"]');
        if (fechaInput) fechaInput.value = formatDate(data.fecha_emision) || '';

        // Documento Receptor
        const docReceptorInput = document.querySelector('input[placeholder="DNI / RUC del receptor"]');
        if (docReceptorInput) {
            const docText = data.num_doc_receptor ?
                `${data.num_doc_receptor} (${data.tipo_doc_receptor_nombre})` : '';
            docReceptorInput.value = docText;
        }

        // Hash SUNAT
        const hashInput = document.querySelector('input[placeholder="Hash generado por SUNAT"]');
        if (hashInput) hashInput.value = data.hash_sunat || '';

        // Resaltar los campos llenados brevemente
        highlightFilledFields();
    }

    // Formatear fecha de YYYY-MM-DD a DD/MM/YYYY
    function formatDate(dateString) {
        if (!dateString) return '';

        // Si viene en formato YYYY-MM-DD
        if (dateString.includes('-')) {
            const parts = dateString.split('-');
            if (parts.length === 3) {
                return `${parts[2]}/${parts[1]}/${parts[0]}`;
            }
        }

        return dateString;
    }

    // Resaltar campos llenados
    function highlightFilledFields() {
        const fields = document.querySelectorAll('.col-lg-5 input, .col-lg-5 select');
        fields.forEach(field => {
            if (field.value && field.value.trim() !== '') {
                field.classList.add('border-success');
                setTimeout(() => {
                    field.classList.remove('border-success');
                }, 2000);
            }
        });
    }

    // Mostrar loading
    function showLoading() {
        const loadingEl = document.createElement('div');
        loadingEl.id = 'qr-loading';
        loadingEl.className = 'position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center';
        loadingEl.style.cssText = 'background: rgba(0,0,0,0.5); z-index: 9998;';
        loadingEl.innerHTML = `
        <div class="bg-white p-4 rounded shadow text-center">
            <div class="spinner-border text-primary mb-3" role="status">
                <span class="visually-hidden">Cargando...</span>
            </div>
            <p class="mb-0 fw-bold">Leyendo código QR...</p>
        </div>
    `;
        document.body.appendChild(loadingEl);
    }

    // Ocultar loading
    function hideLoading() {
        const loadingEl = document.getElementById('qr-loading');
        if (loadingEl) {
            loadingEl.remove();
        }
    }

    // Funciones existentes del visor de imagen
    function updateTransform() {
        const img = document.getElementById('previewImg');
        img.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
    }

    function zoomImg(factor) {
        scale *= factor;
        if (scale < 0.5) scale = 0.5;
        if (scale > 4) scale = 4;
        updateTransform();
    }

    function rotateImg() {
        rotation += 90;
        updateTransform();
    }

    function resetImg() {
        scale = 1;
        rotation = 0;
        updateTransform();
    }

    function removeImg() {
        currentFile = false;
        document.getElementById('fileInput').value = "";
        document.getElementById('emptyState').classList.remove('d-none');
        document.getElementById('imagePreviewContainer').classList.add('d-none');
        document.getElementById('viewerControls').classList.add('d-none');
        document.getElementById('dropzoneArea').style.cursor = 'pointer';
    }

    function downloadImg() {
        const img = document.getElementById('previewImg');
        const link = document.createElement('a');
        link.download = 'comprobante-expensas.png';
        link.href = img.src;
        link.click();
    }

    // Drag and Drop para subir imágenes
    const dropzoneArea = document.getElementById('dropzoneArea');

    dropzoneArea.addEventListener('dragover', (e) => {
        e.preventDefault();
        dropzoneArea.classList.add('border-primary', 'bg-light');
    });

    dropzoneArea.addEventListener('dragleave', (e) => {
        e.preventDefault();
        dropzoneArea.classList.remove('border-primary', 'bg-light');
    });

    dropzoneArea.addEventListener('drop', (e) => {
        e.preventDefault();
        dropzoneArea.classList.remove('border-primary', 'bg-light');

        const file = e.dataTransfer.files[0];
        if (file && file.type.startsWith('image/')) {
            // Simular que se seleccionó el archivo
            const dataTransfer = new DataTransfer();
            dataTransfer.items.add(file);
            document.getElementById('fileInput').files = dataTransfer.files;

            // Disparar el evento change
            const event = new Event('change', {
                bubbles: true
            });
            document.getElementById('fileInput').dispatchEvent(event);
        }
    });

    // ========================================
    // FUNCIONALIDAD DE CÁMARA PARA QR
    // ========================================

    let cameraStream = null;
    let cameraModal = null;

    // Inicializar modal
    document.addEventListener('DOMContentLoaded', function() {
        cameraModal = new bootstrap.Modal(document.getElementById('cameraModal'));

        // Event listeners
        document.getElementById('btnOpenCamera').addEventListener('click', openCamera);
        document.getElementById('btnCloseCamera').addEventListener('click', closeCamera);
        document.getElementById('btnCaptureQR').addEventListener('click', captureAndReadQR);

        // Cerrar cámara al cerrar el modal
        document.getElementById('cameraModal').addEventListener('hidden.bs.modal', function() {
            stopCameraStream();
        });
    });

    // Abrir cámara
    async function openCamera() {
        try {
            cameraModal.show();

            // Esperar a que el modal esté completamente visible
            await new Promise(resolve => setTimeout(resolve, 300));

            await startCameraStream();

        } catch (error) {
            console.error('Error al abrir cámara:', error);
            showNotification('error', 'No se pudo acceder a la cámara: ' + error.message);
            cameraModal.hide();
        }
    }

    // Iniciar stream de cámara
    async function startCameraStream() {
        const video = document.getElementById('cameraVideo');
        const initial = document.getElementById('cameraInitial');
        const guide = document.getElementById('scanGuide');

        try {
            // Configuración para móvil (cámara trasera) y desktop
            const constraints = {
                video: {
                    facingMode: {
                        ideal: 'environment'
                    }, // Cámara trasera en móviles
                    width: {
                        ideal: 1920
                    },
                    height: {
                        ideal: 1080
                    }
                }
            };

            cameraStream = await navigator.mediaDevices.getUserMedia(constraints);
            video.srcObject = cameraStream;

            // Esperar a que el video esté listo
            await new Promise((resolve) => {
                video.onloadedmetadata = () => {
                    video.play();
                    resolve();
                };
            });

            // Ocultar estado inicial y mostrar guía
            initial.style.display = 'none';
            video.style.display = 'block';
            guide.style.display = 'flex';
            guide.classList.add('scanning');

        } catch (error) {
            console.error('Error al acceder a la cámara:', error);
            throw new Error('No se pudo acceder a la cámara. Verifica los permisos en la configuración del navegador.');
        }
    }

    // Detener stream de cámara
    function stopCameraStream() {
        const video = document.getElementById('cameraVideo');
        const initial = document.getElementById('cameraInitial');
        const guide = document.getElementById('scanGuide');
        const success = document.getElementById('scanSuccess');
        const error = document.getElementById('scanError');

        if (cameraStream) {
            cameraStream.getTracks().forEach(track => track.stop());
            cameraStream = null;
        }

        video.srcObject = null;
        video.style.display = 'none';
        guide.style.display = 'none';
        guide.classList.remove('scanning');
        success.classList.add('d-none');
        error.classList.add('d-none');
        initial.style.display = 'flex';
    }

    // Cerrar cámara
    function closeCamera() {
        stopCameraStream();
        cameraModal.hide();
    }

    // Capturar y leer QR
    async function captureAndReadQR() {
        const video = document.getElementById('cameraVideo');
        const canvas = document.getElementById('cameraCanvas');
        const success = document.getElementById('scanSuccess');
        const guide = document.getElementById('scanGuide');

        try {
            // Configurar canvas con las dimensiones del video
            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;

            // Capturar frame actual
            const context = canvas.getContext('2d');
            context.drawImage(video, 0, 0, canvas.width, canvas.height);

            // Convertir a blob
            const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));

            // Mostrar estado de éxito
            guide.style.display = 'none';
            guide.classList.remove('scanning');
            success.classList.remove('d-none');

            // Enviar al backend para leer QR
            await sendCaptureToBackend(blob);

        } catch (error) {
            console.error('Error al capturar QR:', error);
            showNotification('error', 'Error al procesar la captura. Intenta nuevamente.');

            // Restaurar vista
            success.classList.add('d-none');
            guide.style.display = 'flex';
            guide.classList.add('scanning');
        }
    }

    // Enviar captura al backend
    async function sendCaptureToBackend(blob) {
        const formData = new FormData();
        formData.append('qr_image', blob, 'camera-capture.png');

        try {
            const response = await fetch('/qr/read-upload', {
                method: 'POST',
                headers: {
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
                },
                body: formData
            });

            const result = await response.json();

            if (result.success && result.data.formato_valido) {
                // Llenar formulario
                fillFormWithQRData(result.data);

                await validateSunatIfNeeded(true);

                // Mostrar la imagen capturada en el visor
                displayCapturedImage(blob);

                // Cerrar cámara y mostrar éxito
                setTimeout(() => {
                    closeCamera();
                    showNotification('success', 'Datos del QR cargados correctamente en el formulario');
                }, 1500);

            } else {
                // Mostrar error en el modal
                document.getElementById('scanSuccess').classList.add('d-none');
                document.getElementById('scanError').classList.remove('d-none');

                // Auto-ocultar error después de 3 segundos
                setTimeout(() => {
                    retryQRScan();
                }, 3000);
            }

        } catch (error) {
            console.error('Error al enviar captura:', error);
            showNotification('error', 'Error al procesar el código QR. Intenta nuevamente.');

            // Mostrar error en modal
            document.getElementById('scanSuccess').classList.add('d-none');
            document.getElementById('scanError').classList.remove('d-none');

            setTimeout(() => {
                retryQRScan();
            }, 3000);
        }
    }

    // Reintentar escaneo
    function retryQRScan() {
        document.getElementById('scanError').classList.add('d-none');
        document.getElementById('scanGuide').style.display = 'flex';
        document.getElementById('scanGuide').classList.add('scanning');
    }

    // Mostrar imagen capturada en el visor principal
    function displayCapturedImage(blob) {
        const reader = new FileReader();
        reader.onload = function(e) {
            const output = document.getElementById('previewImg');
            output.src = e.target.result;

            document.getElementById('emptyState').classList.add('d-none');
            document.getElementById('imagePreviewContainer').classList.remove('d-none');
            document.getElementById('viewerControls').classList.remove('d-none');
            document.getElementById('dropzoneArea').style.cursor = 'default';

            currentFile = true;
            resetImg();
        };
        reader.readAsDataURL(blob);
    }


    // ========================================
    // BOOTSTRAP TOAST (estilo Hyper + colores + PROGRESS)
    // ========================================
    function showNotification(type, message, title = null, opts = {}) {
        const t = (type || 'info').toLowerCase();

        const map = {
            success: {
                title: 'Éxito',
                headerBg: 'bg-success',
                icon: 'mdi mdi-check-circle-outline',
                barBg: 'bg-success'
            },
            warning: {
                title: 'Advertencia',
                headerBg: 'bg-warning',
                icon: 'mdi mdi-alert-outline',
                barBg: 'bg-warning'
            },
            info: {
                title: 'Información',
                headerBg: 'bg-info',
                icon: 'mdi mdi-information-outline',
                barBg: 'bg-info'
            },
            error: {
                title: 'Error',
                headerBg: 'bg-danger',
                icon: 'mdi mdi-close-circle-outline',
                barBg: 'bg-danger'
            },
            danger: {
                title: 'Error',
                headerBg: 'bg-danger',
                icon: 'mdi mdi-close-circle-outline',
                barBg: 'bg-danger'
            }
        };

        const cfg = map[t] || map.info;
        const finalTitle = title || cfg.title;

        const delay = Number(opts.delay ?? 3500); // ms
        const position = opts.position || 'top-0 end-0'; // bootstrap utilities (top-right)
        const containerId = 'toastContainer';

        let container = document.getElementById(containerId);

        // Si no existe, lo creamos automáticamente (por si te olvidas el HTML)
        if (!container) {
            container = document.createElement('div');
            container.id = containerId;
            container.className = `toast-container position-fixed ${position} p-3`;
            container.style.zIndex = 1080;
            document.body.appendChild(container);
        } else {
            // asegurar posición si lo pasas por opts
            container.className = `toast-container position-fixed ${position} p-3`;
            container.style.zIndex = 1080;
        }

        const id = `toast_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
        const timeText = 'ahora';

        const toastEl = document.createElement('div');
        toastEl.className = 'toast toast-with-progress align-items-stretch border-0 shadow-sm';
        toastEl.id = id;
        toastEl.setAttribute('role', 'alert');
        toastEl.setAttribute('aria-live', 'assertive');
        toastEl.setAttribute('aria-atomic', 'true');

        toastEl.innerHTML = `
        <div class="toast-header ${cfg.headerBg} text-white">
            <i class="${cfg.icon} me-2"></i>
            <strong class="me-auto">${finalTitle}</strong>
            <small class="text-white-50">${timeText}</small>
            <button type="button" class="btn-close btn-close-white ms-2" data-bs-dismiss="toast" aria-label="Close"></button>
        </div>
        <div class="toast-body bg-white">
            ${escapeHtml(message)}
        </div>
        <div class="toast-progress ${cfg.barBg}" style="transform-origin: left; animation-duration: ${delay}ms;"></div>
    `;

        container.appendChild(toastEl);

        const toast = new bootstrap.Toast(toastEl, {
            autohide: true,
            delay
        });

        toastEl.addEventListener('hidden.bs.toast', () => toastEl.remove());

        // Si el usuario cierra manualmente, que pare la animación para que no “salte”
        toastEl.addEventListener('hide.bs.toast', () => {
            const bar = toastEl.querySelector('.toast-progress');
            if (bar) bar.style.animationPlayState = 'paused';
        });

        toast.show();
    }

    // Evitar inyección HTML si el mensaje viene del backend
    function escapeHtml(str) {
        return String(str)
            .replaceAll('&', '&amp;')
            .replaceAll('<', '&lt;')
            .replaceAll('>', '&gt;')
            .replaceAll('"', '&quot;')
            .replaceAll("'", '&#039;');
    }



    function clearNotifications() {
        const c = document.getElementById('toastContainer');
        if (c) c.innerHTML = '';
    }


    // Detectar si es dispositivo móvil
    function isMobileDevice() {
        return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
    }

    // Actualizar texto del botón según dispositivo
    if (isMobileDevice()) {
        const btnText = document.querySelector('#btnOpenCamera');
        if (btnText) {
            btnText.innerHTML = '<i class="mdi mdi-camera me-1"></i> Usar Cámara';
        }
    }

    document.addEventListener('DOMContentLoaded', () => {
        const btn = document.getElementById('btnValidarSunat');
        if (!btn) return;

        btn.addEventListener('click', validarSunat);
    });

    document.addEventListener('DOMContentLoaded', () => {
        const ids = ['ruc_emisor', 'cod_comp', 'serie', 'numero', 'fecha_emision', 'total'];
        ids.forEach(id => {
            const el = document.getElementById(id);
            if (!el) return;
            el.addEventListener('input', () => invalidateSunatValidation());
            el.addEventListener('change', () => invalidateSunatValidation());
        });

        const btnGuardar = document.getElementById('btnGuardarCambios');
        if (btnGuardar) btnGuardar.addEventListener('click', onGuardarCambios);
    });

    function getFormPayloadForSunat() {
        const rucEmisor = document.getElementById('ruc_emisor')?.value?.trim();
        const codComp = document.getElementById('cod_comp')?.value?.trim();
        const serie = document.getElementById('serie')?.value?.trim().toUpperCase();
        const numeroRaw = document.getElementById('numero')?.value?.trim();
        const fecha = document.getElementById('fecha_emision')?.value?.trim();
        const totalRaw = document.getElementById('total')?.value?.trim();

        // numero debe ser integer (SUNAT lo pide como Integer)
        const numero = numeroRaw ? parseInt(numeroRaw, 10) : null;

        return {
            numRuc: rucEmisor,
            codComp,
            numeroSerie: serie,
            numero,
            fechaEmision: fecha,
            monto: totalRaw ? Number(totalRaw) : null
        };
    }

    async function validateSunatIfNeeded(force = false) {
        const spinner = document.getElementById('sunatSpinner');
        const resultBox = document.getElementById('sunatResult');

        const payload = getFormPayloadForSunat();

        // Validación rápida en frontend
        const required = ['numRuc', 'codComp', 'numeroSerie', 'numero', 'fechaEmision', 'monto'];
        for (const k of required) {
            if (payload[k] === null || payload[k] === '' || Number.isNaN(payload[k])) {
                showNotification('warning', `Completa el campo: ${k}`);
                return false;
            }
        }

        const currentHash = stablePayloadHash(payload);

        // Si ya está validado y los datos no cambiaron, NO revalidar
        if (!force && sunatValidation.validated && sunatValidation.lastPayloadHash === currentHash) {
            return true;
        }

        spinner?.classList.remove('d-none');
        spinner?.classList.add('d-flex');
        resultBox?.classList.add('d-none');

        try {
            const res = await fetch('/sunat/validar-comprobante', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
                },
                body: JSON.stringify(payload)
            });

            const json = await res.json();

            if (!res.ok || !json.success) {
                showNotification('error', json.message || 'Error validando en SUNAT');
                return false;
            }

            // Render UI
            renderSunatResult(json.data, json.message);

            // Guardar estado validado
            sunatValidation.validated = true;
            sunatValidation.lastPayloadHash = currentHash;
            sunatValidation.lastResult = json.data;

            showNotification('success', 'Validación SUNAT completada');
            return true;

        } catch (e) {
            console.error(e);
            showNotification('error', 'No se pudo conectar con el servidor');
            return false;

        } finally {
            spinner?.classList.add('d-none');
            spinner?.classList.remove('d-flex');
        }
    }


    function renderSunatResult(data, message) {
        // data = { estadoCp, estadoRuc, condDomiRuc, observaciones, errorCode? }
        const estadoCp = String(data.estadoCp ?? '');
        const estadoRuc = String(data.estadoRuc ?? '');
        const cond = String(data.condDomiRuc ?? '');
        const obs = Array.isArray(data.observaciones) ? data.observaciones : [];

        document.getElementById('sunatMsg').textContent = message || 'OK';
        document.getElementById('sunatEstadoRuc').textContent = `${estadoRuc} - ${mapEstadoRuc(estadoRuc)}`;
        document.getElementById('sunatCondDomi').textContent = `${cond} - ${mapCondDomi(cond)}`;
        document.getElementById('sunatCodigo').textContent = data.errorCode ? `ErrorCode: ${data.errorCode}` : '—';

        const badge = document.getElementById('badgeEstadoCp');
        const {
            text,
            cls
        } = mapEstadoCpBadge(estadoCp);
        badge.textContent = `${estadoCp} - ${text}`;
        badge.className = `badge ${cls}`;

        const ul = document.getElementById('sunatObs');
        ul.innerHTML = '';
        if (obs.length === 0) {
            ul.innerHTML = '<li class="text-muted">Sin observaciones</li>';
        } else {
            obs.forEach(o => {
                const li = document.createElement('li');
                li.textContent = o;
                ul.appendChild(li);
            });
        }

        document.getElementById('sunatResult').classList.remove('d-none');
    }

    function mapEstadoCpBadge(code) {
        // Según manual: 0 NO EXISTE, 1 ACEPTADO, 2 ANULADO, 3 AUTORIZADO, 4 NO AUTORIZADO :contentReference[oaicite:1]{index=1}
        switch (code) {
            case '1':
                return {
                    text: 'ACEPTADO', cls: 'bg-success'
                };
            case '2':
                return {
                    text: 'ANULADO', cls: 'bg-danger'
                };
            case '3':
                return {
                    text: 'AUTORIZADO', cls: 'bg-info'
                };
            case '4':
                return {
                    text: 'NO AUTORIZADO', cls: 'bg-warning text-dark'
                };
            case '0':
                return {
                    text: 'NO EXISTE', cls: 'bg-secondary'
                };
            default:
                return {
                    text: 'DESCONOCIDO', cls: 'bg-secondary'
                };
        }
    }

    function mapEstadoRuc(code) {
        // Según manual :contentReference[oaicite:2]{index=2}
        const m = {
            '00': 'ACTIVO',
            '01': 'BAJA PROVISIONAL',
            '02': 'BAJA PROV. POR OFICIO',
            '03': 'SUSPENSIÓN TEMPORAL',
            '10': 'BAJA DEFINITIVA',
            '11': 'BAJA DE OFICIO',
            '22': 'INHABILITADO-VENT. ÚNICA'
        };
        return m[code] || '—';
    }

    function mapCondDomi(code) {
        // Según manual :contentReference[oaicite:3]{index=3}
        const m = {
            '00': 'HABIDO',
            '09': 'PENDIENTE',
            '11': 'POR VERIFICAR',
            '12': 'NO HABIDO',
            '20': 'NO HALLADO'
        };
        return m[code] || '—';
    }

    async function onGuardarCambios() {
        // Si no está validado (o cambiaron datos), valida primero
        const ok = await validateSunatIfNeeded(false);
        if (!ok) return;

        //  Ya validado, acá recién guardas (NO revalidar)
        // TODO: aquí llamas tu endpoint /gastos/guardar o submit form.
        showNotification('success', 'Listo: validado y guardado');
    }
</script>
@endsection


