jQuery(document).ready(function($) {

    const tabPageMap = {
        dashboard: 'vulnity',
        alerts: 'vulnity-alerts',
        mitigation: 'vulnity-mitigation',
        hardening: 'vulnity-hardening',
        sync: 'vulnity-sync',
        settings: 'vulnity-settings'
    };

    const mitigationSectionAliasMap = {
        overview: 'mitigation-overview',
        synchronization: 'mitigation-synchronization',
        sync: 'mitigation-synchronization',
        blocked: 'mitigation-blocked',
        blockedips: 'mitigation-blocked',
        'blocked-ip': 'mitigation-blocked',
        'blocked-ips': 'mitigation-blocked',
        rules: 'mitigation-rules',
        mitigationrules: 'mitigation-rules',
        'mitigation-rules': 'mitigation-rules',
        whitelist: 'mitigation-whitelist'
    };

    const settingsVisualLanguageStorageKey = 'vulnity_settings_visual_language';
    const settingsVisualLanguageText = {
        en: {
            'settings.connection_title': 'Connection',
            'settings.connection_subtitle': 'SIEM connection details and quick actions.',
            'settings.connected_pill': 'Connected',
            'settings.status_label': 'Status',
            'settings.connected_to_siem': 'Connected to SIEM',
            'settings.site_id_label': 'Site ID',
            'settings.paired_since_label': 'Paired since',
            'settings.plugin_version_label': 'Plugin version',
            'settings.language_title': 'Interface language',
            'settings.language_desc': 'Visual only. This does not change backend behavior.',
            'settings.lang_en': 'English',
            'settings.lang_es': 'Spanish',
            'settings.connection_test_title': 'Connection test',
            'settings.connection_test_desc': 'Verify SIEM connectivity.',
            'settings.connection_test_button': 'Test Connection',
            'settings.disconnect_title': 'Disconnect',
            'settings.disconnect_desc': 'This removes the site from SIEM monitoring.',
            'settings.disconnect_button': 'Disconnect',
            'settings.runtime_title': 'Security Runtime',
            'settings.runtime_subtitle': 'Active sensors and local processing behavior.',
            'settings.runtime_pill': '8 sensors',
            'settings.sensor_bruteforce': 'Brute Force Detection',
            'settings.sensor_file_upload': 'File Upload Monitoring',
            'settings.sensor_admin_changes': 'Admin Account Changes',
            'settings.sensor_permissions': 'Permission Changes',
            'settings.sensor_plugins_themes': 'Plugin and Theme Changes',
            'settings.sensor_core_updates': 'Core Updates',
            'settings.sensor_queries': 'Suspicious Queries',
            'settings.sensor_xmlrpc': 'XML-RPC Attack Detection',
            'settings.metric_alert_retention_label': 'Alert retention',
            'settings.metric_alert_retention_value': 'Last 100 alerts',
            'settings.metric_sync_frequency_label': 'Sync frequency',
            'settings.metric_sync_frequency_value': 'Every hour',
            'settings.metric_retry_queue_label': 'Retry queue',
            'settings.metric_retry_queue_value': 'Every 5 minutes',
            'settings.monitoring_scope_title': 'Monitoring Scope',
            'settings.monitoring_active_plugins': 'Active plugins',
            'settings.monitoring_themes': 'Themes',
            'settings.monitoring_users': 'Users',
            'settings.monitoring_admins': 'Admins',
            'settings.about_title': 'About Vulnity Security',
            'settings.about_subtitle': 'Version and support information.',
            'settings.about_version_label': 'Version:',
            'settings.about_line_1': 'Vulnity Security provides WordPress monitoring with SIEM integration for centralized visibility.',
            'settings.about_line_2': 'Security events are detected locally and delivered to your SIEM dashboard.',
            'settings.about_support_label': 'Support:',
            'settings.about_support_value': 'Use the Vulnity SIEM dashboard or contact support.',
            'settings.snapshot_title': 'Synchronization Snapshot',
            'settings.snapshot_status': 'Status',
            'settings.snapshot_last_sync': 'Last sync',
            'settings.snapshot_next_sync': 'Next sync',
            'settings.snapshot_support': 'Support',
            'settings.snapshot_support_value': 'SIEM dashboard / support team',
            'settings.checklist_title': 'Operational Checklist',
            'settings.checklist_1': 'Run connection test after infrastructure changes.',
            'settings.checklist_2': 'Review failed alerts in Alerts tab weekly.',
            'settings.checklist_3': 'Keep plugin and WordPress core updated.'
        },
        es: {
            'settings.connection_title': 'Conexion',
            'settings.connection_subtitle': 'Detalles de conexion SIEM y acciones rapidas.',
            'settings.connected_pill': 'Conectado',
            'settings.status_label': 'Estado',
            'settings.connected_to_siem': 'Conectado al SIEM',
            'settings.site_id_label': 'ID del sitio',
            'settings.paired_since_label': 'Vinculado desde',
            'settings.plugin_version_label': 'Version del plugin',
            'settings.language_title': 'Idioma de interfaz',
            'settings.language_desc': 'Solo visual. Esto no cambia el comportamiento del backend.',
            'settings.lang_en': 'Ingles',
            'settings.lang_es': 'Espanol',
            'settings.connection_test_title': 'Prueba de conexion',
            'settings.connection_test_desc': 'Verifica conectividad con SIEM.',
            'settings.connection_test_button': 'Probar conexion',
            'settings.disconnect_title': 'Desconectar',
            'settings.disconnect_desc': 'Esto elimina el sitio del monitoreo SIEM.',
            'settings.disconnect_button': 'Desconectar',
            'settings.runtime_title': 'Runtime de seguridad',
            'settings.runtime_subtitle': 'Sensores activos y comportamiento de procesamiento local.',
            'settings.runtime_pill': '8 sensores',
            'settings.sensor_bruteforce': 'Deteccion de fuerza bruta',
            'settings.sensor_file_upload': 'Monitoreo de subida de archivos',
            'settings.sensor_admin_changes': 'Cambios de cuentas admin',
            'settings.sensor_permissions': 'Cambios de permisos',
            'settings.sensor_plugins_themes': 'Cambios de plugins y temas',
            'settings.sensor_core_updates': 'Actualizaciones del core',
            'settings.sensor_queries': 'Consultas sospechosas',
            'settings.sensor_xmlrpc': 'Deteccion de ataques XML-RPC',
            'settings.metric_alert_retention_label': 'Retencion de alertas',
            'settings.metric_alert_retention_value': 'Ultimas 100 alertas',
            'settings.metric_sync_frequency_label': 'Frecuencia de sync',
            'settings.metric_sync_frequency_value': 'Cada hora',
            'settings.metric_retry_queue_label': 'Cola de reintentos',
            'settings.metric_retry_queue_value': 'Cada 5 minutos',
            'settings.monitoring_scope_title': 'Alcance de monitoreo',
            'settings.monitoring_active_plugins': 'Plugins activos',
            'settings.monitoring_themes': 'Temas',
            'settings.monitoring_users': 'Usuarios',
            'settings.monitoring_admins': 'Admins',
            'settings.about_title': 'Acerca de Vulnity Security',
            'settings.about_subtitle': 'Informacion de version y soporte.',
            'settings.about_version_label': 'Version:',
            'settings.about_line_1': 'Vulnity Security ofrece monitoreo de WordPress con integracion SIEM para visibilidad centralizada.',
            'settings.about_line_2': 'Los eventos de seguridad se detectan localmente y se envian a tu panel SIEM.',
            'settings.about_support_label': 'Soporte:',
            'settings.about_support_value': 'Usa el panel SIEM de Vulnity o contacta soporte.',
            'settings.snapshot_title': 'Resumen de sincronizacion',
            'settings.snapshot_status': 'Estado',
            'settings.snapshot_last_sync': 'Ultima sync',
            'settings.snapshot_next_sync': 'Proxima sync',
            'settings.snapshot_support': 'Soporte',
            'settings.snapshot_support_value': 'Panel SIEM / equipo de soporte',
            'settings.checklist_title': 'Checklist operativo',
            'settings.checklist_1': 'Ejecuta una prueba de conexion despues de cambios de infraestructura.',
            'settings.checklist_2': 'Revisa alertas fallidas en la pestana Alerts cada semana.',
            'settings.checklist_3': 'Manten actualizado el plugin y el core de WordPress.'
        }
    };

        const globalVisualTextEnToEs = {
        'Vulnity Security': 'Vulnity Security',
        'Dashboard': 'Panel',
        'Alerts': 'Alertas',
        'Mitigation': 'Mitigacion',
        'Hardening': 'Endurecimiento',
        'Synchronization': 'Sincronizacion',
        'Settings': 'Configuracion',
        'Need help finding your Site ID or Pairing Code?': 'Necesitas ayuda para encontrar tu Site ID o Pairing Code?',
        'or the': 'o la',
        'Quickstart guide': 'Guia de inicio rapido',
        'Which public IP should always be allowed?': 'Que IP publica siempre debe estar permitida?',
        'Optional. If provided, this IP is added to the whitelist immediately after pairing.': 'Opcional. Si se indica, esta IP se agrega a la lista blanca inmediatamente despues de emparejar.',
        'e.g. 203.0.113.10 or 2001:db8::1': 'ej. 203.0.113.10 o 2001:db8::1',
        'Security overview': 'Resumen de seguridad',
        'Alert volume and severity distribution across your Vulnity site.': 'Volumen de alertas y distribucion por severidad en tu sitio Vulnity.',
        'Total Alerts': 'Alertas totales',
        'Last 24 Hours': 'Ultimas 24 horas',
        'Critical': 'Critico',
        'High': 'Alta',
        'Medium': 'Media',
        'Low': 'Baja',
        'Info': 'Info',
        'Historical trend': 'Tendencia historica',
        'Recent activity': 'Actividad reciente',
        'Immediate attention': 'Atencion inmediata',
        'Priority review': 'Revision prioritaria',
        'Monitoring': 'Seguimiento',
        'Basic watch': 'Vigilancia basica',
        'Alert Delivery Status': 'Estado de entrega de alertas',
        'Monitor SIEM event delivery and act quickly on failures.': 'Supervisa el envio de eventos al SIEM y actua rapido ante fallos.',
        'Status': 'Estado',
        'Count': 'Cantidad',
        'Percentage': 'Porcentaje',
        'Sent to SIEM': 'Enviado a SIEM',
        'Failed/Pending': 'Fallido/Pendiente',
        'Note:': 'Nota:',
        'View Alerts Tab': 'Ver pestana Alerts',
        'Alert Distribution (24h)': 'Distribucion de alertas (24h)',
        'Understand how recent alerts are distributed by severity.': 'Analiza como se distribuyen las alertas recientes por severidad.',
        'Severity': 'Severidad',
        'System Status': 'Estado del sistema',
        'Review connectivity, synchronization, and active modules.': 'Revisa conectividad, sincronizacion y modulos activos.',
        'SIEM Connection:': 'Conexion SIEM:',
        'Connected': 'Conectado',
        'Site ID:': 'ID del sitio:',
        'Connected Since:': 'Conectado desde:',
        'Last Sync:': 'Ultima sync:',
        'Never synchronized': 'Nunca sincronizado',
        'Detection Modules:': 'Modulos de deteccion:',
        'Mark All as Viewed': 'Marcar todo como visto',
        'Clear All': 'Limpiar todo',
        'Retry All Failed': 'Reintentar todas las fallidas',
        'No security alerts detected yet.': 'Aun no se detectaron alertas de seguridad.',
        'View Details': 'Ver detalles',
        'Mark as Viewed': 'Marcar como visto',
        'Not Sent': 'No enviado',
        'Retry Send': 'Reintentar envio',
        'Retries:': 'Reintentos:',
        'Error:': 'Error:',
        'Overview': 'Resumen',
        'Blocked IPs': 'IPs bloqueadas',
        'Mitigation Rules': 'Reglas de mitigacion',
        'Whitelist': 'Lista blanca',
        'Mitigation Status': 'Estado de mitigacion',
        'Blocking Method:': 'Metodo de bloqueo:',
        'PHP-based blocking (Universal compatibility)': 'Bloqueo basado en PHP (compatibilidad universal)',
        'Blocking Status:': 'Estado del bloqueo:',
        'Active and Operational': 'Activo y operativo',
        'Inactive': 'Inactivo',
        'Last Synchronization:': 'Ultima sincronizacion:',
        'Automatic Sync:': 'Sincronizacion automatica:',
        'Block Page:': 'Pagina de bloqueo:',
        'Professional branded page with block details': 'Pagina profesional con detalles del bloqueo',
        'Block Statistics by Source': 'Estadisticas de bloqueo por origen',
        'Source': 'Origen',
        'Description': 'Descripcion',
        'Auto-blocked': 'Auto bloqueadas',
        'Manual blocks': 'Bloqueos manuales',
        'SIEM Sync': 'Sync SIEM',
        'Vulnity Blacklist': 'Blacklist Vulnity',
        'Active Rules': 'Reglas activas',
        'Synchronization Status': 'Estado de sincronizacion',
        'Last sync': 'Ultima sync',
        'Next auto sync': 'Proxima sync automatica',
        'Sync frequency': 'Frecuencia de sync',
        'SIEM updated': 'SIEM actualizado',
        'SIEM Configuration Synchronization': 'Sincronizacion de configuracion SIEM',
        'Keep mitigation rules, whitelist entries, and blocked IPs aligned with your SIEM dashboard.': 'Mantiene reglas de mitigacion, lista blanca e IPs bloqueadas alineadas con tu panel SIEM.',
        'Never synced': 'Nunca sincronizada',
        'Last sync failed': 'Ultima sincronizacion fallida',
        'Synchronized': 'Sincronizada',
        'Synchronizing': 'Sincronizando',
        'Failed': 'Fallido',
        'Unknown': 'Desconocido',
        'Success': 'Exito',
        'Sync Configuration Now': 'Sincronizar configuracion ahora',
        'What gets synchronized': 'Que se sincroniza',
        'Security note': 'Nota de seguridad',
        'Use the button below to run your first synchronization.': 'Usa el boton de abajo para ejecutar tu primera sincronizacion.',
        'Check your cron configuration and run a manual sync.': 'Revisa la configuracion de cron y ejecuta una sincronizacion manual.',
        'Manual synchronization is available at any time.': 'La sincronizacion manual esta disponible en cualquier momento.',
        'Mitigation rules:': 'Reglas de mitigacion:',
        'Automatic block thresholds and conditions.': 'Umbrales y condiciones de bloqueo automatico.',
        'IPs that are exempt from automatic blocking.': 'IPs exentas de bloqueo automatico.',
        'Centrally managed blocked addresses.': 'Direcciones bloqueadas gestionadas de forma centralizada.',
        'Security policies:': 'Politicas de seguridad:',
        'Configuration metadata distributed from SIEM.': 'Metadatos de configuracion distribuidos desde SIEM.',
        'Configuration is managed centrally in the SIEM to keep all connected sites consistent.': 'La configuracion se gestiona de forma centralizada en el SIEM para mantener consistencia entre sitios.',
        'Local blocks created manually in WordPress are preserved during synchronization.': 'Los bloqueos locales creados manualmente en WordPress se conservan durante la sincronizacion.',
        'Synchronization uses authenticated API communication between this site and the SIEM backend.': 'La sincronizacion usa comunicacion API autenticada entre este sitio y el backend SIEM.',
        'Manual IP Blocking': 'Bloqueo manual de IP',
        'IP Address': 'Direccion IP',
        'IP Address (e.g., 192.168.1.1)': 'Direccion IP (ej. 192.168.1.1)',
        'Reason for blocking': 'Motivo del bloqueo',
        'Block IP': 'Bloquear IP',
        'Currently Blocked IP Addresses': 'IPs bloqueadas actualmente',
        'No Blocked IPs': 'No hay IPs bloqueadas',
        'No IP addresses are currently blocked. IPs will appear here when they are blocked manually or by mitigation rules.': 'No hay direcciones IP bloqueadas actualmente. Las IPs apareceran aqui cuando se bloqueen manualmente o por reglas de mitigacion.',
        'Unblock Selected': 'Desbloquear seleccionadas',
        '0 IPs selected': '0 IPs seleccionadas',
        'Select multiple addresses to unblock them in a single action.': 'Selecciona varias direcciones para desbloquearlas en una sola accion.',
        'Reason': 'Motivo',
        'Blocked Since': 'Bloqueada desde',
        'Expires': 'Expira',
        'Permanent': 'Permanente',
        'Expired': 'Expirada',
        'Actions': 'Acciones',
        'Unblock': 'Desbloquear',
        'Auto': 'Auto',
        'Manual': 'Manual',
        'Sync': 'Sync',
        '15 Minutes': '15 minutos',
        '30 Minutes': '30 minutos',
        '1 Hour': '1 hora',
        '6 Hours': '6 horas',
        '24 Hours': '24 horas',
        '7 Days': '7 dias',
        '30 Days': '30 dias',
        'Automated Mitigation Rules': 'Reglas de mitigacion automatizadas',
        'These rules automatically block IP addresses when specific thresholds are exceeded. Rules are managed from the SIEM dashboard and synced to your site.': 'Estas reglas bloquean automaticamente direcciones IP cuando se superan umbrales especificos. Las reglas se gestionan desde el panel SIEM y se sincronizan con tu sitio.',
        'No mitigation rules configured yet.': 'Aun no hay reglas de mitigacion configuradas.',
        'Go to the Synchronization tab and click "Sync Configuration Now" to fetch the latest rules from your SIEM dashboard.': 'Ve a la pestana Synchronization y pulsa "Sync Configuration Now" para traer las ultimas reglas desde tu panel SIEM.',
        'Alert Type': 'Tipo de alerta',
        'Time Window': 'Ventana de tiempo',
        'Min Severity': 'Severidad minima',
        'Rule Description': 'Descripcion de regla',
        'Enabled': 'Habilitada',
        'Disabled': 'Deshabilitada',
        'alerts': 'alertas',
        'Automatically block IP after detecting': 'Bloquear IP automaticamente despues de detectar',
        'of severity': 'de severidad',
        'or higher': 'o superior',
        'within': 'dentro de',
        'How it works:': 'Como funciona:',
        'Mitigation rules are configured in your SIEM dashboard and automatically synced every 24 hours.': 'Las reglas de mitigacion se configuran en tu panel SIEM y se sincronizan automaticamente cada 24 horas.',
        'When an IP address triggers a rule threshold, it\'s automatically blocked for the specified duration.': 'Cuando una direccion IP dispara el umbral de una regla, se bloquea automaticamente durante la duracion especificada.',
        'Changes made in the SIEM will be reflected here after the next sync.': 'Los cambios hechos en el SIEM se reflejaran aqui tras la siguiente sincronizacion.',
        'IP Address Whitelist': 'Lista blanca de IPs',
        'Whitelist managed from SIEM': 'Lista blanca gestionada desde SIEM',
        'For security reasons, allowed addresses are managed only from the SIEM. Use the SIEM dashboard to add or remove trusted IPs, then synchronize to keep this list updated.': 'Por seguridad, las direcciones permitidas se gestionan solo desde el SIEM. Usa el panel SIEM para anadir o eliminar IPs de confianza y luego sincroniza para mantener esta lista actualizada.',
        'Whitelisted IP addresses are immune to automatic blocking. These IPs will never be blocked regardless of alert activity or mitigation rules.': 'Las IPs en lista blanca son inmunes al bloqueo automatico. Nunca se bloquearan sin importar la actividad de alertas o las reglas de mitigacion.',
        'No Whitelisted IPs': 'No hay IPs en lista blanca',
        'No IP addresses are currently whitelisted.': 'No hay direcciones IP en lista blanca.',
        'Add trusted IPs from your SIEM dashboard and sync to update.': 'Anade IPs de confianza desde tu panel SIEM y sincroniza para actualizar.',
        'Go to Synchronization': 'Ir a Sincronizacion',
        'Protection Level': 'Nivel de proteccion',
        'Whitelisted': 'En lista blanca',
        'Auto-block Immune': 'Inmune a autobloqueo',
        'Manual Block Protected': 'Protegida de bloqueo manual',
        'Always Allowed': 'Siempre permitida',
        'Managing the Whitelist:': 'Gestion de la lista blanca:',
        'The whitelist is centrally managed from your SIEM dashboard for security reasons.': 'La lista blanca se gestiona de forma centralizada desde tu panel SIEM por motivos de seguridad.',
        'To add or remove IPs from the whitelist, please use the SIEM interface. Changes will be synced to all connected WordPress sites automatically.': 'Para anadir o eliminar IPs de la lista blanca, usa la interfaz SIEM. Los cambios se sincronizaran automaticamente con todos los sitios WordPress conectados.',
        'Enable the protections you need. Changes apply immediately.': 'Activa las protecciones que necesites. Los cambios aplican inmediatamente.',
        'enabled': 'habilitadas',
        'Login path': 'Ruta de login',
        'Save Changes': 'Guardar cambios',
        'Disable XML-RPC': 'Desactivar XML-RPC',
        'Blocks anonymous XML-RPC requests.': 'Bloquea solicitudes XML-RPC anonimas.',
        'Rename Login URL': 'Renombrar URL de login',
        'Hides wp-login.php behind a custom slug.': 'Oculta wp-login.php detras de un slug personalizado.',
        'Login slug': 'Slug de login',
        'Set any slug you prefer for the new login URL.': 'Define el slug que prefieras para la nueva URL de login.',
        'Restrict REST API': 'Restringir REST API',
        'Requires authentication for REST API.': 'Requiere autenticacion para REST API.',
        'Block User Enumeration': 'Bloquear enumeracion de usuarios',
        'Prevents author ID scraping.': 'Evita scraping de ID de autor.',
        'Protect Common Paths': 'Proteger rutas comunes',
        'Blocks directory indexing access.': 'Bloquea acceso a indexacion de directorios.',
        'Login Attempt Limit': 'Limite de intentos de login',
        'Locks out repeated failures.': 'Bloquea tras fallos repetidos.',
        'Maximum attempts': 'Intentos maximos',
        'Lockout minutes': 'Minutos de bloqueo',
        'When the threshold is reached, the IP is blocked from login for the configured minutes.': 'Cuando se alcanza el umbral, la IP queda bloqueada del login durante los minutos configurados.',
        'Inventory Synchronization': 'Sincronizacion de inventario',
        'Keep this site inventory aligned with your SIEM dashboard.': 'Mantiene este inventario del sitio alineado con tu panel SIEM.',
        'Next scheduled sync': 'Proxima sync programada',
        'Automatic frequency': 'Frecuencia automatica',
        'Every 24 hours': 'Cada 24 horas',
        'Every 24 hours (automatic)': 'Cada 24 horas (automatica)',
        'Sync Now': 'Sincronizar ahora',
        'Coverage Snapshot': 'Resumen de cobertura',
        'Tracked assets': 'Activos monitoreados',
        'Pending updates': 'Actualizaciones pendientes',
        'Admins tracked': 'Admins monitoreados',
        'Transport': 'Transporte',
        'Secure SIEM API': 'API SIEM segura',
        'Synchronization Policy': 'Politica de sincronizacion',
        'What is synchronized and how often.': 'Que se sincroniza y cada cuanto.',
        'Managed': 'Gestionado',
        'Automatic sync': 'Sync automatica',
        'Manual sync': 'Sync manual',
        'On demand': 'Bajo demanda',
        'Delivery': 'Entrega',
        'SIEM API': 'API SIEM',
        'Recommended Workflow': 'Flujo recomendado',
        'Run manual sync after plugin, theme, or core changes.': 'Ejecuta sincronizacion manual despues de cambios en plugins, temas o core.',
        'Review pending updates after each successful synchronization.': 'Revisa actualizaciones pendientes despues de cada sincronizacion correcta.',
        'Verify status is synchronized at least once per day.': 'Verifica que el estado este sincronizado al menos una vez al dia.',
        'Current Inventory Snapshot': 'Resumen actual de inventario',
        'Latest synchronized components and update posture.': 'Componentes sincronizados mas recientes y estado de actualizacion.',
        'Live data': 'Datos en vivo',
        'Total plugins': 'Plugins totales',
        'Active plugins': 'Plugins activos',
        'Users': 'Usuarios',
        'Component': 'Componente',
        'Version': 'Version',
        'Update Status': 'Estado de actualizacion',
        'WordPress Core': 'Core de WordPress',
        'Update available': 'Actualizacion disponible',
        'Up to date': 'Actualizado',
        'Informational': 'Informativo',
        'Plugins': 'Plugins',
        'All up to date': 'Todo actualizado',
        'Themes': 'Temas',
        'Administrators': 'Administradores',
        'Inventory only': 'Solo inventario',
        'Vulnity Security Setup': 'Configuracion de Vulnity Security',
        'Connect this site to Vulnity SIEM to enable monitoring, synchronization, and mitigation workflows.': 'Conecta este sitio a Vulnity SIEM para activar monitoreo, sincronizacion y mitigacion.',
        'Step 1 of 1': 'Paso 1 de 1',
        'SIEM Pairing': 'Vinculacion SIEM',
        'Connect to Vulnity SIEM': 'Conectar con Vulnity SIEM',
        'Use the credentials generated in your Vulnity SIEM dashboard.': 'Usa las credenciales generadas en tu panel SIEM de Vulnity.',
        'Site ID': 'ID del sitio',
        'Paste the Site ID exactly as shown in SIEM.': 'Pega el Site ID exactamente como aparece en SIEM.',
        'Pairing Code': 'Codigo de vinculacion',
        '8 character code': 'Codigo de 8 caracteres',
        'Enter the one-time 8 character pairing code.': 'Ingresa el codigo de vinculacion de 8 caracteres de un solo uso.',
        'Which public IP should always be allowed?': 'Que IP publica debe permitirse siempre?',
        'Optional. If provided, this IP is added to the whitelist immediately after pairing.': 'Opcional. Si la indicas, esta IP se agrega a la lista blanca justo despues del pairing.',
        'Connect Plugin': 'Conectar plugin',
        'How to get your credentials': 'Como obtener tus credenciales',
        'Follow this flow from your SIEM dashboard.': 'Sigue este flujo desde tu panel SIEM.',
        'Sign in to Vulnity SIEM.': 'Inicia sesion en Vulnity SIEM.',
        'Open Sites and create a new site record.': 'Abre Sites y crea un nuevo registro de sitio.',
        'Copy the Site ID and Pairing Code.': 'Copia el Site ID y el Pairing Code.',
        'Paste both values here and connect.': 'Pega ambos valores aqui y conecta.',
        'Before you connect': 'Antes de conectar',
        'Use credentials from the same SIEM environment.': 'Usa credenciales del mismo entorno SIEM.',
        'Do not add extra spaces before or after values.': 'No anadas espacios extra antes o despues de los valores.',
        'Keep this page open until you see the success message.': 'Manten esta pagina abierta hasta ver el mensaje de exito.',
        'Sign in to Vulnity before continuing': 'Inicia sesion en Vulnity antes de continuar',
        'You need an active Vulnity account to get your Site ID and Pairing Code. Access your dashboard at': 'Necesitas una cuenta activa de Vulnity para obtener tu Site ID y Pairing Code. Accede a tu panel desde',
        'Sign in →': 'Iniciar sesion →',
        'Failed / Pending': 'Fallido / Pendiente',
        'Operational': 'Operativo',
        'Needs review': 'Requiere revision',
        'SIEM Connection': 'Conexion SIEM',
        'Connected Since': 'Conectado desde',
        'Last Sync': 'Ultima sync',
        'Detection Modules': 'Modulos de deteccion',
        'Background retry attempts occur every 5 minutes.': 'Los reintentos en segundo plano se ejecutan cada 5 minutos.',
        'The domain configured for this WordPress site does not match the domain registered in SIEM. Verify the URL in Add Domain -> Settings -> Full WordPress URL (for example, https://example.com).': 'El dominio configurado para este WordPress no coincide con el dominio registrado en SIEM. Verifica la URL en Add Domain -> Settings -> Full WordPress URL (por ejemplo, https://example.com).',
        'The Site ID or Pair Code is incorrect.': 'El Site ID o el Pair Code son incorrectos.',
        'This site was removed from the SIEM dashboard. The plugin disconnected automatically. You can pair it again whenever you need.': 'Este sitio se elimino del panel SIEM. El plugin se desconecto automaticamente. Puedes volver a vincularlo cuando lo necesites.',
        'Connection successful!': 'Conexion correcta',
        'Inventory synchronized successfully': 'Inventario sincronizado correctamente',
        'Plugin successfully paired with Vulnity SIEM': 'Plugin vinculado correctamente con Vulnity SIEM',
        'Plugin successfully paired with Vulnity SIEM. Public IP added to whitelist.': 'Plugin vinculado correctamente con Vulnity SIEM. La IP publica se ha anadido a la lista blanca.',
        '. Public IP added to whitelist.': '. La IP publica se ha anadido a la lista blanca.',
        'Whitelist managed from SIEM': 'Lista blanca gestionada desde SIEM',
        'For security reasons, allowed addresses are managed only from SIEM. Use the SIEM dashboard to add or remove trusted IPs, then synchronize to keep this list updated.': 'Por seguridad, las direcciones permitidas se gestionan solo desde SIEM. Usa el panel SIEM para anadir o eliminar IPs de confianza y luego sincroniza para mantener esta lista actualizada.',
        'Whitelisted IP addresses are protected from automatic blocking. These IPs are never blocked regardless of alert activity or mitigation rules.': 'Las IPs en lista blanca estan protegidas frente al bloqueo automatico. Nunca se bloquean independientemente de la actividad de alertas o las reglas de mitigacion.',
        'Suppress alerts for whitelisted IPs': 'Suprimir alertas de IPs en lista blanca',
        'When enabled, security events originating from whitelisted IPs do not create alerts.': 'Cuando esta activado, los eventos de seguridad originados desde IPs en lista blanca no generan alertas.',
        'Server Configuration': 'Configuracion del servidor',
        'Detected Server': 'Servidor detectado',
        'Unknown Server': 'Servidor desconocido',
        'Good News!': 'Buenas noticias!',
        'Your server supports .htaccess files. Vulnity manages these rules automatically. No manual configuration needed.': 'Tu servidor admite archivos .htaccess. Vulnity gestiona estas reglas automaticamente. No necesitas configuracion manual.',
        'Automatic Configuration': 'Configuracion automatica',
        'The following features are configured automatically via .htaccess:': 'Las siguientes funciones se configuran automaticamente mediante .htaccess:',
        'Custom Login URL': 'URL de login personalizada',
        'Common Paths Protection': 'Proteccion de rutas comunes',
        'Log Directory Protection': 'Proteccion del directorio de logs',
        'No active features requiring Nginx configuration. Enable features in the Mitigation tab first.': 'No hay funciones activas que requieran configuracion Nginx. Activa primero las funciones en la pestana Mitigation.',
        'Action Required': 'Accion requerida',
        'Nginx does not support .htaccess files. You need to manually add the configuration below to your nginx.conf or server block.': 'Nginx no admite archivos .htaccess. Debes anadir manualmente la configuracion siguiente a tu nginx.conf o al bloque del servidor.',
        'Nginx Configuration': 'Configuracion Nginx',
        'Add the following to your Nginx server block configuration:': 'Anade lo siguiente a la configuracion del bloque de servidor de Nginx:',
        'Copy Configuration': 'Copiar configuracion',
        'Installation Steps': 'Pasos de instalacion',
        'Copy the configuration above': 'Copia la configuracion anterior',
        'Open your Nginx configuration file (usually /etc/nginx/sites-available/your-site)': 'Abre tu archivo de configuracion de Nginx (normalmente /etc/nginx/sites-available/your-site)',
        'Paste the configuration inside your server { } block': 'Pega la configuracion dentro del bloque server { }',
        'Test the configuration: sudo nginx -t': 'Prueba la configuracion: sudo nginx -t',
        'Reload Nginx: sudo systemctl reload nginx': 'Recarga Nginx: sudo systemctl reload nginx',
        'Alternative: PHP-Only Protection': 'Alternativa: proteccion solo PHP',
        'Vulnity also provides basic PHP-level protection that works without server configuration. However, server-level protection is more secure and performant.': 'Vulnity tambien ofrece una proteccion basica a nivel PHP que funciona sin configuracion del servidor. Sin embargo, la proteccion a nivel de servidor es mas segura y eficiente.',
        'Unknown Server Type': 'Tipo de servidor desconocido',
        'Vulnity could not detect your web server. Please configure manually based on your server type.': 'Vulnity no pudo detectar tu servidor web. Configuralo manualmente segun tu tipo de servidor.',
        'Manual Configuration Required': 'Configuracion manual requerida',
        'If you are using:': 'Si estas usando:',
        'Configuration should work automatically via .htaccess': 'La configuracion deberia funcionar automaticamente mediante .htaccess',
        'See the Nginx configuration section above': 'Consulta arriba la seccion de configuracion Nginx',
        'Consult your server documentation for implementing URL rewrites and path protection': 'Consulta la documentacion de tu servidor para implementar reescrituras de URL y proteccion de rutas',
        'Need Help?': 'Necesitas ayuda?',
        'If you need assistance with server configuration, please visit our': 'Si necesitas ayuda con la configuracion del servidor, visita nuestra',
        'documentation': 'documentacion',
        'or contact your hosting provider.': 'o contacta con tu proveedor de hosting.'
    };

    const visualMessageText = {
        en: {
            required_pairing: 'Site ID and Pair Code are required',
            invalid_site_id: 'Invalid Site ID format',
            invalid_pair_code: 'Pair code must be exactly 8 characters',
            invalid_public_ip: 'Please enter a valid public IP address (IPv4 or IPv6)',
            connecting: 'Connecting...',
            connection_failed: 'Connection failed',
            connect_plugin: 'Connect Plugin',
            manual_block: 'Manual block',
            enter_ip: 'Please enter an IP address',
            invalid_ip: 'Please enter a valid IP address (e.g., 192.168.1.1)',
            confirm_block: 'Are you sure you want to block {ip} for {duration}?',
            ip_blocked_success: 'IP blocked successfully',
            error_prefix: 'Error: ',
            one_ip_selected: '1 IP selected',
            many_ips_selected: '{count} IPs selected',
            confirm_bulk_unblock: 'Are you sure you want to unblock the selected {count} IP address(es)? They will regain access immediately.',
            unblocking: 'Unblocking...',
            unblocked_success: 'Unblocked {count} IPs successfully.',
            request_failed: 'Request failed. Please try again.',
            clear_alerts_confirm: 'Are you sure you want to clear all alerts?',
            testing: 'Testing...',
            unpair_confirm: 'Are you sure you want to disconnect from Vulnity SIEM?',
            synchronizing: 'Synchronizing...',
            sync_now: 'Sync Now',
            retrying: 'Retrying...',
            sent: 'Sent',
            failed: 'Failed',
            failed_to_send: 'Failed to send alert: {error}',
            retry_all_confirm: 'Retry sending all failed alerts to SIEM?',
            processing: 'Processing...',
            retry_all_failed: 'Retry All Failed',
            unblock_ip_confirm: 'Are you sure you want to unblock {ip}? This IP will be able to access the site immediately.',
            unblock_ip_success_notice: 'IP {ip} has been unblocked successfully.',
            syncing_wait: 'Syncing... Please wait',
            connecting_siem: 'Connecting to SIEM...',
            mitigation_synced: 'Mitigation configuration synchronized.',
            mitigation_sync_success_notice: 'Mitigation configuration has been synchronized.',
            mitigation_sync_unable: 'Unable to synchronize mitigation configuration.',
            sync_failed_title: 'Sync Failed:',
            success_title: 'Success!',
            connection_failed_retry: 'Connection failed. Please try again.',
            unblocking_label: 'Unblock Selected',
            retry_send: 'Retry Send'
        },
        es: {
            required_pairing: 'Site ID y Pair Code son obligatorios',
            invalid_site_id: 'Formato de Site ID invalido',
            invalid_pair_code: 'El Pair Code debe tener exactamente 8 caracteres',
            invalid_public_ip: 'Introduce una direccion IP publica valida (IPv4 o IPv6)',
            connecting: 'Conectando...',
            connection_failed: 'Fallo de conexion',
            connect_plugin: 'Conectar plugin',
            manual_block: 'Bloqueo manual',
            enter_ip: 'Introduce una direccion IP',
            invalid_ip: 'Introduce una direccion IP valida (ej. 192.168.1.1)',
            confirm_block: 'Seguro que quieres bloquear {ip} durante {duration}?',
            ip_blocked_success: 'IP bloqueada correctamente',
            error_prefix: 'Error: ',
            one_ip_selected: '1 IP seleccionada',
            many_ips_selected: '{count} IPs seleccionadas',
            confirm_bulk_unblock: 'Seguro que quieres desbloquear las {count} IP seleccionadas?\\n\\nRecuperaran acceso inmediatamente.',
            unblocking: 'Desbloqueando...',
            unblocked_success: 'Se desbloquearon {count} IPs correctamente.',
            request_failed: 'La solicitud fallo. Intentalo de nuevo.',
            clear_alerts_confirm: 'Seguro que quieres eliminar todas las alertas?',
            testing: 'Probando...',
            unpair_confirm: 'Seguro que quieres desconectar de Vulnity SIEM?',
            synchronizing: 'Sincronizando...',
            sync_now: 'Sincronizar ahora',
            retrying: 'Reintentando...',
            sent: 'Enviado',
            failed: 'Fallido',
            failed_to_send: 'No se pudo enviar la alerta: {error}',
            retry_all_confirm: 'Reintentar envio de todas las alertas fallidas al SIEM?',
            processing: 'Procesando...',
            retry_all_failed: 'Reintentar todas las fallidas',
            unblock_ip_confirm: 'Seguro que quieres desbloquear {ip}? Esta IP podra acceder al sitio inmediatamente.',
            unblock_ip_success_notice: 'La IP {ip} se ha desbloqueado correctamente.',
            syncing_wait: 'Sincronizando... Espera',
            connecting_siem: 'Conectando con SIEM...',
            mitigation_synced: 'Configuracion de mitigacion sincronizada.',
            mitigation_sync_success_notice: 'La configuracion de mitigacion se ha sincronizado.',
            mitigation_sync_unable: 'No se pudo sincronizar la configuracion de mitigacion.',
            sync_failed_title: 'Sync fallida:',
            success_title: 'Exito!',
            connection_failed_retry: 'Fallo de conexion. Intentalo de nuevo.',
            unblocking_label: 'Desbloquear seleccionadas',
            retry_send: 'Reintentar envio'
        }
    };

    const globalVisualTextEsToEn = Object.keys(globalVisualTextEnToEs).reduce(function(acc, enText) {
        const esText = globalVisualTextEnToEs[enText];
        if (typeof esText === 'string' && esText.length > 0) {
            acc[esText] = enText;
        }
        return acc;
    }, {});

    let currentVisualLanguage = 'en';
    const textNodeSourceCache = new WeakMap();
    const attributeSourceCache = new WeakMap();
    let visualLanguageObserver = null;

    function normalizeSectionKey(rawValue) {
        if (!rawValue) {
            return '';
        }

        return String(rawValue)
            .trim()
            .toLowerCase()
            .replace(/^mitigation[-_\s]?/, '')
            .replace(/[_\s]+/g, '-');
    }

    function sectionTabToParam(sectionTab) {
        return String(sectionTab || 'mitigation-overview').replace(/^mitigation-/, '');
    }

    function resolveMitigationSection(sectionValue) {
        if (!sectionValue) {
            return '';
        }

        const rawSection = String(sectionValue).trim().toLowerCase();

        if ($('.vulnity-mitigation-tab[data-tab="' + rawSection + '"]').length) {
            return rawSection;
        }

        const normalized = normalizeSectionKey(sectionValue);
        const aliasedSection = mitigationSectionAliasMap[normalized];

        if (aliasedSection && $('.vulnity-mitigation-tab[data-tab="' + aliasedSection + '"]').length) {
            return aliasedSection;
        }

        const prefixedSection = 'mitigation-' + normalized;
        if ($('.vulnity-mitigation-tab[data-tab="' + prefixedSection + '"]').length) {
            return prefixedSection;
        }

        return '';
    }

    function getActiveMitigationSection() {
        const activeSection = $('.vulnity-mitigation-tab.active').attr('data-tab');
        return resolveMitigationSection(activeSection) || 'mitigation-overview';
    }

    function setActiveMitigationSection(sectionValue, updateUrl = true) {
        const targetSection = resolveMitigationSection(sectionValue) || 'mitigation-overview';
        const $sectionButton = $('.vulnity-mitigation-tab[data-tab="' + targetSection + '"]');
        const $sectionContent = $('#mitigation-tab-' + targetSection);

        if (!$sectionButton.length || !$sectionContent.length) {
            return;
        }

        $('.vulnity-mitigation-tab').removeClass('active');
        $sectionButton.addClass('active');

        $('.vulnity-mitigation-content').hide().removeClass('active');
        $sectionContent.fadeIn(200).addClass('active');

        if (!updateUrl) {
            return;
        }

        try {
            const url = new URL(window.location.href);
            url.searchParams.set('page', tabPageMap.mitigation);
            url.searchParams.set('tab', 'mitigation');
            url.searchParams.set('section', sectionTabToParam(targetSection));
            window.history.replaceState({}, '', url.toString());
        } catch (error) {
            // Silent fail - do not block tab switching if URL manipulation fails
        }
    }

    function syncMitigationSectionWithUrl() {
        try {
            const url = new URL(window.location.href);
            const sectionFromUrl = url.searchParams.get('section');

            if (!sectionFromUrl) {
                return false;
            }

            const tabFromUrl = url.searchParams.get('tab');
            const pageFromUrl = url.searchParams.get('page');
            const activeTopTab = $('.vulnity-tab.active').attr('data-tab');
            const isMitigationContext = activeTopTab === 'mitigation' || tabFromUrl === 'mitigation' || pageFromUrl === tabPageMap.mitigation;

            if (!isMitigationContext) {
                return false;
            }

            setActiveMitigationSection(sectionFromUrl, false);
            return true;
        } catch (error) {
            // Ignore URL parsing issues and rely on rendered active section
        }

        return false;
    }

    function setActiveTab(targetTab, updateUrl = true) {
        if (!targetTab) {
            return;
        }

        const $tabButton = $('.vulnity-tab[data-tab="' + targetTab + '"]');
        const $tabContent = $('#tab-' + targetTab);

        if (!$tabButton.length || !$tabContent.length) {
            return;
        }

        $('.vulnity-tab').removeClass('active');
        $('.vulnity-tab-content').removeClass('active').attr('hidden', true);

        $tabButton.addClass('active');
        $tabContent.addClass('active').removeAttr('hidden');

        if (!updateUrl) {
            return;
        }

        try {
            const url = new URL(window.location.href);

            if (tabPageMap[targetTab]) {
                url.searchParams.set('page', tabPageMap[targetTab]);
            }

            url.searchParams.set('tab', targetTab);

            if (targetTab === 'mitigation') {
                url.searchParams.set('section', sectionTabToParam(getActiveMitigationSection()));
            } else {
                url.searchParams.delete('section');
            }

            window.history.replaceState({}, '', url.toString());
        } catch (error) {
            // Silent fail - do not block tab switching if URL manipulation fails
        }
    }

    $('.vulnity-tab').on('click', function(e) {
        e.preventDefault();

        const tab = $(this).attr('data-tab');
        setActiveTab(tab, true);
    });

    function syncTabWithUrl() {
        try {
            const url = new URL(window.location.href);
            const tabFromUrl = url.searchParams.get('tab');
            const pageFromUrl = url.searchParams.get('page');

            if (tabFromUrl) {
                setActiveTab(tabFromUrl, false);
                return true;
            } else if (pageFromUrl) {
                const derivedTab = Object.entries(tabPageMap).find(([, page]) => page === pageFromUrl);

                if (derivedTab && derivedTab[0]) {
                    setActiveTab(derivedTab[0], false);
                    return true;
                }
            }
        } catch (error) {
            // Ignore URL parsing issues and rely on server-rendered active tab
        }

        return false;
    }

    const syncedFromUrl = syncTabWithUrl();

    if (!syncedFromUrl) {
        const initialActiveTab = $('.vulnity-tab.active').data('tab') || 'dashboard';
        setActiveTab(initialActiveTab, false);
    }

    const syncedMitigationSection = syncMitigationSectionWithUrl();
    if (!syncedMitigationSection && ($('.vulnity-tab.active').data('tab') === 'mitigation' || $('.vulnity-mitigation-tab').length)) {
        setActiveMitigationSection(getActiveMitigationSection(), false);
    }

    function normalizeSettingsVisualLanguage(language) {
        const raw = String(language || '').trim().toLowerCase();
        return settingsVisualLanguageText[raw] ? raw : 'en';
    }

    function formatVisualMessage(template, replacements) {
        const safeTemplate = String(template || '');
        const vars = replacements || {};

        return safeTemplate.replace(/\{([a-z0-9_]+)\}/gi, function(match, key) {
            return Object.prototype.hasOwnProperty.call(vars, key) ? String(vars[key]) : match;
        });
    }

    function getVisualMessage(key, replacements) {
        const safeKey = String(key || '');
        const catalog = visualMessageText[currentVisualLanguage] || visualMessageText.en;
        const fallbackCatalog = visualMessageText.en || {};
        const template = Object.prototype.hasOwnProperty.call(catalog, safeKey)
            ? catalog[safeKey]
            : fallbackCatalog[safeKey] || safeKey;

        return formatVisualMessage(template, replacements);
    }

    function translateSettingKeys(language) {
        const $settingsTab = $('#tab-settings');
        if (!$settingsTab.length) {
            return;
        }

        const selectedLanguage = normalizeSettingsVisualLanguage(language);
        const languageText = settingsVisualLanguageText[selectedLanguage];

        $settingsTab.find('[data-vulnity-i18n]').each(function() {
            const key = $(this).attr('data-vulnity-i18n');
            if (key && Object.prototype.hasOwnProperty.call(languageText, key)) {
                $(this).text(languageText[key]);
            }
        });

        $settingsTab.attr('data-vulnity-language', selectedLanguage);
        $('#vulnity-language-current').text(selectedLanguage === 'es' ? languageText['settings.lang_es'] : languageText['settings.lang_en']);

        $settingsTab.find('.vulnity-language-option')
            .removeClass('is-active')
            .attr('aria-pressed', 'false');

        $settingsTab.find('.vulnity-language-option[data-vulnity-lang="' + selectedLanguage + '"]')
            .addClass('is-active')
            .attr('aria-pressed', 'true');
    }

    function translateTextValue(rawValue, targetLanguage) {
        const sourceText = String(rawValue || '');
        const trimmed = sourceText.trim();

        if (!trimmed) {
            return sourceText;
        }

        const dictionary = targetLanguage === 'es' ? globalVisualTextEnToEs : globalVisualTextEsToEn;
        let translated = Object.prototype.hasOwnProperty.call(dictionary, trimmed) ? dictionary[trimmed] : trimmed;

        if (targetLanguage === 'es') {
            translated = translated.replace(/^Retry All Failed \((\d+)\)$/i, 'Reintentar todas las fallidas ($1)');
            translated = translated.replace(/^(\d+) updates available$/i, '$1 actualizaciones disponibles');
            translated = translated.replace(/^(\d+) users$/i, '$1 usuarios');
            translated = translated.replace(/^(\d+) alerts$/i, '$1 alertas');
            translated = translated.replace(/^(\d+(?:[.,]\d+)?)%\s+delivered$/i, '$1% entregado');
            translated = translated.replace(/^(\d+)\s+alerts failed to send\.$/i, '$1 alertas no pudieron enviarse.');
            translated = translated.replace(/^(\d+)\s+Active$/i, '$1 activos');
            translated = translated.replace(/^(\d+)\s*\/\s*(\d+) active$/i, '$1 / $2 activos');
            translated = translated.replace(/^(\d+) installed$/i, '$1 instalados');
            translated = translated.replace(/^(\d+)\/(\d+) enabled$/i, '$1/$2 habilitadas');
            translated = translated.replace(/^Sent at:\s*(.+)$/i, 'Enviado el: $1');
            translated = translated.replace(/^Connection failed:\s*(.+)$/i, 'Fallo de conexion: $1');
            translated = translated.replace(/^Synchronization failed:\s*(.+)$/i, 'Sincronizacion fallida: $1');
            translated = translated.replace(/^Pairing failed:\s*(.+)$/i, 'Vinculacion fallida: $1');
            translated = translated.replace(/^In (.+)$/i, 'En $1');
            translated = translated.replace(/^(.+) ago$/i, 'Hace $1');
            translated = translated.replace(/\(in ([^)]+)\)/i, '(en $1)');
            translated = translated.replace(/^Never$/i, 'Nunca');
            translated = translated.replace(/^Not scheduled$/i, 'No programada');
            translated = translated.replace(/\bseconds?\b/gi, 'segundos');
            translated = translated.replace(/\bminutes?\b/gi, 'minutos');
            translated = translated.replace(/\bhours?\b/gi, 'horas');
            translated = translated.replace(/\bdays?\b/gi, 'dias');
            translated = translated.replace(/\bweeks?\b/gi, 'semanas');
            translated = translated.replace(/\bmonths?\b/gi, 'meses');
            translated = translated.replace(/\byears?\b/gi, 'anos');
        } else {
            translated = translated.replace(/^Reintentar todas las fallidas \((\d+)\)$/i, 'Retry All Failed ($1)');
            translated = translated.replace(/^(\d+) actualizaciones disponibles$/i, '$1 updates available');
            translated = translated.replace(/^(\d+) usuarios$/i, '$1 users');
            translated = translated.replace(/^(\d+) alertas$/i, '$1 alerts');
            translated = translated.replace(/^(\d+(?:[.,]\d+)?)%\s+entregado$/i, '$1% delivered');
            translated = translated.replace(/^(\d+)\s+alertas no pudieron enviarse\.$/i, '$1 alerts failed to send.');
            translated = translated.replace(/^(\d+)\s+activos$/i, '$1 Active');
            translated = translated.replace(/^(\d+)\s*\/\s*(\d+) activos$/i, '$1 / $2 active');
            translated = translated.replace(/^(\d+) instalados$/i, '$1 installed');
            translated = translated.replace(/^(\d+)\/(\d+) habilitadas$/i, '$1/$2 enabled');
            translated = translated.replace(/^Enviado el:\s*(.+)$/i, 'Sent at: $1');
            translated = translated.replace(/^Fallo de conexion:\s*(.+)$/i, 'Connection failed: $1');
            translated = translated.replace(/^Sincronizacion fallida:\s*(.+)$/i, 'Synchronization failed: $1');
            translated = translated.replace(/^Vinculacion fallida:\s*(.+)$/i, 'Pairing failed: $1');
            translated = translated.replace(/^En (.+)$/i, 'In $1');
            translated = translated.replace(/^Hace (.+)$/i, '$1 ago');
            translated = translated.replace(/\(en ([^)]+)\)/i, '(in $1)');
            translated = translated.replace(/^Nunca$/i, 'Never');
            translated = translated.replace(/^No programada$/i, 'Not scheduled');
            translated = translated.replace(/\bsegundos?\b/gi, 'seconds');
            translated = translated.replace(/\bminutos?\b/gi, 'minutes');
            translated = translated.replace(/\bhoras?\b/gi, 'hours');
            translated = translated.replace(/\bdias?\b/gi, 'days');
            translated = translated.replace(/\bsemanas?\b/gi, 'weeks');
            translated = translated.replace(/\bmes(es)?\b/gi, 'months');
            translated = translated.replace(/\banos?\b/gi, 'years');
        }

        const leading = sourceText.match(/^\s*/);
        const trailing = sourceText.match(/\s*$/);
        return (leading ? leading[0] : '') + translated + (trailing ? trailing[0] : '');
    }

    function localizeVisualText(value) {
        if (typeof value !== 'string') {
            return value;
        }

        return translateTextValue(value, currentVisualLanguage);
    }

    function getAttributeSourceValue(element, attribute, currentValue) {
        const sourceMap = attributeSourceCache.get(element) || {};
        if (!Object.prototype.hasOwnProperty.call(sourceMap, attribute)) {
            sourceMap[attribute] = String(currentValue || '');
            attributeSourceCache.set(element, sourceMap);
        }
        return sourceMap[attribute];
    }

    function translateTextNodesInElement(element, targetLanguage) {
        if (!element) {
            return;
        }

        const blockedTags = { SCRIPT: true, STYLE: true, PRE: true, CODE: true, TEXTAREA: true };
        if (typeof NodeFilter === 'undefined' || blockedTags[element.tagName]) {
            return;
        }

        const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null);
        const nodes = [];
        let node = walker.nextNode();

        while (node) {
            nodes.push(node);
            node = walker.nextNode();
        }

        nodes.forEach(function(textNode) {
            const parent = textNode.parentElement;
            if (!parent || blockedTags[parent.tagName]) {
                return;
            }

            if (!textNodeSourceCache.has(textNode)) {
                textNodeSourceCache.set(textNode, textNode.nodeValue);
            }

            const sourceValue = textNodeSourceCache.get(textNode);
            const translatedValue = translateTextValue(sourceValue, targetLanguage);
            if (translatedValue !== textNode.nodeValue) {
                textNode.nodeValue = translatedValue;
            }
        });

        ['placeholder', 'title', 'aria-label', 'value'].forEach(function(attribute) {
            element.querySelectorAll('[' + attribute + ']').forEach(function(target) {
                const currentValue = target.getAttribute(attribute);
                if (!currentValue) {
                    return;
                }

                if (attribute === 'value') {
                    const targetTag = (target.tagName || '').toUpperCase();
                    const inputType = String(target.getAttribute('type') || '').toLowerCase();
                    const isButtonValue = targetTag === 'INPUT' && (inputType === 'button' || inputType === 'submit' || inputType === 'reset');
                    if (!isButtonValue) {
                        return;
                    }
                }

                const sourceValue = getAttributeSourceValue(target, attribute, currentValue);
                const translatedValue = translateTextValue(sourceValue, targetLanguage).trim();
                if (translatedValue && translatedValue !== target.getAttribute(attribute)) {
                    target.setAttribute(attribute, translatedValue);
                }
            });
        });
    }

    function translateAddedNode(node, targetLanguage) {
        if (!node) {
            return;
        }

        if (node.nodeType === 1) {
            translateTextNodesInElement(node, targetLanguage);
        } else if (node.nodeType === 3 && node.parentElement) {
            translateTextNodesInElement(node.parentElement, targetLanguage);
        }
    }

    function ensureVisualLanguageObserver() {
        if (visualLanguageObserver || typeof MutationObserver === 'undefined' || !document.body) {
            return;
        }

        visualLanguageObserver = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if (!mutation.addedNodes || mutation.addedNodes.length === 0) {
                    return;
                }

                mutation.addedNodes.forEach(function(addedNode) {
                    translateAddedNode(addedNode, currentVisualLanguage);
                });
            });
        });

        visualLanguageObserver.observe(document.body, { childList: true, subtree: true });
    }

    function applyGlobalVisualLanguage(language) {
        const selectedLanguage = normalizeSettingsVisualLanguage(language);
        currentVisualLanguage = selectedLanguage;
        document.documentElement.setAttribute('data-vulnity-language', selectedLanguage);

        const roots = document.querySelectorAll('.vulnity-tabs, .vulnity-tab-content, .vulnity-mitigation-wrap, .vulnity-setup-wrap, .vulnity-server-config, .vulnity-visual-text');
        roots.forEach(function(root) {
            translateTextNodesInElement(root, selectedLanguage);
        });

        translateSettingKeys(selectedLanguage);
    }

    function initializeVisualLanguage() {
        let initialLanguage = 'en';
        try {
            initialLanguage = normalizeSettingsVisualLanguage(window.localStorage.getItem(settingsVisualLanguageStorageKey));
        } catch (error) {
            initialLanguage = 'en';
        }

        applyGlobalVisualLanguage(initialLanguage);
        ensureVisualLanguageObserver();

        $(document).on('click', '#tab-settings .vulnity-language-option', function(e) {
            e.preventDefault();
            const selectedLanguage = normalizeSettingsVisualLanguage($(this).attr('data-vulnity-lang'));
            applyGlobalVisualLanguage(selectedLanguage);

            try {
                window.localStorage.setItem(settingsVisualLanguageStorageKey, selectedLanguage);
            } catch (error) {
                // Ignore storage errors in private mode or restricted environments
            }
        });
    }

    window.vulnityTranslateMessage = getVisualMessage;
    window.vulnityApplyVisualLanguage = applyGlobalVisualLanguage;
    window.vulnityGetVisualLanguage = function() {
        return currentVisualLanguage;
    };

    initializeVisualLanguage();
    
    $('#vulnity-form').on('submit', function(e) {
        e.preventDefault();
        
        const $btn = $(this).find('button[type="submit"]');
        const siteId = $(this).find('input[name="site_id"]').val().trim();
        const pairCode = $(this).find('input[name="pair_code"]').val().trim();
        const publicIpWhitelist = $(this).find('input[name="public_ip_whitelist"]').val().trim();
        const nonceField = $(this).find('input[name="nonce"]').val();
        const ajaxNonce = nonceField || (typeof vulnity_ajax !== 'undefined' ? vulnity_ajax.nonce : '');
        
        if (!siteId || !pairCode) {
            showMessage('error', getVisualMessage('required_pairing'));
            return;
        }
        
        if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(siteId)) {
            showMessage('error', getVisualMessage('invalid_site_id'));
            return;
        }
        
        if (!/^.{8}$/.test(pairCode)) {
            showMessage('error', getVisualMessage('invalid_pair_code'));
            return;
        }
        
        $btn.prop('disabled', true).text(getVisualMessage('connecting'));
        
        $.post(vulnity_ajax.ajaxurl, {
            action: 'vulnity_pair',
            nonce: ajaxNonce,
            site_id: siteId,
            pair_code: pairCode,
            public_ip_whitelist: publicIpWhitelist
        })
        .done(function(response) {
            if (response.success) {
                showMessage('success', response.data);
                setTimeout(() => location.reload(), 2000);
            } else {
                let errorMessage = response.data;
                if (typeof errorMessage === 'string' && errorMessage.toLowerCase().indexOf('invalid public ip format') !== -1) {
                    errorMessage = getVisualMessage('invalid_public_ip');
                }

                showMessage('error', errorMessage);
            }
        })
        .fail(function() {
            showMessage('error', getVisualMessage('connection_failed'));
        })
        .always(function() {
            $btn.prop('disabled', false).text(getVisualMessage('connect_plugin'));
        });
    });
    
    function showMessage(type, text) {
        const className = type === 'success' ? 'notice-success' : 'notice-error';
        const $notice = $('<div>').addClass('notice ' + className + ' is-dismissible').append($('<p>').text(localizeVisualText(text)));

        $('#vulnity-messages').empty().append($notice);
        $('html, body').animate({scrollTop: 0}, 300);

        if (type === 'success') {
            setTimeout(() => $('.notice').fadeOut(), 5000);
        }
    }

    $('.vulnity-mitigation-tab').on('click', function(e) {
        e.preventDefault();

        const tab = $(this).attr('data-tab');
        setActiveMitigationSection(tab, true);
    });

    const $manualBlockForm = $('#manual-block-form');
    if ($manualBlockForm.length) {
        $manualBlockForm.on('submit', function(e) {
            e.preventDefault();

            const $ipField = $('#block-ip');
            const ip = $ipField.val();
            const reason = $('#block-reason').val() || getVisualMessage('manual_block');
            const duration = $('#block-duration').val();
            const nonceField = $(this).find('input[name="nonce"]').val();
            const ajaxNonce = nonceField || (typeof vulnity_ajax !== 'undefined' ? vulnity_ajax.nonce : '');

            const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;

            if (!ip) {
                alert(getVisualMessage('enter_ip'));
                return;
            }

            if (!ipRegex.test(ip)) {
                alert(getVisualMessage('invalid_ip'));
                return;
            }

            const durationLabel = $('#block-duration option:selected').text();

            if (confirm(getVisualMessage('confirm_block', { ip: ip, duration: durationLabel }))) {
                $.post(vulnity_ajax.ajaxurl, {
                    action: 'vulnity_block_ip',
                    nonce: ajaxNonce,
                    ip: ip,
                    reason: reason,
                    duration: duration
                }, function(response) {
                    if (response.success) {
                        $ipField.val('');
                        $('#block-reason').val('');
                        $('#block-duration').val('1h');

                        alert(getVisualMessage('ip_blocked_success'));
                        location.reload();
                    } else {
                        alert(getVisualMessage('error_prefix') + localizeVisualText(response.data));
                    }
                });
            }
        });
    }

    const $bulkUnblockBtn = $('#bulk-unblock-btn');
    if ($bulkUnblockBtn.length) {
        const $selectionCount = $('#bulk-selection-count');
        const formatSelectionText = (count) => (
            count === 1
                ? getVisualMessage('one_ip_selected')
                : getVisualMessage('many_ips_selected', { count: count })
        );

        const updateBulkSelection = () => {
            const selectedCount = $('.blocked-select:checked').length;
            const total = $('.blocked-select').length;

            $selectionCount.text(formatSelectionText(selectedCount));
            $bulkUnblockBtn.prop('disabled', selectedCount === 0);

            if (total > 0) {
                $('#blocked-select-all').prop('checked', selectedCount === total);
            }
        };

        $('#blocked-select-all').on('change', function() {
            const checked = $(this).is(':checked');
            $('.blocked-select').prop('checked', checked);
            updateBulkSelection();
        });

        $(document).on('change', '.blocked-select', function() {
            updateBulkSelection();
        });

        $bulkUnblockBtn.on('click', function() {
            const selectedIps = $('.blocked-select:checked').map(function() {
                return $(this).val();
            }).get();

            if (!selectedIps.length) {
                return;
            }

            if (!confirm(getVisualMessage('confirm_bulk_unblock', { count: selectedIps.length }))) {
                return;
            }

            const originalText = $bulkUnblockBtn.text();
            $bulkUnblockBtn.prop('disabled', true).text(getVisualMessage('unblocking'));

            $.post(vulnity_ajax.ajaxurl, {
                action: 'vulnity_bulk_unblock_ips',
                nonce: vulnity_ajax.nonce,
                ips: selectedIps
            }, function(response) {
                if (response.success) {
                    const data = response.data || {};
                    const message = localizeVisualText(data.message || getVisualMessage('unblocked_success', { count: selectedIps.length }));
                    const $successMsg = $('<div>').addClass('notice notice-success is-dismissible').append($('<p>').text(message));
                    $('.vulnity-mitigation-wrap').prepend($successMsg);

                    setTimeout(function() {
                        location.reload();
                    }, 1500);
                } else {
                    alert(getVisualMessage('error_prefix') + localizeVisualText(response.data));
                    $bulkUnblockBtn.prop('disabled', false).text(originalText);
                }
            }).fail(function() {
                alert(getVisualMessage('request_failed'));
                $bulkUnblockBtn.prop('disabled', false).text(originalText);
            });
        });

        updateBulkSelection();
    }
});

