<?php
// CARGA MAESTRA DE CONFIGURACIÓN Y BRANDING
require_once(__DIR__ . '/core/init.php');

// === UNIFICACIÓN DE ENDPOINTS API ===
const API_CLIENTS_READ_CONTACT_URL = 'db/clients-read-contact-data.php';
const API_LEADS_READ_CONTACT_URL = 'db/leads-read-contact-data.php';
const API_SERVICES_READ_ALL_URL = 'db/services-read-all.php';
const API_QUOTE_READ_URL = 'db/quote-read.php';
const API_QUOTE_CREATE_URL = 'db/quote-create.php';
const API_QUOTE_UPDATE_URL = 'db/quote-update.php';
const API_QUOTE_DELETE_URL = 'db/quote-delete.php';
const API_QUOTE_CONVERT_TO_INVOICE_URL = 'db/convert-quote-to-invoice.php';
const API_QUOTE_UPDATE_STATUS_URL = 'db/quote-update.php';

$db_error_message = null;
$company_logo_url = 'img/LogoPlaceholder.webp';
$company_name_from_db = 'Nombre Del Negocio';
$contact_name_from_db = 'Nombre De Contacto';
$contact_email_from_db = 'correo@ejemplo.com';
$contact_phone_from_db = 'N/A';

// Fase IV: Sanitización del CSRF Token antes de ser inyectado en el HTML
$csrf_token = htmlspecialchars($_SESSION['csrf_token'] ?? '', ENT_QUOTES, 'UTF-8');