function vulnityMsg(key, fallback, replacements) {
    if (window.vulnityTranslateMessage && typeof window.vulnityTranslateMessage === 'function') {
        return window.vulnityTranslateMessage(key, replacements || {});
    }

    let output = String(fallback || '');
    const vars = replacements || {};
    Object.keys(vars).forEach(function(name) {
        output = output.replace(new RegExp('\\{' + name + '\\}', 'g'), String(vars[name]));
    });
    return output;
}

function vulnityMarkViewed(alertId) {
    jQuery.post(vulnity_ajax.ajaxurl, {
        action: 'vulnity_mark_viewed',
        nonce: vulnity_ajax.nonce,
        alert_id: alertId
    }, function(response) {
        if (response.success) {
            location.reload();
        }
    });
}

function vulnityMarkAllViewed() {
    jQuery.post(vulnity_ajax.ajaxurl, {
        action: 'vulnity_mark_viewed',
        nonce: vulnity_ajax.nonce,
        mark_all: true
    }, function(response) {
        if (response.success) {
            location.reload();
        }
    });
}

function vulnityClearAlerts() {
    if (!confirm(vulnityMsg('clear_alerts_confirm', 'Are you sure you want to clear all alerts?'))) {
        return;
    }
    
    jQuery.post(vulnity_ajax.ajaxurl, {
        action: 'vulnity_clear_alerts',
        nonce: vulnity_ajax.nonce
    }, function(response) {
        if (response.success) {
            location.reload();
        }
    });
}

function vulnityTestConnection() {
    const $result = jQuery('#connection-test-result');
    $result.html(vulnityMsg('testing', 'Testing...'));
    
    jQuery.post(vulnity_ajax.ajaxurl, {
        action: 'vulnity_test_connection',
        nonce: vulnity_ajax.nonce
    }, function(response) {
        if (response.success) {
            $result.empty().append($('<span>').css('color', 'green').text(vulnityMsg('sent', 'Sent') + ': ' + localizeVisualText(response.data)));
        } else {
            $result.empty().append($('<span>').css('color', 'red').text(vulnityMsg('failed', 'Failed') + ': ' + localizeVisualText(response.data)));
        }

        setTimeout(() => $result.fadeOut(), 5000);
    });
}

function vulnityUnpair() {
    if (!confirm(vulnityMsg('unpair_confirm', 'Are you sure you want to disconnect from Vulnity SIEM?'))) {
        return;
    }
    
    jQuery.post(vulnity_ajax.ajaxurl, {
        action: 'vulnity_unpair',
        nonce: vulnity_ajax.nonce
    }, function(response) {
        if (response.success) {
            location.reload();
        }
    });
}

function vulnitySyncInventory() {
    const $btn = jQuery('#sync-inventory-btn');
    const $result = jQuery('#sync-result');
    
    $btn.prop('disabled', true).text(vulnityMsg('synchronizing', 'Synchronizing...'));
    $result.html('');
    
    jQuery.post(vulnity_ajax.ajaxurl, {
        action: 'vulnity_sync_inventory',
        nonce: vulnity_ajax.nonce
    }, function(response) {
        if (response.success) {
            $result.empty().append($('<span>').css('color', 'green').text(vulnityMsg('sent', 'Sent') + ': ' + localizeVisualText(response.data)));
            setTimeout(() => location.reload(), 2000);
        } else {
            $result.empty().append($('<span>').css('color', 'red').text(vulnityMsg('failed', 'Failed') + ': ' + localizeVisualText(response.data)));
        }
    }).always(function() {
        $btn.prop('disabled', false).text(vulnityMsg('sync_now', 'Sync Now'));
    });
}