try {
    // MITIGACIÓN SQLI: Uso de Sentencia Preparada (Best Practice Enforcement)
    $stmt_config = $pdo->prepare("SELECT * FROM business_info WHERE id = :id");
    $stmt_config->execute(['id' => 1]);
    $config = $stmt_config->fetch(PDO::FETCH_ASSOC);

    if ($config) {
        // Fase IV: Sanitización estricta de salida para contexto HTML
        $company_name_from_db = htmlspecialchars($config['company_name'] ?? 'Nombre Del Negocio', ENT_QUOTES, 'UTF-8');
        $contact_name_from_db = htmlspecialchars($config['full_name'] ?? 'Nombre De Contacto', ENT_QUOTES, 'UTF-8');
        $contact_email_from_db = htmlspecialchars($config['site_email'] ?? 'correo@ejemplo.com', ENT_QUOTES, 'UTF-8');
        $contact_phone_from_db = htmlspecialchars($config['phone_primary'] ?? 'N/A', ENT_QUOTES, 'UTF-8');
        
        if (!empty($config['logo_url'])) {
            $company_logo_url = htmlspecialchars($config['logo_url'], ENT_QUOTES, 'UTF-8');
        }

    } else {
        $db_error_message = 'No Se Encontró La Configuración';
    }
    
} catch (PDOException $e) {
    error_log("Error PDO al cargar configuración desde business_info: " . $e->getMessage());
    $db_error_message = 'Error de Base de Datos: No se pudo cargar la configuración.';
} catch (Exception $e) {
    error_log("Error general al cargar configuración desde business_info: " . $e->getMessage());
    $db_error_message = 'Error inesperado.';
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Panel De Control De Cotizaciones <?php echo htmlspecialchars($branding['full_title'] ?? ''); ?></title>
    <meta name="robots" content="noindex, nofollow">
    
    <link rel="icon" type="image/png" href="<?php echo htmlspecialchars($branding['favicon'] ?? ''); ?>">
    <link rel="apple-touch-icon" href="<?php echo htmlspecialchars($branding['favicon'] ?? ''); ?>">
    
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="<?php echo htmlspecialchars($google_font_url ?? ''); ?>">

    <?php if(file_exists('files/gtm-head.php')) include 'files/gtm-head.php'; ?>
    
    <script src="https://unpkg.com/lucide@latest"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
    
    <script src="https://unpkg.com/imask"></script>

    <link rel="stylesheet" href="style.css">
    <script src="files/header-manager.js"></script>
    
</head>
<body data-page-title="Panel De Control De Cotizaciones"
      data-page-subtitle="Genera Y Administra Oportunidades De Negocios"
      data-page-icon="file-text">

    <input type="hidden" id="csrf-token-php" value="<?php echo $csrf_token; ?>">
    
    <div id="toast-container" class="toast-container"></div>
    
    <?php if(file_exists('files/gtm-body.php')) include 'files/gtm-body.php'; ?>  

<div class="relative min-h-screen md:flex bg-[var(--color-background)] font-['Barlow']">
    <div id="sidebar-overlay" class="fixed inset-0 bg-black bg-opacity-50 z-30 hidden md:hidden"></div>
    <div id="task-panel-overlay" class="fixed inset-0 bg-black/60 z-40 hidden transition-opacity duration-300"></div>  
    
    <?php if(file_exists('menu.php')) include 'menu.php'; ?>
    
    <main class="flex-1 overflow-y-auto">
        <header class="bg-white shadow-sm p-4 flex justify-between items-center sticky top-0 z-20">
            <button id="mobile-menu-button" class="md:hidden text-gray-600 hover:text-gray-800">
                <i data-lucide="menu" class="w-6 h-6"></i>
            </button>
            <div class="page-header-container">
                <h2 id="page-title"></h2>
                <p id="page-subtitle"></p>
            </div>
        </header>

        <div id="content-area" class="p-4 md:p-8 space-y-8">
            
            <section id="dashboard-stats-section">
                <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 stat-cards-grid-main">
                    
                    <div class="stat-card bg-white p-3 lg:p-6 rounded-xl shadow-md flex items-center border-l-4 border-[var(--color-highlight)]">
                        <i data-lucide="user-plus" class="w-8 h-8 lg:w-12 lg:h-12 text-[var(--color-secondary)] shrink-0"></i>
                        <div class="flex-1 flex justify-between items-center ml-3 lg:ml-4">
                            <h3 class="text-md lg:text-lg font-black text-gray-500 leading-tight uppercase">ESTIMADOS A PROSPECTOS</h3>
                            <p id="stats-prospects-count" class="text-3xl lg:text-5xl font-black text-[var(--color-primary)]">0</p>
                        </div>
                    </div>

                    <div class="stat-card bg-white p-3 lg:p-6 rounded-xl shadow-md flex items-center border-l-4 border-[var(--color-highlight)]">
                        <i data-lucide="users" class="w-8 h-8 lg:w-12 lg:h-12 text-[var(--color-secondary)] shrink-0"></i>
                        <div class="flex-1 flex justify-between items-center ml-3 lg:ml-4">
                            <h3 class="text-md lg:text-lg font-black text-gray-500 leading-tight uppercase">ESTIMADOS A CLIENTES</h3>
                            <p id="stats-clients-count" class="text-3xl lg:text-5xl font-black text-[var(--color-primary)]">0</p>
                        </div>
                    </div>

                    <div class="stat-card bg-white p-3 lg:p-6 rounded-xl shadow-md flex items-center border-l-4 border-[var(--color-highlight)]">
                        <i data-lucide="wallet" class="w-8 h-8 lg:w-12 lg:h-12 text-[var(--color-secondary)] shrink-0"></i>
                        <div class="flex-1 flex justify-between items-center ml-3 lg:ml-4">
                            <h3 class="text-md lg:text-lg font-black text-gray-500 leading-tight uppercase">VALOR TOTAL</h3>
                            <p id="stats-month-total" class="text-3xl lg:text-5xl font-black text-[var(--color-primary)]">$0</p>
                        </div>
                    </div>

                </div>
            </section>

            <section id="list-view-section">
                <div class="bg-white p-6 rounded-xl shadow-md">
                    <div class="flex flex-col md:flex-row justify-between items-center mb-6 gap-4">
                        <h3 class="text-2xl font-black text-[var(--color-primary)] flex items-center gap-2 uppercase">
                            <i data-lucide="file-check" class="w-7 h-7 text-[var(--color-secondary)]"></i> TODOS LOS ESTIMADOS
                        </h3>
                        <button id="show-generator-btn" class="w-full bg-[var(--color-secondary)] hover:opacity-90 text-white font-black py-2.5 px-4 rounded-lg uppercase sm:w-auto flex items-center justify-center gap-2 touch-target-btn transition-transform hover:-translate-y-1">
                            <i data-lucide="plus" class="w-5 h-5 mr-2"></i> NUEVO ESTIMADO
                        </button>
                    </div>

                    <div class="flex flex-col md:flex-row gap-4 mb-6">
                        <div class="input-with-icon w-full relative">
                            <i data-lucide="search" class="search-icon w-5 h-5 absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
                            <input type="text" id="list-search" placeholder="BUSCAR POR NOMBRE..." class="w-full pl-10 p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-highlight)]">
                        </div>
                        <select id="month-filter" class="w-full md:w-auto p-3 border border-gray-300 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-[var(--color-highlight)] uppercase text-sm font-bold text-gray-600"></select>
                        <select id="list-filter-contact-type" class="w-full md:w-auto p-3 border border-gray-300 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-[var(--color-highlight)] uppercase text-sm font-bold text-gray-600"></select>
                        <select id="list-filter-status" class="w-full md:w-auto p-3 border border-gray-300 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-[var(--color-highlight)] uppercase text-sm font-bold text-gray-600"></select>
                    </div>

                    <div class="overflow-x-auto pt-4">
                        <table class="min-w-full bg-white responsive-table-stack">
                            <thead class="bg-gray-50 hidden md:table-header-group">
                                <tr class="text-left text-gray-500 uppercase text-sm font-semibold">
                                    <th class="py-3 px-6">FECHA</th>
                                    <th class="py-3 px-6">CLIENTE/PROSPECTO</th>
                                    <th class="py-3 px-6">SERVICIO</th>
                                    <th class="py-3 px-6">MONTO</th>
                                    <th class="py-3 px-6">ESTADO</th>
                                    <th class="py-3 px-6 text-center">ACCIONES</th>
                                </tr>
                            </thead>
                            <tbody id="estimadosTableBody" class="text-gray-700 text-sm"></tbody>
                        </table>
                        <p id="no-estimados-msg" class="text-center text-gray-500 py-8 hidden uppercase font-bold">AÚN NO HAS GENERADO NINGÚN ESTIMADO</p>
                    </div>
                </div>
            </section>
        </div>
    </main>
</div>

<section id="generator-view-section" class="hidden">
    <div id="generator-overlay" class="fixed inset-0 bg-black bg-opacity-50 z-40"></div>
    <div id="generator-panel" class="fixed inset-y-0 right-0 w-full max-w-full lg:max-w-6xl bg-[var(--color-background)] shadow-2xl z-50 transform translate-x-full transition-transform duration-300 ease-in-out flex flex-col lg:flex-row">
        
        <div class="w-full lg:w-2/5 bg-white border-r border-gray-200 flex flex-col h-full">
            
            <div class="flex-shrink-0 flex justify-between items-center p-4 border-b border-gray-200 bg-[var(--color-primary)] text-white shadow z-20">
                <h1 id="generator-title" class="text-xl lg:text-2xl font-black text-[var(--color-highlight)] uppercase flex items-center gap-2">
                    <i data-lucide="file-plus-2" class="w-6 h-6 text-white"></i> CREAR ESTIMADO
                </h1>
            </div>

            <div class="flex-grow overflow-y-auto p-4 sm:p-6 space-y-6 generator-controls-panel">
                <input type="hidden" id="editing-quote-id">
                
                <div class="space-y-4">
                    <h2 class="text-lg font-black text-[var(--color-primary)] flex items-center gap-2 uppercase border-b pb-2 border-gray-200">
                        <i data-lucide="user-check" class="w-5 h-5 text-[var(--color-secondary)]"></i> INFORMACIÓN DE CONTACTO
                    </h2>
                    
                    <div id="contact-selection-area" class="space-y-4">
                        <div class="space-y-2">
                            <label for="clientSearch" class="text-sm font-bold uppercase text-gray-600">BUSCAR CLIENTE</label>
                            <div class="relative input-with-icon">
                                <i data-lucide="search" class="search-icon w-5 h-5 absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
                                <input type="text" id="clientSearch" placeholder="Escribe nombre del cliente..." autocomplete="off" class="w-full pl-10 p-2.5 border border-gray-300 rounded-lg focus:border-[var(--color-highlight)] focus:ring-[var(--color-highlight)] focus:ring-1 transition text-sm">
                                <div id="client-suggestions" class="absolute w-full bg-white border rounded-md mt-1 shadow-lg z-20 hidden max-h-40 overflow-y-auto"></div>
                            </div>
                        </div>
                        
                        <div class="flex items-center">
                            <hr class="flex-grow border-t border-gray-200">
                            <span class="px-2 text-gray-400 text-xs font-bold uppercase">O SELECCIONA</span>
                            <hr class="flex-grow border-t border-gray-200">
                        </div>
                        
                        <div class="space-y-2">
                            <label for="prospectSearch" class="text-sm font-bold uppercase text-gray-600">BUSCAR PROSPECTO</label>
                            <div class="relative input-with-icon">
                                <i data-lucide="search" class="search-icon w-5 h-5 absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
                                <input type="text" id="prospectSearch" placeholder="Escribe nombre del prospecto..." autocomplete="off" class="w-full pl-10 p-2.5 border border-gray-300 rounded-lg focus:border-[var(--color-highlight)] focus:ring-[var(--color-highlight)] focus:ring-1 transition text-sm">
                                <div id="prospect-suggestions" class="absolute w-full bg-white border rounded-md mt-1 shadow-lg z-20 hidden max-h-40 overflow-y-auto"></div>
                            </div>
                        </div>
                    </div>

                    <div id="selected-contact-display" class="hidden items-center justify-between p-4 bg-blue-50 border border-blue-100 rounded-lg shadow-sm">
                        <div class="flex items-center gap-3">
                            <div class="bg-blue-100 p-2 rounded-full">
                                <i data-lucide="user-check" class="w-6 h-6 text-[var(--color-primary)]"></i>
                            </div>
                            <div>
                                <p class="text-lg font-black text-[var(--color-primary)] uppercase leading-none" id="selected-contact-name"></p>
                                <p class="text-xs text-gray-500 font-bold mt-1" id="selected-contact-email"></p>
                            </div>
                        </div>
                        <button id="change-contact-btn" class="text-xs text-[var(--color-secondary)] font-black uppercase hover:underline">CAMBIAR</button>
                    </div>
                </div>

                <div class="space-y-4">
                    <h2 class="text-lg font-black text-[var(--color-primary)] flex items-center gap-2 uppercase border-b pb-2 border-gray-200">
                        <i data-lucide="clipboard-list" class="w-5 h-5 text-[var(--color-secondary)]"></i> DETALLES DEL SERVICIO
                    </h2>
                    <select id="tipoServicio" class="w-full p-2.5 border border-gray-300 rounded-lg focus:border-[var(--color-highlight)] focus:ring-[var(--color-highlight)] focus:ring-1 transition text-sm bg-white"></select>
                </div>

                <div class="space-y-4">
                    <h2 class="text-lg font-black text-[var(--color-primary)] flex items-center gap-2 uppercase border-b pb-2 border-gray-200">
                        <i data-lucide="percent" class="w-5 h-5 text-[var(--color-secondary)]"></i> IMPUESTOS (TAXES)
                    </h2>
                    <input type="number" id="tax-rate" value="0" min="0" step="0.1" class="w-full p-2.5 border border-gray-300 rounded-lg focus:border-[var(--color-highlight)] focus:ring-[var(--color-highlight)] focus:ring-1 transition text-sm">
                </div>

                <div id="digital-link-container" class="hidden space-y-2 bg-[var(--color-background)] p-4 rounded-lg border border-gray-200 mt-4">
                    <label class="text-lg font-black text-[var(--color-primary)] uppercase flex items-center gap-2">
                        <i data-lucide="link" class="w-4 h-4 text-[var(--color-secondary)]"></i> ENLACE DIGITAL
                    </label>
                    <div class="flex gap-2">
                        <input type="text" id="sidebar-quote-link" readonly class="w-full text-xs p-2 border border-gray-300 rounded text-gray-600 bg-white focus:outline-none select-all font-mono">
                        <button type="button" id="sidebar-copy-btn" class="bg-gray-200 text-gray-700 p-2 rounded hover:bg-gray-300 transition-colors" title="Copiar">
                            <i data-lucide="copy" class="w-4 h-4"></i>
                        </button>
                        <a id="sidebar-open-btn" href="#" target="_blank" class="bg-[var(--color-secondary)] text-white p-2 rounded hover:opacity-90 transition-colors flex items-center" title="Abrir">
                            <i data-lucide="external-link" class="w-4 h-4"></i>
                        </a>
                    </div>
                    <p class="text-xs text-gray-500 font-bold uppercase mt-1">
                        * COMPARTE ESTE ENLACE CON TU CLIENTE.
                    </p>
                </div>
            </div>

            <div class="p-4 bg-gray-50 border-t border-gray-200 flex flex-col space-y-3"> 
                <button id="save-quote-btn" class="w-full bg-[var(--color-secondary)] hover:opacity-90 text-white font-black py-2.5 px-4 rounded-lg uppercase flex items-center justify-center gap-2 touch-target-btn transition-transform hover:-translate-y-1 shadow-md">
                    <i data-lucide="save" class="w-5 h-5"></i> GUARDAR CAMBIOS
                </button>
                <div class="flex gap-3">
                    <button id="download-pdf-btn" class="w-full bg-[var(--color-primary)] hover:opacity-90 text-white font-black py-2.5 px-4 rounded-lg uppercase flex items-center justify-center gap-2 touch-target-btn transition-transform hover:-translate-y-1 shadow-md">
                        <i data-lucide="download" class="w-5 h-5"></i> DESCARGAR PDF
                    </button>
                    <button id="back-to-list-btn" class="w-full bg-[var(--color-primary)] hover:opacity-90 text-white font-black py-2.5 px-4 rounded-lg uppercase flex items-center justify-center gap-2 touch-target-btn transition-transform hover:-translate-y-1 shadow-md">
                        <i data-lucide="x-circle" class="w-5 h-5"></i> CERRAR
                    </button>
                </div>
            </div>
        </div>

        <div class="w-full lg:w-3/5 bg-gray-100 p-4 lg:p-8 overflow-y-auto h-full generator-preview-panel flex justify-center">
            <div id="pdf-preview" class="w-full max-w-2xl bg-white shadow-xl min-h-[800px] rounded-sm">
                <div id="quote-content" class="p-8"></div>
            </div>
        </div>
    </div>
</section>

<div id="status-modal-overlay" class="status-modal-overlay fixed inset-0 bg-gray-900 bg-opacity-90 flex items-center justify-center z-50">
    
    <div id="status-modal" class="bg-white rounded-xl shadow-2xl w-full max-w-sm m-4 transform transition-all duration-300 scale-95 text-center">
        
        <div class="modal-header-container bg-[var(--color-primary)] p-6 rounded-t-xl">
            <h3 class="modal-primary-title text-3xl font-black text-[var(--color-highlight)] uppercase leading-none flex items-center justify-center gap-2">
                <i data-lucide="refresh-cw" class="w-8 h-8 text-white"></i> ACTUALIZAR
            </h3>
        </div>

        <div class="p-8">
            <input type="hidden" id="modal-estimate-id">

            <p class="text-[var(--color-primary)] mb-6 uppercase text-lg font-bold leading-tight">
                SELECCIONA EL NUEVO ESTADO
            </p>

            <div class="mb-6">
                <select id="modal-status-select" class="w-full p-3 border-2 border-gray-200 rounded-lg text-center font-bold text-gray-700 uppercase focus:outline-none focus:border-[var(--color-highlight)] bg-white transition-colors cursor-pointer"></select>
            </div>

            <div class="flex flex-col sm:flex-row justify-center space-y-2 sm:space-y-0 sm:space-x-4">
                <button type="button" id="modal-cancel-btn" class="w-full bg-[var(--color-primary)] hover:opacity-90 text-white font-black py-2.5 px-4 rounded-lg uppercase sm:w-auto flex items-center justify-center gap-2 transition-transform hover:-translate-y-1">
                    <i data-lucide="x-circle" class="w-5 h-5"></i> CANCELAR
                </button>
                
                <button type="button" id="modal-save-status-btn" class="w-full bg-[var(--color-secondary)] hover:opacity-90 text-white font-black py-2.5 px-4 rounded-lg uppercase sm:w-auto flex items-center justify-center gap-2 transition-transform hover:-translate-y-1">
                    <i data-lucide="save" class="w-5 h-5"></i> GUARDAR
                </button>
            </div>

            <p class="mt-6 uppercase text-xs font-black text-gray-500 tracking-wider"> 
                SE ACTUALIZARÁ INMEDIATAMENTE
            </p>
        </div>
    </div>
</div>

<div id="confirmConvertInvoiceModal" 
     class="fixed inset-0 bg-gray-900 bg-opacity-90 flex items-center justify-center hidden z-50"
     onclick="if(event.target === this) window.closeModal('confirmConvertInvoiceModal')">
    
    <div class="bg-white rounded-xl shadow-2xl w-full max-w-sm m-4 transform transition-all duration-300 scale-95 opacity-0 text-center">
        
        <div class="modal-header-container bg-[var(--color-primary)] p-6 rounded-t-xl">
            <h3 class="modal-primary-title text-3xl font-black text-[var(--color-highlight)] uppercase leading-none flex items-center justify-center gap-2">
                <i data-lucide="arrow-right-circle" class="w-8 h-8 text-white"></i> CONVERTIR
            </h3>
        </div>

        <div class="p-8">
            <p class="text-[var(--color-primary)] mb-6 uppercase text-lg font-bold leading-tight">
                ¿CONVERTIR ESTIMADO <span id="quote-to-convert-id" class="font-black text-[var(--color-secondary)] inline">#ID</span> A FACTURA?
            </p>

            <div class="flex flex-col sm:flex-row justify-center space-y-2 sm:space-y-0 sm:space-x-4">
                <button type="button" 
                        id="cancel-convert-btn"
                        class="w-full bg-[var(--color-primary)] hover:opacity-90 text-white font-black py-2.5 px-4 rounded-lg uppercase sm:w-auto flex items-center justify-center gap-2 transition-transform hover:-translate-y-1" 
                        onclick="window.closeModal('confirmConvertInvoiceModal')">
                    <i data-lucide="x-circle" class="w-5 h-5"></i> CANCELAR
                </button>
                
                <button type="button" 
                        id="confirm-convert-invoice-button"
                        class="w-full bg-[var(--color-secondary)] hover:opacity-90 text-white font-black py-2.5 px-4 rounded-lg uppercase sm:w-auto flex items-center justify-center gap-2 transition-transform hover:-translate-y-1">
                    <i data-lucide="check-circle-2" class="w-5 h-5"></i> CONVERTIR
                </button>
            </div>

            <p class="mt-6 uppercase text-xs font-black text-gray-500 tracking-wider"> 
                LA COTIZACIÓN SE MARCARÁ COMO CONVERTIDA
            </p>
        </div>
    </div>
</div>

<div id="confirmDeleteModal" 
     class="fixed inset-0 bg-gray-900 bg-opacity-90 flex items-center justify-center hidden z-50"
     onclick="if(event.target === this) window.closeModal('confirmDeleteModal')">
    
    <div class="bg-white rounded-xl shadow-2xl w-full max-w-sm m-4 transform transition-all duration-300 scale-95 opacity-0 text-center">
        
        <div class="modal-header-container bg-[var(--color-primary)] p-6 rounded-t-xl">
            <h3 class="modal-primary-title text-3xl font-black text-[var(--color-highlight)] uppercase leading-none flex items-center justify-center gap-2">
                <i data-lucide="alert-triangle" class="w-8 h-8 text-white"></i> ADVERTENCIA
            </h3>
        </div>

        <div class="p-8">
            <p class="text-[var(--color-primary)] mb-6 uppercase text-lg font-bold leading-tight">
                ¿ELIMINAR ESTE <span id="confirm-item-type" class="font-black text-[var(--color-secondary)] inline">ELEMENTO</span>?
            </p>

            <div class="flex flex-col sm:flex-row justify-center space-y-2 sm:space-y-0 sm:space-x-4">
                <button type="button" 
                        id="cancel-delete-btn"
                        class="w-full bg-[var(--color-primary)] hover:opacity-90 text-white font-black py-2.5 px-4 rounded-lg uppercase sm:w-auto flex items-center justify-center gap-2 transition-transform hover:-translate-y-1" 
                        onclick="window.closeModal('confirmDeleteModal')">
                    <i data-lucide="x-circle" class="w-5 h-5"></i> CANCELAR
                </button>
                
                <button type="button" 
                        id="confirm-delete-button"
                        class="w-full bg-[var(--color-secondary)] hover:opacity-90 text-white font-black py-2.5 px-4 rounded-lg uppercase sm:w-auto flex items-center justify-center gap-2 transition-transform hover:-translate-y-1">
                    <i data-lucide="trash-2" class="w-5 h-5"></i> ELIMINAR
                </button>
            </div>

            <p class="mt-6 uppercase text-xs font-black text-gray-500 tracking-wider"> 
                ESTA ACCIÓN NO SE PUEDE DESHACER
            </p>
        </div>
    </div>
</div>

<div id="quoteSuccessModal" 
     class="fixed inset-0 bg-gray-900 bg-opacity-90 flex items-center justify-center hidden z-50"
     onclick="/* No auto close */">
    
    <div class="bg-white rounded-xl shadow-2xl w-full max-w-lg m-4 transform transition-all duration-300 scale-95 opacity-0 text-center">
        
        <div class="modal-header-container bg-[var(--color-primary)] p-6 rounded-t-xl">
            <h3 class="modal-primary-title text-3xl font-black text-[var(--color-highlight)] uppercase leading-none flex items-center justify-center gap-2">
                <i data-lucide="check-circle" class="w-8 h-8 text-white"></i> ¡ESTIMADO GUARDADO!
            </h3>
        </div>

        <div class="p-8">
            <p class="text-gray-500 mb-2 font-bold uppercase text-lg tracking-wide">
                ENLACE DIGITAL PARA ENVIAR
            </p>
            
            <div class="flex gap-2 mb-6">
                <input type="text" id="success-quote-link" readonly 
                       class="w-full p-3 border-2 border-gray-200 rounded-lg text-gray-700 bg-gray-50 focus:outline-none font-medium text-sm font-mono select-all">
                
                <button id="success-copy-btn" class="bg-[var(--color-primary)] hover:opacity-90 text-white p-3 rounded-lg transition-colors flex items-center justify-center shadow-md" title="Copiar">
                    <i data-lucide="copy" class="w-5 h-5"></i>
                </button>
                
                <a id="success-open-btn" href="#" target="_blank" class="bg-[var(--color-secondary)] hover:opacity-90 text-white p-3 rounded-lg transition-colors flex items-center justify-center shadow-md" title="Abrir">
                    <i data-lucide="external-link" class="w-5 h-5"></i>
                </a>
            </div>

            <div class="flex justify-center">
                <button type="button" 
                        id="close-success-modal-btn"
                        class="w-full bg-[var(--color-primary)] hover:opacity-90 text-white font-black py-3 px-6 rounded-lg uppercase flex items-center justify-center gap-2 transition-transform hover:-translate-y-1 shadow-md">
                    <i data-lucide="x" class="w-5 h-5"></i> CERRAR Y ACTUALIZAR
                </button>
            </div>
        </div>
    </div>
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
    const urlParams = new URLSearchParams(window.location.search);
    const notificationType = urlParams.get('notification_type');
    const notificationMessage = urlParams.get('notification_message');

    if (notificationType && notificationMessage) {
        if (typeof showToast === 'function') {
            const safeMessage = escapeHTML(notificationMessage);
            showToast(safeMessage, notificationType);
        } else {
            console.error("showToast function is not defined.");
        }
    }

    if (window.jspdf) { window.jsPDF = window.jspdf.jsPDF; }

    let allClients = [];
    let allProspects = [];
    let allServices = [];
    let allEstimates = {};
    let nextAvailableEstimateNumber = 1;
    let quoteToConvertExternalId = null; 
    
    // FIX: Verificar existencia del elemento CSRF antes de acceder a .value
    const csrfElement = document.getElementById('csrf-token-php');
    const CSRF_TOKEN = csrfElement ? csrfElement.value : '';
    
    if (!CSRF_TOKEN) {
        console.error("Critical Security Error: CSRF Token missing from DOM. Verify input#csrf-token-php exists.");
        if (typeof showToast === 'function') showToast("Error de seguridad: Token CSRF faltante. Recargue la página.", 'error');
    }

    const today = new Date();
    const currentMonthString = today.getFullYear() + '-' + String(today.getMonth() + 1).padStart(2, '0');

    const API_URLS = {
        CLIENTS_READ: '<?php echo API_CLIENTS_READ_CONTACT_URL; ?>',
        LEADS_READ: '<?php echo API_LEADS_READ_CONTACT_URL; ?>',
        SERVICES_READ: '<?php echo API_SERVICES_READ_ALL_URL; ?>',
        QUOTE_READ: '<?php echo API_QUOTE_READ_URL; ?>',
        QUOTE_CREATE: '<?php echo API_QUOTE_CREATE_URL; ?>',
        QUOTE_UPDATE: '<?php echo API_QUOTE_UPDATE_URL; ?>',
        QUOTE_DELETE: '<?php echo API_QUOTE_DELETE_URL; ?>',
        QUOTE_CONVERT: '<?php echo API_QUOTE_CONVERT_TO_INVOICE_URL; ?>',
        QUOTE_UPDATE_STATUS: '<?php echo API_QUOTE_UPDATE_STATUS_URL; ?>'
    };

    const escapeHTML = (str) => {
        if (!str) return '';
        const s = String(str);
        return s.replace(/[&<>'"]/g, 
            tag => ({'&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#39;', '"': '&quot;'}[tag]));
    };

    // SECURITY FIX: Inyección segura de variables PHP a JS usando json_encode
    const companyLogoUrl = <?php echo json_encode($company_logo_url); ?>;
    const companyName = <?php echo json_encode($company_name_from_db); ?>;
    const contactName = <?php echo json_encode($contact_name_from_db); ?>;
    const contactEmail = <?php echo json_encode($contact_email_from_db); ?>;
    const contactPhone = <?php echo json_encode($contact_phone_from_db); ?>;

    const quoteTemplateHTML = `<div class="flex justify-between items-start mb-10">
        <div>
            <img src="${escapeHTML(companyLogoUrl)}" alt="Logo" class="h-16 mb-4">
            <h2 class="text-2xl font-black text-gray-800">${escapeHTML(companyName)}</h2>
            <p class="text-gray-600">${escapeHTML(contactName)}</p>
            <p class="text-gray-600">${escapeHTML(contactEmail)}</p>
            <p class="text-gray-600">${escapeHTML(contactPhone)}</p>
        </div>
        <div class="text-right">
            <h1 class="text-5xl font-black text-gray-300 tracking-wider">ESTIMATE</h1>
            <p><strong>Date:</strong> <span id="previewDate"></span></p>
            <p><strong>Estimate No.:</strong> <span id="previewQuoteId"></span></p>
        </div>
    </div>
    <div class="bg-gray-50 p-4 rounded-lg mb-8">
        <h3 class="font-bold text-gray-500 text-sm tracking-wider">QUOTE TO:</h3>
        <p id="previewContactName" contenteditable="true"></p>
        <p id="previewContactAddress" contenteditable="true"></p>
        <p id="previewContactEmail" contenteditable="true"></p>
        <p id="previewContactPhone" contenteditable="true"></p>
    </div>
    <table class="w-full mb-8">
        <thead class="bg-gray-800 text-white">
            <tr>
                <th class="text-left p-3 rounded-tl-md">DESCRIPTION</th>
                <th class="text-right p-3">QTY.</th>
                <th class="text-right p-3">UNIT PRICE</th>
                <th class="text-right p-3 rounded-tr-md">TOTAL</th>
                <th class="p-3 rounded-tr-md"></th>
            </tr>
        </thead>
        <tbody id="quote-items"></tbody>
    </table>
    <button id="addItemBtn" class="bg-[var(--color-secondary)] text-white border w-full p-2.5 rounded-lg text-sm mb-6 flex items-center justify-center">
        <i data-lucide="plus-circle" class="w-5 h-5 mr-2"></i> AGREGAR ADICIONAL
    </button>
    <div class="flex justify-end mt-4">
        <div class="w-full md:w-1/2">
            <div class="flex justify-between p-2">
                <span>Subtotal</span>
                <span id="previewSubtotal"></span>
            </div>
            <div class="flex justify-between p-2 border-b">
                <span>Tax (<span id="previewTaxRate">0</span>%)</span>
                <span id="previewTaxAmount"></span>
            </div>
            <div class="flex justify-between font-bold text-xl bg-yellow-300 p-3 rounded-md">
                <span>TOTAL</span>
                <span id="previewTotal"></span>
            </div>
        </div>
    </div>
    <div class="mt-16 text-xs text-gray-500">
        <h4 class="font-bold mb-1">Terms and Conditions</h4>
        <p contenteditable="true">This estimate is valid for 30 days.</p>
    </div>`;

    const elements = {
        generatorView: document.getElementById('generator-view-section'),
        generatorPanel: document.getElementById('generator-panel'),
        generatorOverlay: document.getElementById('generator-overlay'),
        showGeneratorBtn: document.getElementById('show-generator-btn'),
        saveQuoteBtn: document.getElementById('save-quote-btn'),
        downloadPdfBtn: document.getElementById('download-pdf-btn'),
        backToListBtn: document.getElementById('back-to-list-btn'),
        estimadosTableBody: document.getElementById('estimadosTableBody'),
        noEstimadosMsg: document.getElementById('no-estimados-msg'),
        listSearch: document.getElementById('list-search'),
        monthFilter: document.getElementById('month-filter'),
        listFilterContactType: document.getElementById('list-filter-contact-type'),
        listFilterStatus: document.getElementById('list-filter-status'),
        generatorTitle: document.getElementById('generator-title'),
        editingQuoteId: document.getElementById('editing-quote-id'),
        clientSearch: document.getElementById('clientSearch'),
        clientSuggestions: document.getElementById('client-suggestions'),
        prospectSearch: document.getElementById('prospectSearch'),
        prospectSuggestions: document.getElementById('prospect-suggestions'),
        contactSelectionArea: document.getElementById('contact-selection-area'),
        selectedContactDisplay: document.getElementById('selected-contact-display'),
        selectedContactName: document.getElementById('selected-contact-name'),
        selectedContactEmail: document.getElementById('selected-contact-email'),
        changeContactBtn: document.getElementById('change-contact-btn'),
        tipoServicio: document.getElementById('tipoServicio'),
        taxRate: document.getElementById('tax-rate'),
        quoteContent: document.getElementById('quote-content'),
        statsProspectsCount: document.getElementById('stats-prospects-count'),
        statsClientsCount: document.getElementById('stats-clients-count'),
        statsMonthTotal: document.getElementById('stats-month-total'),
        statusModalOverlay: document.getElementById('status-modal-overlay'),
        statusModal: document.getElementById('status-modal'),
        modalEstimateId: document.getElementById('modal-estimate-id'),
        modalStatusSelect: document.getElementById('modal-status-select'),
        modalCancelBtn: document.getElementById('modal-cancel-btn'),
        modalSaveStatusBtn: document.getElementById('modal-save-status-btn'),
        confirmDeleteModal: document.getElementById('confirmDeleteModal'),
        confirmDeleteButton: document.getElementById('confirm-delete-button'),
        confirmItemTypeSpan: document.getElementById('confirm-item-type'),
        confirmConvertInvoiceModal: document.getElementById('confirmConvertInvoiceModal'),
        confirmConvertInvoiceButton: document.getElementById('confirm-convert-invoice-button'),
        quoteToConvertIdSpan: document.getElementById('quote-to-convert-id')
    };
    let dynamicElements = {};
    let selectedContact = null;

    const showGeneratorView = (externalId = null) => {
        elements.generatorView.classList.remove('hidden');
        setTimeout(() => { elements.generatorPanel.classList.remove('translate-x-full'); }, 10);
        elements.quoteContent.innerHTML = quoteTemplateHTML;
        
        bindDynamicElements();
        
        selectedContact = null;
        elements.editingQuoteId.value = '';
        elements.contactSelectionArea.classList.remove('hidden');
        elements.selectedContactDisplay.classList.add('hidden');
        elements.clientSearch.value = '';
        elements.prospectSearch.value = '';
        elements.tipoServicio.value = '';
        elements.taxRate.value = 0;
        
        // ELEMENTOS DEL LINK EN EL PANEL LATERAL
        const digitalLinkContainer = document.getElementById('digital-link-container');
        const sidebarQuoteLink = document.getElementById('sidebar-quote-link');
        const sidebarOpenBtn = document.getElementById('sidebar-open-btn');

        if(dynamicElements.quoteItemsBody) dynamicElements.quoteItemsBody.innerHTML = '';
        
        if (externalId && allEstimates[externalId]) {
            const estimate = allEstimates[externalId];
            elements.generatorTitle.textContent = `Editando Estimado #${estimate.id.replace('est-', '')}`;
            elements.editingQuoteId.value = externalId;
            if (estimate.contact) {
                handleContactSelection(estimate.contact);
            }
            elements.taxRate.value = estimate.taxRate || 0;
            dynamicElements.quoteItemsBody.innerHTML = '';
            
            estimate.items.forEach(([desc, price, quantity = 1]) => dynamicElements.quoteItemsBody.appendChild(createItemRow(desc, price, quantity)));
            
            if (estimate.items.length > 0) {
                const firstItemDesc = estimate.items[0][0];
                const matchingService = allServices.find(s => s.name === firstItemDesc);
                if (matchingService) {
                    elements.tipoServicio.value = matchingService.id;
                } else {
                    elements.tipoServicio.value = '';
                }
            }

            // MOSTRAR ENLACE EN EL PANEL (SOLO EN MODO EDICIÓN)
            if (digitalLinkContainer) {
                const baseUrl = window.location.href.substring(0, window.location.href.lastIndexOf('/'));
                const fullLink = `${baseUrl}/quote-view.php?token=${externalId}`;
                sidebarQuoteLink.value = fullLink;
                sidebarOpenBtn.href = fullLink;
                digitalLinkContainer.classList.remove('hidden');
            }

            recalculateTotal();
        } else {
            dynamicElements.quoteItemsBody.appendChild(createItemRow("Nuevo Ítem", 0.00, 1)); 
            elements.generatorTitle.textContent = 'Crear Nuevo Estimado';
            elements.editingQuoteId.value = '';
            
            // OCULTAR ENLACE EN EL PANEL (MODO CREACIÓN)
            if (digitalLinkContainer) {
                digitalLinkContainer.classList.add('hidden');
                sidebarQuoteLink.value = '';
            }
        }
        updatePreview();

        if (typeof lucide !== 'undefined') {
            lucide.createIcons({ container: elements.generatorPanel });
        }
    };
    
    const hideGeneratorView = () => {
        elements.generatorPanel.classList.add('translate-x-full');
        setTimeout(() => { elements.generatorView.classList.add('hidden'); }, 300);
    };

    const bindDynamicElements = () => {
        dynamicElements = {
            quoteItemsBody: document.getElementById('quote-items'),
            addItemBtn: document.getElementById('addItemBtn'),
            previewContactName: document.getElementById('previewContactName'),
            previewContactAddress: document.getElementById('previewContactAddress'),
            previewContactEmail: document.getElementById('previewContactEmail'),
            previewContactPhone: document.getElementById('previewContactPhone'),
            previewDate: document.getElementById('previewDate'),
            previewQuoteId: document.getElementById('previewQuoteId'),
            previewSubtotal: document.getElementById('previewSubtotal'),
            previewTaxRate: document.getElementById('previewTaxRate'),
            previewTaxAmount: document.getElementById('previewTaxAmount'),
            previewTotal: document.getElementById('previewTotal')
        };
        
        dynamicElements.addItemBtn.addEventListener('click', () => {
            dynamicElements.quoteItemsBody.appendChild(createItemRow());
            if (typeof lucide !== 'undefined') {
                lucide.createIcons();
            }
        });
        
        dynamicElements.quoteItemsBody.addEventListener('input', recalculateTotal);
        elements.taxRate.addEventListener('input', recalculateTotal);
        dynamicElements.quoteItemsBody.addEventListener('blur', (e) => {
            if (e.target.matches('.item-unit-price')) {
                const value = parseFloat(e.target.textContent.replace(/[^\d.]/g, '')) || 0;
                e.target.textContent = `$${value.toFixed(2)}`;
            } else if (e.target.matches('.item-qty')) {
                const value = parseInt(e.target.textContent) || 1;
                e.target.textContent = `${value}`;
            }
        }, true);
    };

    const mobileMenuButton = document.getElementById('mobile-menu-button');
    const sidebar = document.getElementById('sidebar');
    const sidebarOverlay = document.getElementById('sidebar-overlay');

    if (mobileMenuButton && sidebar && sidebarOverlay) {
        mobileMenuButton.addEventListener('click', () => {
            sidebar.classList.toggle('-translate-x-full');
            sidebarOverlay.classList.toggle('hidden');
        });

        sidebarOverlay.addEventListener('click', () => {
            sidebar.classList.add('-translate-x-full');
            sidebarOverlay.classList.add('hidden');
        });
    }

    const phoneInput = document.getElementById('contact_phone');
    if (phoneInput && typeof IMask !== 'undefined') {
        IMask(phoneInput, { mask: '(000) 000-0000' });
    }
    
    const formatStatusText = (status) => (status ? status.replace(/_/g, ' ').toUpperCase() : '');
    const getStatusClass = (status) => {
        const s = status ? status.toLowerCase() : '';
        if (s === 'aprobado') return 'bg-green-100 text-green-800';
        if (s === 'enviado') return 'bg-blue-100 text-blue-800';
        if (s === 'rechazado') return 'bg-[var(--color-secondary)] text-white';
        if (s === 'en_revision') return 'bg-yellow-100 text-yellow-800';
        if (s === 'generado') return 'bg-gray-300 text-gray-800';
        if (s === 'convertida') return 'bg-green-100 text-green-800';
        return 'bg-gray-100 text-gray-800';
    };

    const translateContactType = (type) => {
        if (type === 'client') return 'CLIENTE';
        if (type === 'prospect') return 'PROSPECTO';
        return type || '';
    };

    const updateDisplay = () => {
        const monthFilterValue = elements.monthFilter.value;
        renderTable(monthFilterValue);
        updateDashboardStats(monthFilterValue);
    };

    const renderTable = (monthFilterValue) => {
        const filters = {
            term: elements.listSearch.value.toLowerCase(),
            contactType: elements.listFilterContactType.value,
            status: elements.listFilterStatus.value
        };

        const filteredData = Object.values(allEstimates).filter(doc => {
            const matchesTerm = doc.cliente.toLowerCase().includes(filters.term);
            const matchesContactType = (filters.contactType === 'all' || doc.contactType === filters.contactType);
            const matchesStatus = (filters.status === 'all' || doc.estado === filters.status);
            let matchesMonth = true;
            if (monthFilterValue !== 'all') {
                const docDate = new Date(doc.fechaSolicitud + 'T00:00:00');
                const filterYear = parseInt(monthFilterValue.substring(0, 4));
                const filterMonth = parseInt(monthFilterValue.substring(5, 7)) - 1;
                matchesMonth = (docDate.getFullYear() === filterYear && docDate.getMonth() === filterMonth);
            }
            return matchesTerm && matchesContactType && matchesStatus && matchesMonth;
        });

        elements.estimadosTableBody.innerHTML = '';
        elements.noEstimadosMsg.classList.toggle('hidden', filteredData.length > 0);
        
        // Ordenar por fecha descendente
        filteredData.sort((a, b) => new Date(b.fechaSolicitud) - new Date(a.fechaSolicitud));

        filteredData.forEach(doc => {
            const row = elements.estimadosTableBody.insertRow();
            const contactTypeClass = doc.contactType === 'client' ? 'bg-blue-100 text-blue-800' : 'bg-purple-100 text-purple-800';
            row.className = 'border-b hover:bg-gray-50';

            // AQUÍ AGREGAMOS EL BOTÓN DE LINK (COLOR ÍNDIGO/MORADO)
            row.innerHTML = `
                <td class="py-4 px-6" data-label="FECHA">${escapeHTML(doc.fechaSolicitud)}</td>
                <td class="py-4 px-6" data-label="CLIENTE/PROSPECTO">
                    <div class="flex flex-col items-end">
                        <span class="font-semibold">${escapeHTML(doc.cliente)}</span>
                        <span class="text-xs font-bold p-1 rounded ${contactTypeClass}">
                            ${translateContactType(doc.contactType).toUpperCase()}
                        </span>
                    </div>
                </td>
                <td class="py-4 px-6" data-label="SERVICIO">${escapeHTML(doc.servicio)}</td>
                <td class="py-4 px-6" data-label="MONTO">$${parseFloat(doc.montoEstimado).toFixed(2)}</td>
                <td class="py-4 px-6" data-label="ESTADO"><span class="${getStatusClass(doc.estado)} py-1 px-3 rounded-full text-xs font-bold">${formatStatusText(doc.estado)}</span></td>
                <td class="py-4 px-6 text-center actions-cell" data-label="ACCIONES">
                    <div class="flex flex-row items-center justify-end space-x-3 w-full">
                        
                        <button class="text-indigo-600 hover:text-indigo-800 copy-row-link-btn w-auto" data-token="${doc.id}" title="Copiar Enlace Digital">
                            <i data-lucide="link" class="w-5 h-5"></i>
                        </button>

                        ${doc.estado !== 'convertida' ? `
                            <button class="text-gray-500 hover:text-gray-700 change-status-btn w-auto" data-estimate-id="${doc.id}" data-current-status="${doc.estado}" title="Cambiar Estado">
                                <i data-lucide="edit-3" class="w-5 h-5"></i>
                            </button>
                            <button class="text-blue-600 hover:text-blue-800 edit-quote-btn w-auto" data-estimate-id="${doc.id}" title="Ver/Editar">
                                <i data-lucide="eye" class="w-5 h-5"></i>
                            </button>
                            <button class="text-green-600 hover:text-green-800 convert-quote-btn w-auto" data-quote-external-id="${doc.id}" title="Convertir a Factura">
                                <i data-lucide="check-circle" class="w-5 h-5"></i>
                            </button>
                        ` : `
                            <span class="text-gray-400 w-auto" title="Cotización Convertida a Factura"><i data-lucide="check-check" class="w-5 h-5"></i> Convertida</span>
                        `}
                        <button class="text-[var(--color-secondary)] hover:text-[var(--color-secondary)] delete-estimate-btn w-auto" data-estimate-id="${doc.id}" title="Eliminar">
                            <i data-lucide="trash-2" class="w-5 h-5"></i>
                        </button>
                    </div>
                </td>`;
            elements.estimadosTableBody.appendChild(row);
        });
        
        if (typeof lucide !== 'undefined') {
            lucide.createIcons();
        }
        setupStatusChangeListeners();
        setupDeleteListeners();
        setupConvertQuoteListeners();
        setupEditQuoteListeners();
        setupQuickLinkListeners(); // <--- NUEVA FUNCIÓN
    };

    // --- NUEVA LÓGICA PARA EL BOTÓN DE COPIAR EN LA TABLA ---
    const setupQuickLinkListeners = () => {
        document.querySelectorAll('.copy-row-link-btn').forEach(button => {
            button.onclick = null;
            button.addEventListener('click', (event) => {
                event.stopPropagation();
                const token = button.dataset.token;
                
                // Construir URL
                const path = window.location.pathname;
                const directory = path.substring(0, path.lastIndexOf('/'));
                const fullLink = `${window.location.origin}${directory}/quote-view.php?token=${token}`;
                
                // Copiar al portapapeles
                navigator.clipboard.writeText(fullLink).then(() => {
                    if (typeof showToast === 'function') {
                        showToast('Enlace copiado al portapapeles', 'success');
                    } else {
                        alert('Enlace copiado');
                    }
                }).catch(err => {
                    console.error('Error al copiar:', err);
                });
            });
        });
    };

    const setupStatusChangeListeners = () => {
        document.querySelectorAll('.change-status-btn').forEach(button => {
            button.onclick = null;
            button.addEventListener('click', (event) => {
                const externalId = button.dataset.estimateId;
                const currentStatus = button.dataset.currentStatus;
                showStatusModal(externalId, currentStatus);
                event.stopPropagation();
            });
        });
    };

    const setupDeleteListeners = () => {
        document.querySelectorAll('.delete-estimate-btn').forEach(button => {
            button.onclick = null;
            button.addEventListener('click', (event) => {
                const externalId = button.dataset.estimateId;
                openConfirmDeleteModal(externalId, 'estimado');
                event.stopPropagation();
            });
        });
    };

    const setupEditQuoteListeners = () => {
        document.querySelectorAll('.edit-quote-btn').forEach(button => {
            button.onclick = null;
            button.addEventListener('click', (event) => {
                const externalId = button.dataset.estimateId;
                showGeneratorView(externalId);
                event.stopPropagation();
            });
        });
    };

    const setupConvertQuoteListeners = () => {
        document.querySelectorAll('.convert-quote-btn').forEach(button => {
            button.onclick = null;
            button.addEventListener('click', (event) => {
                const quoteExternalId = button.dataset.quoteExternalId;
                const quote = allEstimates[quoteExternalId];
                if (!quote || !quote.db_id) {
                    if (typeof showToast === 'function') showToast('Error: Datos de la cotización no encontrados.', 'error');
                    return;
                }
                
                quoteToConvertExternalId = quoteExternalId;
                elements.quoteToConvertIdSpan.textContent = `#${String(quote.db_id).padStart(3, '0')}`;
                
                openModal('confirmConvertInvoiceModal');
                event.stopPropagation();
            });
        });
    };
    
    const executeConvertQuote = async () => {
        const quoteExternalId = quoteToConvertExternalId;
        const quote = allEstimates[quoteExternalId];
        
        if (!quote || !quote.db_id) {
            if (typeof showToast === 'function') showToast('Error: Datos de la cotización no encontrados.', 'error');
            return;
        }
        
        // Fase V: Envío de CSRF Token en la solicitud
        const payload = { 
            quote_id: quote.db_id,
            csrf_token: CSRF_TOKEN
        };
        
        if (typeof showToast === 'function') showToast('Iniciando conversión a factura...', 'info');
        try {
            const response = await fetch(API_URLS.QUOTE_CONVERT, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(payload)
            });
            const result = await response.json();
            closeModal('confirmConvertInvoiceModal');
            
            if (result.success) {
                if (typeof showToast === 'function') showToast(result.message || 'Cotización convertida con éxito.', 'success');
                allEstimates[quoteExternalId].estado = 'convertida';
                setTimeout(() => location.reload(), 1000);
            } else {
                if (typeof showToast === 'function') showToast(result.message || 'Error al convertir la cotización.', 'error');
            }
        } catch (error) {
            console.error('Error de conexión:', error);
            if (typeof showToast === 'function') showToast('Error de conexión al convertir.', 'error');
        }
    };

    const showStatusModal = (externalId, currentStatus) => {
        elements.modalEstimateId.value = externalId;
        const statuses = ['generado', 'enviado', 'aprobado', 'rechazado', 'convertida'];
        elements.modalStatusSelect.innerHTML = '';
        statuses.forEach(status => {
            const option = document.createElement('option');
            option.value = status;
            option.textContent = formatStatusText(status);
            if (status === currentStatus) {
                option.selected = true;
            }
            elements.modalStatusSelect.appendChild(option);
        });
        elements.statusModalOverlay.classList.add('active');
        if (typeof lucide !== 'undefined') lucide.createIcons();
    };

    const hideStatusModal = () => {
        elements.statusModalOverlay.classList.remove('active');
    };

    const openModal = function(modalId) {
        const modal = document.getElementById(modalId);
        if (!modal) { return; }
        const modalBox = modal.querySelector('div:first-of-type');
        modal.classList.remove('hidden');
        setTimeout(() => { modalBox.classList.remove('scale-95', 'opacity-0'); }, 50);
        if (typeof lucide !== 'undefined') lucide.createIcons();
    };

    const closeModal = function(modalId) {
        const modal = document.getElementById(modalId);
        if (!modal) { return; }
        const modalBox = modal.querySelector('div:first-of-type');
        modalBox.classList.add('scale-95', 'opacity-0');
        setTimeout(() => { modal.classList.add('hidden'); }, 300);
    };
    
    window.closeModal = closeModal; 

    const openConfirmDeleteModal = function(itemId, itemType) {
        if (!elements.confirmDeleteButton || !elements.confirmItemTypeSpan) { return; }
        elements.confirmDeleteButton.dataset.itemId = itemId;
        elements.confirmDeleteButton.dataset.itemType = itemType;
        elements.confirmItemTypeSpan.textContent = itemType.toUpperCase();
        openModal('confirmDeleteModal');
    };

    const executeDeleteQuote = async (externalId) => {
        const estimateToDelete = allEstimates[externalId];
        if (!estimateToDelete || !estimateToDelete.db_id) {
            if (typeof showToast === 'function') showToast('Error: ID de base de datos no encontrado.', 'error');
            return;
        }
        
        // Fase V: Envío de CSRF Token en la solicitud
        const payload = { 
            id: estimateToDelete.db_id, 
            csrf_token: CSRF_TOKEN 
        };
        
        try {
            const response = await fetch(API_URLS.QUOTE_DELETE, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(payload)
            });
            const result = await response.json();
            if (result.success) {
                if (typeof showToast === 'function') showToast('Estimado eliminado con éxito.', 'success');
                closeModal('confirmDeleteModal');
                setTimeout(() => location.reload(), 1000);
            } else {
                if (typeof showToast === 'function') showToast(`Error al eliminar: ${result.message}`, 'error');
            }
        }
        catch (error) {
            console.error('Error al eliminar:', error);
            if (typeof showToast === 'function') showToast('Error de conexión al eliminar.', 'error');
        }
    };

    const updateEstimateStatus = async (externalId, newStatus) => {
        const estimate = allEstimates[externalId];
        if (!estimate || !estimate.db_id) {
            if (typeof showToast === 'function') showToast('Error: Datos del estimado no encontrados.', 'error');
            return;
        }
        
        // Fase V: Envío de CSRF Token en la solicitud
        const payload = { 
            id: estimate.db_id, 
            status: newStatus, 
            csrf_token: CSRF_TOKEN 
        };
        
        try {
            const response = await fetch(API_URLS.QUOTE_UPDATE_STATUS, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(payload)
            });
            const result = await response.json();
            if (result.success) {
                hideStatusModal();
                if (typeof showToast === 'function') showToast('Estado actualizado con éxito.', 'success');
                setTimeout(() => location.reload(), 1000);
            } else {
                if (typeof showToast === 'function') showToast(`Error al actualizar estado: ${result.message}`, 'error');
            }
        } catch (error) {
            console.error('Error de conexión:', error);
            if (typeof showToast === 'function') showToast('Error de conexión al actualizar estado.', 'error');
        }
    };

    const formatToK = (num) => {
        if (num === 0) return '0';
        return Math.abs(num) > 999 ? (Math.sign(num)*((Math.abs(num)/1000).toFixed(1))) + 'k' : Math.sign(num)*Math.abs(num);
    };

    const updateDashboardStats = (monthFilterValue) => {
        let prospectsCount = 0, clientsCount = 0, monthTotal = 0;
        const dataToProcess = Object.values(allEstimates).filter(doc => {
            let matchesMonth = true;
            if (monthFilterValue !== 'all') {
                const docDate = new Date(doc.fechaSolicitud + 'T00:00:00');
                const filterYear = parseInt(monthFilterValue.substring(0, 4));
                const filterMonth = parseInt(monthFilterValue.substring(5, 7)) - 1;
                matchesMonth = (docDate.getFullYear() === filterYear && docDate.getMonth() === filterMonth);
            }
            return matchesMonth;
        });
        dataToProcess.forEach(estimate => {
            if (estimate.contactType === 'prospect') prospectsCount++;
            if (estimate.contactType === 'client') clientsCount++;
            monthTotal += parseFloat(estimate.montoEstimado) || 0;
        });
        elements.statsProspectsCount.textContent = prospectsCount;
        elements.statsClientsCount.textContent = clientsCount;
        elements.statsMonthTotal.textContent = `$${formatToK(monthTotal)}`;
    };

    const generatePDF = () => {
        if (typeof showToast === 'function') showToast('Generando PDF...', 'info');
        const quoteElement = document.getElementById('pdf-preview');
        if (!quoteElement) {
            console.error('Element #pdf-preview not found.');
            return;
        }
        
        const addItemBtn = quoteElement.querySelector('#addItemBtn');
        const originalDisplayAddItem = addItemBtn ? addItemBtn.style.display : '';
        const removeBtns = quoteElement.querySelectorAll('.remove-item-btn');
        const originalDisplayRemoveBtns = Array.from(removeBtns).map(btn => btn.style.display);
        const quoteItemsBody = quoteElement.querySelector('#quote-items');
        const originalMarginBottom = quoteItemsBody ? quoteItemsBody.style.marginBottom : '';

        if (addItemBtn) addItemBtn.style.display = 'none';
        removeBtns.forEach(btn => btn.style.display = 'none');
        if (quoteItemsBody) {
            quoteItemsBody.style.marginBottom = '20px';
        }

        quoteElement.style.width = '816px';
        quoteElement.style.margin = '0 auto';
        quoteElement.style.padding = '32px';
        quoteElement.style.boxSizing = 'border-box';

        html2canvas(quoteElement, { 
            scale: 2,
            useCORS: true,
            width: 816,
        }).then(canvas => {
            quoteElement.style.width = '';
            quoteElement.style.margin = '';
            quoteElement.style.padding = '';
            quoteElement.style.boxSizing = '';

            if (addItemBtn) addItemBtn.style.display = originalDisplayAddItem;
            removeBtns.forEach((btn, i) => btn.style.display = originalDisplayRemoveBtns[i]);
            if (quoteItemsBody) {
                quoteItemsBody.style.marginBottom = originalMarginBottom;
            }

            const imgData = canvas.toDataURL('image/png');
            const pdf = new window.jsPDF({ orientation: 'portrait', unit: 'pt', format: 'letter' });
            const pdfWidth = pdf.internal.pageSize.getWidth();
            const pdfHeight = pdf.internal.pageSize.getHeight();
            
            const imgHeight = (canvas.height * pdfWidth) / canvas.width;
            let heightLeft = imgHeight;
            let position = 0;

            pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, imgHeight);
            heightLeft -= pdfHeight;

            while (heightLeft >= 0) {
                position = heightLeft - imgHeight;
                pdf.addPage();
                pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, imgHeight);
                heightLeft -= pdfHeight;
            }
            pdf.save(`Estimado-${dynamicElements.previewQuoteId.textContent}-${dynamicElements.previewContactName.textContent.replace(/\s+/g, '_')}.pdf`);
            if (typeof showToast === 'function') showToast('PDF generado.', 'success');
        }).catch(err => {
            quoteElement.style.width = '';
            quoteElement.style.margin = '';
            quoteElement.style.padding = '';
            quoteElement.style.boxSizing = '';

            if (addItemBtn) addItemBtn.style.display = originalDisplayAddItem;
            removeBtns.forEach((btn, i) => btn.style.display = originalDisplayRemoveBtns[i]);
            if (quoteItemsBody) {
                quoteItemsBody.style.marginBottom = originalMarginBottom;
            }
            console.error("Error al generar PDF:", err);
            if (typeof showToast === 'function') showToast('Error al generar el PDF.', 'error');
        });
    };

    const createItemRow = (desc = "Nuevo Ítem", unitPrice = 0.00, qty = 1) => {
        const row = document.createElement('tr');
        const safeDesc = escapeHTML(desc);
        const safeQty = escapeHTML(qty.toString());
        const safeUnitPrice = `$${parseFloat(unitPrice).toFixed(2)}`;
        const safeItemTotal = `$${(unitPrice * qty).toFixed(2)}`;

        row.innerHTML = `<td class="p-3 border-b" contenteditable="true">${safeDesc}</td><td class="text-right p-3 item-qty border-b" contenteditable="true">${safeQty}</td><td class="text-right p-3 item-unit-price border-b" contenteditable="true">${safeUnitPrice}</td><td class="text-right p-3 item-total-price border-b">${safeItemTotal}</td><td class="p-3 border-b text-center"><button type="button" class="text-[var(--color-secondary)] hover:text-[var(--color-secondary)] remove-item-btn font-bold text-lg" title="Remove Item"><i data-lucide="x" class="w-5 h-5"></i></button></td>`;
        const removeButton = row.querySelector('.remove-item-btn');
        if (removeButton) {
            removeButton.addEventListener('click', () => {
                row.remove();
                recalculateTotal();
            });
        }
        
        return row;
    };

    const recalculateTotal = () => {
        if (!dynamicElements.quoteItemsBody) return;
        let subtotal = 0;
        dynamicElements.quoteItemsBody.querySelectorAll('tr').forEach(row => {
            const qty = parseInt(row.cells[1].textContent) || 0;
            const unitPrice = parseFloat(row.cells[2].textContent.replace(/[$,]/g, '')) || 0;
            const itemTotal = qty * unitPrice;
            row.cells[3].textContent = `$${itemTotal.toFixed(2)}`;
            subtotal += itemTotal;
        });
        const taxRate = parseFloat(elements.taxRate.value) || 0;
        const taxAmount = subtotal * (taxRate / 100);
        const total = subtotal + taxAmount;
        dynamicElements.previewSubtotal.textContent = `$${subtotal.toFixed(2)}`;
        dynamicElements.previewTaxRate.textContent = taxRate.toFixed(2);
        dynamicElements.previewTaxAmount.textContent = `$${taxAmount.toFixed(2)}`;
        dynamicElements.previewTotal.textContent = `$${total.toFixed(2)}`;
    };

    const resetGeneratorForm = () => {
        selectedContact = null;
        elements.editingQuoteId.value = '';
        elements.contactSelectionArea.classList.remove('hidden');
        elements.selectedContactDisplay.classList.add('hidden');
        elements.clientSearch.value = '';
        elements.prospectSearch.value = '';
        elements.tipoServicio.value = '';
        elements.taxRate.value = 0;
        if(dynamicElements.quoteItemsBody) dynamicElements.quoteItemsBody.innerHTML = '';
        
        if(dynamicElements.quoteItemsBody) {
             dynamicElements.quoteItemsBody.appendChild(createItemRow("Nuevo Ítem", 0.00, 1));
             if (typeof lucide !== 'undefined') {
                 lucide.createIcons({ container: dynamicElements.quoteItemsBody });
             }
        }
        
        // OCULTAR ENLACE AL CREAR NUEVO
        const digitalLinkContainer = document.getElementById('digital-link-container');
        if (digitalLinkContainer) digitalLinkContainer.classList.add('hidden');

        updatePreview();
    };

    const handleContactSelection = (contact) => {
        selectedContact = contact;
        elements.contactSelectionArea.classList.add('hidden');
        elements.selectedContactName.textContent = contact.name; 
        elements.selectedContactEmail.textContent = contact.email;
        dynamicElements.previewContactAddress.textContent = contact.street_address || contact.address || '';
        elements.selectedContactDisplay.classList.remove('hidden');
        elements.clientSuggestions.classList.add('hidden');
        elements.prospectSuggestions.classList.add('hidden');
        elements.clientSearch.value = '';
        elements.prospectSearch.value = '';
        updatePreview();
    };

    const updatePreview = () => {
        if (!dynamicElements.previewContactName) return;

        dynamicElements.previewContactName.textContent = selectedContact?.name || "Nombre del Contacto";
        dynamicElements.previewContactName.style.display = ''; 

        const address = selectedContact?.street_address || selectedContact?.address;
        if (address) {
            dynamicElements.previewContactAddress.textContent = address;
            dynamicElements.previewContactAddress.style.display = '';
        } else {
            dynamicElements.previewContactAddress.textContent = '';
            dynamicElements.previewContactAddress.style.display = 'none'; 
        }

        const email = selectedContact?.email;
        if (email) {
            dynamicElements.previewContactEmail.textContent = email;
            dynamicElements.previewContactEmail.style.display = ''; 
        } else {
            dynamicElements.previewContactEmail.textContent = '';
            dynamicElements.previewContactEmail.style.display = 'none'; 
        }

        const phone = selectedContact?.phone || selectedContact?.mobile;
        if (phone) {
            dynamicElements.previewContactPhone.textContent = phone;
            dynamicElements.previewContactPhone.style.display = ''; 
        } else {
            dynamicElements.previewContactPhone.textContent = '';
            dynamicElements.previewContactPhone.style.display = 'none'; 
        }

        dynamicElements.previewDate.textContent = new Date().toLocaleDateString('es-ES');
        let quoteNumberDisplay = 'NUEVO';
        if (elements.editingQuoteId.value) {
            const externalIdKey = elements.editingQuoteId.value;
            if (allEstimates[externalIdKey] && allEstimates[externalIdKey].db_id !== undefined) {
                const numericId = allEstimates[externalIdKey].db_id;
                if (!isNaN(numericId)) {
                    quoteNumberDisplay = String(numericId).padStart(3, '0');
                }
            }
        } else {
            quoteNumberDisplay = String(nextAvailableEstimateNumber).padStart(3, '0');
        }
        dynamicElements.previewQuoteId.textContent = quoteNumberDisplay;
        recalculateTotal();
    };

    const saveQuote = async () => {
        if (!selectedContact) {
            if (typeof showToast === 'function') showToast("Por favor, selecciona un contacto.", 'warning');
            return;
        }
        
        const itemsData = Array.from(dynamicElements.quoteItemsBody.querySelectorAll('tr')).map(row => {
            const desc = escapeHTML(row.cells[0].textContent);
            const qty = parseInt(row.cells[1].textContent) || 1;
            const unitPrice = parseFloat(row.cells[2].textContent.replace(/[$,]/g, '')) || 0;
            return [desc, unitPrice, qty];
        });
        
        if (itemsData.length === 0 || itemsData.every(item => item[0].trim() === '' && (parseInt(item[2]) || 0) === 0 && parseFloat(item[1]) === 0)) {
            if (typeof showToast === 'function') showToast("Por favor, agrega al menos un ítem.", 'warning');
            return;
        }
        
        // Fase V: Envío de CSRF Token en la solicitud
        const quoteData = {
            id: elements.editingQuoteId.value ? allEstimates[elements.editingQuoteId.value].db_id : null,
            estimate_date: new Date().toISOString().split('T')[0],
            status: 'generado',
            tax_rate: parseFloat(elements.taxRate.value) || 0,
            items: itemsData,
            client_id: selectedContact.type === 'client' ? selectedContact.id : null,
            lead_id: selectedContact.type === 'prospect' ? selectedContact.id : null,
            csrf_token: CSRF_TOKEN 
        };
        const apiUrl = quoteData.id ? API_URLS.QUOTE_UPDATE : API_URLS.QUOTE_CREATE;
        try {
            const response = await fetch(apiUrl, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(quoteData)
            });
            const result = await response.json();
            if (result.success) {
                // ÉXITO: Cerrar panel lateral y mostrar Modal con Link
                hideGeneratorView();

                // Construir URL del link
                let externalId = quoteData.id ? elements.editingQuoteId.value : result.external_id;
                const baseUrl = window.location.href.substring(0, window.location.href.lastIndexOf('/'));
                const fullLink = `${baseUrl}/quote-view.php?token=${externalId}`;

                // Rellenar Modal de Éxito
                const successLinkInput = document.getElementById('success-quote-link');
                const successOpenBtn = document.getElementById('success-open-btn');
                
                if (successLinkInput && successOpenBtn) {
                    successLinkInput.value = fullLink;
                    successOpenBtn.href = fullLink;
                    openModal('quoteSuccessModal');
                } else {
                    // Fallback si no existe el modal (no debería pasar)
                    if (typeof showToast === 'function') showToast(`Estimado guardado correctamente.`, 'success');
                    setTimeout(() => location.reload(), 1000);
                }

            } else {
                if (typeof showToast === 'function') showToast(`Error al guardar: ${result.message}`, 'error');
            }
        } catch (error) {
            console.error('Error al guardar:', error);
            if (typeof showToast === 'function') showToast('Error de conexión al guardar.', 'error');
        }
    };