function vulnityRetryAlert(alertId) {
    const $btn = jQuery(`#retry-${alertId}`);
    $btn.prop('disabled', true).text(vulnityMsg('retrying', 'Retrying...'));
    
    jQuery.post(vulnity_ajax.ajaxurl, {
        action: 'vulnity_retry_alert',
        nonce: vulnity_ajax.nonce,
        alert_id: alertId
    }, function(response) {
        if (response.success) {
            $btn.text(vulnityMsg('sent', 'Sent')).addClass('button-primary');
            setTimeout(() => location.reload(), 1000);
        } else {
            $btn.text(vulnityMsg('failed', 'Failed')).removeClass('button-primary').addClass('button-secondary');
            alert(vulnityMsg('failed_to_send', 'Failed to send alert: {error}', { error: localizeVisualText(response.data) }));
        }
    }).always(function() {
        $btn.prop('disabled', false);
    });
}

function vulnityRetryAllFailedAlerts() {
    if (!confirm(vulnityMsg('retry_all_confirm', 'Retry sending all failed alerts to SIEM?'))) {
        return;
    }

    const $btn = jQuery('#retry-all-failed-btn');
    $btn.prop('disabled', true).text(vulnityMsg('processing', 'Processing...'));

    jQuery.post(vulnity_ajax.ajaxurl, {
        action: 'vulnity_retry_all_failed',
        nonce: vulnity_ajax.nonce
    }, function(response) {
        if (response.success) {
            alert(localizeVisualText(response.data));
            location.reload();
        } else {
            alert(vulnityMsg('error_prefix', 'Error: ') + localizeVisualText(response.data));
        }
    }).always(function() {
        $btn.prop('disabled', false).text(vulnityMsg('retry_all_failed', 'Retry All Failed'));
    });
}

function vulnityUnblockIP(ip) {
    if (!confirm(vulnityMsg('unblock_ip_confirm', 'Are you sure you want to unblock {ip}? This IP will be able to access the site immediately.', { ip: ip }))) {
        return;
    }

    jQuery.post(vulnity_ajax.ajaxurl, {
        action: 'vulnity_unblock_ip',
        nonce: vulnity_ajax.nonce,
        ip: ip
    }, function(response) {
        if (response.success) {
            const successMsg = jQuery('<div class="notice notice-success is-dismissible"><p>' + vulnityMsg('unblock_ip_success_notice', 'IP {ip} has been unblocked successfully.', { ip: ip }) + '</p></div>');
            jQuery('.vulnity-mitigation-wrap').prepend(successMsg);

            setTimeout(function() {
                location.reload();
            }, 1500);
        } else {
            alert(vulnityMsg('error_prefix', 'Error: ') + localizeVisualText(response.data));
        }
    });
}

function vulnitySyncMitigationConfig() {
    const $btn = jQuery('#sync-mitigation-btn');
    const $result = jQuery('#sync-mitigation-result');
    const originalText = $btn.text();

    $btn.prop('disabled', true).text(vulnityMsg('syncing_wait', 'Syncing... Please wait'));
    $result.removeClass('is-success is-error').addClass('is-working').text(vulnityMsg('connecting_siem', 'Connecting to SIEM...'));

    jQuery.post(vulnity_ajax.ajaxurl, {
        action: 'vulnity_sync_mitigation',
        nonce: vulnity_ajax.nonce
    }, function(response) {
        if (response.success) {
            const successMessage = (typeof response.data === 'string' && response.data.length > 0)
                ? localizeVisualText(response.data)
                : vulnityMsg('mitigation_synced', 'Mitigation configuration synchronized.');
            $result.removeClass('is-working is-error').addClass('is-success').text(successMessage);

            const successMsg = jQuery('<div class="notice notice-success is-dismissible"><p><strong>' + vulnityMsg('success_title', 'Success!') + '</strong> ' + vulnityMsg('mitigation_sync_success_notice', 'Mitigation configuration has been synchronized.') + '</p></div>');
            jQuery('.vulnity-mitigation-wrap').prepend(successMsg);

            setTimeout(function() {
                location.reload();
            }, 1500);
        } else {
            const errorMessage = (typeof response.data === 'string' && response.data.length > 0)
                ? localizeVisualText(response.data)
                : vulnityMsg('mitigation_sync_unable', 'Unable to synchronize mitigation configuration.');
            $result.removeClass('is-working is-success').addClass('is-error').text(vulnityMsg('error_prefix', 'Error: ') + errorMessage);
            $btn.prop('disabled', false).text(originalText);

            const errorMsg = jQuery('<div class="notice notice-error is-dismissible"><p><strong>' + vulnityMsg('sync_failed_title', 'Sync Failed:') + '</strong> ' + errorMessage + '</p></div>');
            jQuery('.vulnity-mitigation-wrap').prepend(errorMsg);
        }
    }).fail(function() {
        $result.removeClass('is-working is-success').addClass('is-error').text(vulnityMsg('connection_failed_retry', 'Connection failed. Please try again.'));
        $btn.prop('disabled', false).text(originalText);
    });
}

function vulnitySyncMitigation() {
    vulnitySyncMitigationConfig();
}