const setupSearch = (inputEl, suggestionEl, contactType) => {
    inputEl.addEventListener('input', async () => {
        const searchTerm = inputEl.value.toLowerCase();
        suggestionEl.innerHTML = '';
        if (!searchTerm) { suggestionEl.classList.add('hidden'); return; }
        const sourceArray = contactType === 'client' ? allClients : allProspects;
        const matches = sourceArray.filter(c => c.name.toLowerCase().includes(searchTerm) || (c.email && c.email.toLowerCase().includes(searchTerm)));
        if (matches.length > 0) {
            matches.forEach(contact => {
                const div = document.createElement('div');
                const safeName = contact.name;
                const safeCompany = contact.company || '';
                
                // Seguridad: textContent previene XSS al renderizar nombres
                const mainDisplayDiv = document.createElement('div');
                mainDisplayDiv.className = 'font-bold text-gray-800';
                mainDisplayDiv.textContent = safeName; 

                const detailDisplayDiv = document.createElement('div');
                detailDisplayDiv.className = 'text-xs text-gray-500';
                const detailText = contact.type === 'prospect' && safeCompany ? ` (${safeCompany})` : '';
                detailDisplayDiv.textContent = detailText; 

                div.appendChild(mainDisplayDiv);
                if (detailText) {
                    div.appendChild(detailDisplayDiv);
                }

                div.className = 'p-2 hover:bg-gray-100 cursor-pointer text-sm border-b last:border-b-0';
                
                div.onclick = () => handleContactSelection(contact);
                suggestionEl.appendChild(div);
            });
            suggestionEl.classList.remove('hidden');
           } else {
            suggestionEl.classList.add('hidden');
        }
    });
    inputEl.addEventListener('blur', () => {
        setTimeout(() => { if (!suggestionEl.matches(':hover')) { suggestionEl.classList.add('hidden'); } }, 200);
    });
};

    const populateServiceDropdown = () => {
        elements.tipoServicio.innerHTML = '<option value="">-- Selecciona Un Servicio --</option>';
        const servicesByCategory = {};
        allServices.forEach(service => {
            const category = service.category || 'Sin Categoría';
            if (!servicesByCategory[category]) {
                servicesByCategory[category] = [];
            }
            servicesByCategory[category].push(service);
        });
        for (const category in servicesByCategory) {
            const optgroup = document.createElement('optgroup');
            optgroup.label = category; 
            servicesByCategory[category].forEach(service => {
                const option = document.createElement('option');
                option.value = service.id;
                option.textContent = `${service.name} ($${parseFloat(service.price).toFixed(2)})`; 
                optgroup.appendChild(option);
            });
            elements.tipoServicio.appendChild(optgroup);
        }
    };

    elements.tipoServicio.addEventListener('change', () => {
        const selectedServiceId = elements.tipoServicio.value;
        if (selectedServiceId) {
            const service = allServices.find(s => s.id == selectedServiceId);
            if (service) {
                
                const firstRow = dynamicElements.quoteItemsBody.querySelector('tr');
                const isDefaultRow = firstRow && firstRow.cells[0].textContent.trim() === 'Nuevo Ítem' && parseFloat(firstRow.cells[2].textContent.replace(/[$,]/g, '')) === 0;

                if (isDefaultRow) {
                    firstRow.remove();
                }
                
                dynamicElements.quoteItemsBody.appendChild(createItemRow(service.name, service.price, 1));

                recalculateTotal();
                
                if (typeof lucide !== 'undefined') {
                    lucide.createIcons();
                }
            }
        }
    });

    const fetchAndDisplayEstimates = async () => {
        try {
            const response = await fetch(API_URLS.QUOTE_READ);
            const result = await response.json();

            if (result.success) {
                allEstimates = {};
                let currentMaxDbId = 0;
                result.data.forEach(estimate => {
                    allEstimates[estimate.id] = estimate;
                    if (parseInt(estimate.db_id) > currentMaxDbId) {
                        currentMaxDbId = parseInt(estimate.db_id);
                    }
                });
                nextAvailableEstimateNumber = currentMaxDbId + 1;
                updateMonthFilterOptions();
            } else {
                if (typeof showToast === 'function') showToast('Error al cargar estimados: ' + (result.message || 'Error desconocido.'), 'error');
                console.error('Error al cargar estimados:', result.message);
            }
        } catch (error) {
            if (typeof showToast === 'function') showToast('Error de conexión al cargar estimados.', 'error');
            console.error('Error de conexión al cargar estimados:', error);
        }
    };

    const updateMonthFilterOptions = () => {
        const months = [...new Set(Object.values(allEstimates).map(q => q.fechaSolicitud.substring(0, 7)))].sort().reverse();
        elements.monthFilter.innerHTML = '<option value="all">TODOS LOS MESES</option>';
        months.forEach(m => {
            const date = new Date(m + '-02');
            const monthName = date.toLocaleString('es-ES', { month: 'long' });
            const year = date.getFullYear();
            elements.monthFilter.add(new Option(`${monthName.charAt(0).toUpperCase() + monthName.slice(1).toUpperCase()} ${year}`, m));
        });
    };

    elements.confirmConvertInvoiceButton.addEventListener('click', executeConvertQuote);


    (async function init() {
        <?php if (isset($db_error_message) && $db_error_message): ?>
            if (typeof showToast === 'function') showToast('<?php echo addslashes($db_error_message); ?>', 'error');
        <?php endif; ?>

        elements.listFilterStatus.innerHTML = '<option value="all">TODOS LOS ESTADOS</option>';
        ['generado', 'enviado', 'aprobado', 'rechazado', 'convertida'].forEach(s => elements.listFilterStatus.add(new Option(formatStatusText(s), s)));
        
        elements.listFilterContactType.innerHTML = '<option value="all">TODOS LOS CONTACTOS</option>';
        elements.listFilterContactType.add(new Option('CLIENTES', 'client'));
        elements.listFilterContactType.add(new Option('PROSPECTOS', 'prospect'));

        [elements.listSearch, elements.listFilterContactType, elements.listFilterStatus].forEach(el => el.addEventListener('input', updateDisplay));
        elements.monthFilter.addEventListener('change', updateDisplay);

        elements.showGeneratorBtn.addEventListener('click', () => showGeneratorView());
        elements.backToListBtn.addEventListener('click', hideGeneratorView);
        elements.generatorOverlay.addEventListener('click', hideGeneratorView);
        elements.saveQuoteBtn.addEventListener('click', saveQuote);
        elements.downloadPdfBtn.addEventListener('click', generatePDF);
        elements.changeContactBtn.addEventListener('click', resetGeneratorForm);
        elements.taxRate.addEventListener('input', recalculateTotal);

        elements.modalCancelBtn.addEventListener('click', hideStatusModal);
        elements.statusModalOverlay.addEventListener('click', (event) => {
            if (event.target === elements.statusModalOverlay) {
                hideStatusModal();
            }
        });
        elements.modalSaveStatusBtn.addEventListener('click', () => {
            const externalId = elements.modalEstimateId.value;
            const newStatus = elements.modalStatusSelect.value;
            updateEstimateStatus(externalId, newStatus);
        });

        const cancelDeleteBtn = document.getElementById('cancel-delete-btn');
        if (cancelDeleteBtn) {
            cancelDeleteBtn.addEventListener('click', () => closeModal('confirmDeleteModal'));
        }

        if (elements.confirmDeleteButton) {
            elements.confirmDeleteButton.addEventListener('click', async function() {
                const itemId = this.dataset.itemId;
                executeDeleteQuote(itemId);
            });
        }
        
        // Listener para el botón cerrar del modal de éxito (Recarga la página)
        const closeSuccessBtn = document.getElementById('close-success-modal-btn');
        if (closeSuccessBtn) {
            closeSuccessBtn.addEventListener('click', () => {
                location.reload();
            });
        }

        // Listener para copiar link del modal de éxito
        const successCopyBtn = document.getElementById('success-copy-btn');
        if (successCopyBtn) {
            successCopyBtn.addEventListener('click', () => {
                const linkInput = document.getElementById('success-quote-link');
                linkInput.select();
                linkInput.setSelectionRange(0, 99999);
                navigator.clipboard.writeText(linkInput.value).then(() => {
                    if (typeof showToast === 'function') showToast("Enlace copiado", 'success');
                });
            });
        }

        // Listener para copiar link del panel lateral
        const sidebarCopyBtn = document.getElementById('sidebar-copy-btn');
        if (sidebarCopyBtn) {
            sidebarCopyBtn.addEventListener('click', () => {
                const linkInput = document.getElementById('sidebar-quote-link');
                linkInput.select();
                linkInput.setSelectionRange(0, 99999);
                navigator.clipboard.writeText(linkInput.value).then(() => {
                    if (typeof showToast === 'function') showToast("Enlace copiado", 'success');
                });
            });
        }

        try {
            const [clientsResponse, leadsResponse, servicesResponse] = await Promise.all([
                fetch(API_URLS.CLIENTS_READ),
                fetch(API_URLS.LEADS_READ),
                fetch(API_URLS.SERVICES_READ)
            ]);

            const [clientsResult, leadsResult, servicesResult] = await Promise.all([
                clientsResponse.json(),
                leadsResponse.json(),
                servicesResponse.json()
            ]);

            if (clientsResult.success) {
                allClients = clientsResult.data.map(c => ({ 
                    ...c, 
                    name: c.name, 
                    email: c.email,
                    street_address: c.street_address
                }));
                setupSearch(elements.clientSearch, elements.clientSuggestions, 'client');
            } else {
                if (typeof showToast === 'function') showToast('Error al cargar clientes: ' + (clientsResult.message || 'Error desconocido.'), 'error');
            }
            if (leadsResult.success) {
                allProspects = leadsResult.data.map(p => ({ 
                    ...p, 
                    name: p.name, 
                    email: p.email,
                    company: p.company,
                    street_address: p.street_address
                }));
                setupSearch(elements.prospectSearch, elements.prospectSuggestions, 'prospect');
            } else {
                if (typeof showToast === 'function') showToast('Error al cargar prospectos: ' + (leadsResult.message || 'Error desconocido.'), 'error');
            }
            if (servicesResult.success) {
                allServices = servicesResult.data;
                populateServiceDropdown();
            } else {
                if (typeof showToast === 'function') showToast('Error al cargar servicios: ' + (servicesResult.message || 'Error desconocido.'), 'error');
            }

            await fetchAndDisplayEstimates();
            
            if (elements.monthFilter.querySelector(`option[value="${currentMonthString}"]`)) {
                elements.monthFilter.value = currentMonthString;
            } else {
                elements.monthFilter.value = 'all';
            }
            
            updateDisplay(); 

            if (typeof lucide !== 'undefined') lucide.createIcons();
        } catch (error) {
            console.error("Error en la carga inicial de datos:", error);
            if (typeof showToast === 'function') showToast("Error crítico al inicializar la aplicación. Verifique la consola.", "error");
        }
    })();
});
</script>
<script src="files/toast.js"></script>
</body>
</html>