<?php
/**
 * Configuración del sistema WhatsApp Bot Manager
 * Desarrollado por U-Site.app
 * Fecha: 4 de enero de 2026 - Sistema de autenticación unificado
 */

// ── Cargar .env ANTES de definir cualquier constante ──────────────────────────
// Si no se hace aquí, getenv() devuelve vacío y los define() usan el fallback
// 'mysql' (nombre del contenedor Docker) en lugar de la IP real de la BD.
if (!function_exists('loadEnvFile')) {
function loadEnvFile($path) {
    if (!file_exists($path)) {
        return false;
    }
    $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($lines as $line) {
        if (strpos($line, '#') === 0 || strpos($line, '=') === false) {
            continue;
        }
        [$name, $value] = explode('=', $line, 2);
        $name = trim($name);
        $value = trim($value, " \t\n\r\0\x0B\"'");
        if (!array_key_exists($name, $_ENV)) {
            $_ENV[$name] = $value;
            putenv("$name=$value");
        }
    }
    return true;
}
}
loadEnvFile(__DIR__ . '/../.env');

// Configuración de la base de datos - 100% desde variables de entorno (o .env)
define('DB_HOST', getenv('DB_HOST') ?: 'mysql');
define('DB_PORT', getenv('DB_PORT') ?: '3306');
define('DB_NAME', getenv('DB_NAME') ?: 'usite_whatsapp_bot');
define('DB_USER', getenv('DB_USER') ?: 'usite_whatsapp_user');
define('DB_PASS', getenv('DB_PASS') ?: '');
define('DB_CHARSET', 'utf8mb4');

// Configuración de autenticación (LEGACY - migrado a BD)
// NOTA: Esta constante se mantiene para retrocompatibilidad durante la migración
// El sistema ahora usa autenticación basada en base de datos (admin_users table)
define('ADMIN_PASSWORD', '$2y$10$IXCY8Sm1xFkfhC6Y67Ahn.QLHxE.sjWfmTEOKFZdCN2a9s9YLVuGW'); // password123

// ── Módulos del sistema ──────────────────────────────────────────────────────
// Catálogo de todos los módulos disponibles para asignar a los roles.
// Clave: slug interno · Valor: etiqueta legible para la UI.
define('SYSTEM_MODULES', [
    // ── Bot ──────────────────────────────────────────────────────────────────
    'whatsapp'          => 'WhatsApp Bot',
    // ── Laboratorio (Oleada 0) ───────────────────────────────────────────────
    'lab_dashboard'     => 'Dashboard Lab',
    'lab_ordenes'       => 'Órdenes Médicas',
    'lab_pacientes'     => 'Pacientes',
    'lab_domicilios'    => 'Domicilios',
    'lab_enfermeras'    => 'Enfermeras',
    'lab_formularios'   => 'Formularios',
    'lab_reportes'      => 'Reportes',
    'lab_configuracion' => 'Configuración Lab',
    // ── Sistema ──────────────────────────────────────────────────────────────
    'usuarios'          => 'Gestión de Usuarios',
    'enfermero_portal'  => 'Portal Enfermero',
    'soporte'           => 'Soporte y Documentación',
    // ── Oleada 1 — Turnero ───────────────────────────────────────────────────
    'turnero'           => 'Turnero',
    // ── Oleada 2 — pendiente ─────────────────────────────────────────────────
    'registro_exams'    => 'Registro de Exámenes',
    'facturacion'       => 'Facturación',
    'inventario'        => 'Inventario',
    'citas'             => 'Citas',
    'resultados'        => 'Resultados',
]);

// loadEnvFile ya fue definida y ejecutada al inicio del archivo.

// Cargar autoloader de Composer si existe (solo una vez)
$composerAutoload = __DIR__ . '/../vendor/autoload.php';
if (file_exists($composerAutoload)) {
    // Verificar que no se haya cargado ya usando una clase del autoloader
    if (!class_exists('Predis\Client', false)) {
        require_once $composerAutoload;
    }
}

// Función helper para obtener variables de entorno
if (!function_exists('env')) {
function env($key, $default = null) {
    return $_ENV[$key] ?? getenv($key) ?: $default;
}
} 

// Incluir clase Database
require_once __DIR__ . '/../classes/Database.php';

// Función para verificar si la instalación está completada
if (!function_exists('isInstallationCompleted')) {
function isInstallationCompleted() {
    return file_exists(dirname(__DIR__) . '/.installation_completed');
}
} 

// Función para obtener configuración desde base de datos
if (!function_exists('getConfigFromDB')) {
function getConfigFromDB($key, $default = null) {
    static $configs = null;
    global $config_cache_cleared;

    // Asegurar que la bandera exista y tenga valor booleano
    if (!isset($config_cache_cleared)) {
        $config_cache_cleared = false;
    }
    
    if ($configs === null || $config_cache_cleared) {
        $config_cache_cleared = false;
        $configs = [];
        
        if (isInstallationCompleted()) {
            try {
                // Usar la función centralizada para crear la conexión
                $pdo = createDbConnection();
                
                // Verificar si existe la tabla system_config
                $tableExistsStmt = $pdo->query("SHOW TABLES LIKE 'system_config'");
                $tableExists = $tableExistsStmt && $tableExistsStmt->rowCount() > 0;
                
                if ($tableExists) {
                    // Cargar todas las configuraciones en caché de una sola vez
                    $stmt = $pdo->query("SELECT config_key, config_value FROM system_config");
                    while ($row = $stmt->fetch()) {
                        $configs[$row['config_key']] = $row['config_value'];
                    }
                } else {
                    // Si no existe la tabla, usar valores por defecto
                    $configs = [];
                }
            } catch (PDOException $e) {
                error_log("Error loading config: " . $e->getMessage());
                $configs = [];
            }
        } else {
            $configs = [];
        }
    }
    
    return $configs[$key] ?? $default;
}
} // end if !function_exists('getConfigFromDB')

/**
 * Guarda una configuración en la base de datos
 * @param string $key Clave de configuración
 * @param mixed $value Valor de configuración
 * @param string $description Descripción opcional
 * @return bool True si se guardó correctamente
 */
function saveConfigToDB($key, $value, $description = null) {
    try {
        if (!isInstallationCompleted()) {
            return false;
        }
        
        $pdo = createDbConnection();
        
        // Crear tabla si no existe
        createSystemConfigTable($pdo);
        
        // Verificar si ya existe la configuración
        $stmt = $pdo->prepare("SELECT id FROM system_config WHERE config_key = ?");
        $stmt->execute([$key]);
        $exists = $stmt->fetch();
        
        if ($exists) {
            // Actualizar configuración existente
            $sql = "UPDATE system_config SET config_value = ?, updated_at = NOW()";
            $params = [$value];
            
            if ($description !== null) {
                $sql .= ", description = ?";
                $params[] = $description;
            }
            
            $sql .= " WHERE config_key = ?";
            $params[] = $key;
            
            $stmt = $pdo->prepare($sql);
            $result = $stmt->execute($params);
        } else {
            // Insertar nueva configuración
            $stmt = $pdo->prepare("
                INSERT INTO system_config (config_key, config_value, description, created_at, updated_at) 
                VALUES (?, ?, ?, NOW(), NOW())
            ");
            $result = $stmt->execute([$key, $value, $description]);
        }
        
        // Limpiar cache estático
        if ($result) {
            clearConfigCache();
        }
        
        return $result;
        
    } catch (PDOException $e) {
        error_log("Error guardando configuración '$key': " . $e->getMessage());
        return false;
    }
}

/**
 * Guarda múltiples configuraciones en una sola transacción
 * @param array $configs Array asociativo [key => value]
 * @return bool True si todas se guardaron correctamente
 */
function saveMultipleConfigsToDB($configs) {
    try {
        if (!isInstallationCompleted() || empty($configs)) {
            return false;
        }
        
        $pdo = createDbConnection();
        $pdo->beginTransaction();
        
        // Crear tabla si no existe
        createSystemConfigTable($pdo);
        
        $success = true;
        foreach ($configs as $key => $value) {
            // Verificar si ya existe
            $stmt = $pdo->prepare("SELECT id FROM system_config WHERE config_key = ?");
            $stmt->execute([$key]);
            $exists = $stmt->fetch();
            
            if ($exists) {
                // Actualizar
                $stmt = $pdo->prepare("UPDATE system_config SET config_value = ?, updated_at = NOW() WHERE config_key = ?");
                $result = $stmt->execute([$value, $key]);
            } else {
                // Insertar
                $stmt = $pdo->prepare("
                    INSERT INTO system_config (config_key, config_value, created_at, updated_at) 
                    VALUES (?, ?, NOW(), NOW())
                ");
                $result = $stmt->execute([$key, $value]);
            }
            
            if (!$result) {
                $success = false;
                break;
            }
        }
        
        if ($success) {
            $pdo->commit();
            clearConfigCache();
            return true;
        } else {
            $pdo->rollback();
            return false;
        }
        
    } catch (PDOException $e) {
        if (isset($pdo) && $pdo->inTransaction()) {
            $pdo->rollback();
        }
        error_log("Error guardando configuraciones múltiples: " . $e->getMessage());
        return false;
    }
}

/**
 * Obtiene todas las configuraciones de la base de datos
 * @return array Array asociativo con todas las configuraciones
 */
function getAllConfigsFromDB() {
    try {
        if (!isInstallationCompleted()) {
            return [];
        }
        
        $pdo = createDbConnection();
        
        // Verificar si existe la tabla
        $tableExists = $pdo->query("SHOW TABLES LIKE 'system_config'")->rowCount() > 0;
        
        if (!$tableExists) {
            return [];
        }
        
        $stmt = $pdo->query("
            SELECT config_key, config_value, description, created_at, updated_at 
            FROM system_config 
            ORDER BY config_key
        ");
        
        $configs = [];
        while ($row = $stmt->fetch()) {
            $configs[$row['config_key']] = [
                'value' => $row['config_value'],
                'description' => $row['description'],
                'created_at' => $row['created_at'],
                'updated_at' => $row['updated_at']
            ];
        }
        
        return $configs;
        
    } catch (PDOException $e) {
        error_log("Error obteniendo todas las configuraciones: " . $e->getMessage());
        return [];
    }
}

/**
 * Elimina una configuración de la base de datos
 * @param string $key Clave de configuración a eliminar
 * @return bool True si se eliminó correctamente
 */
function deleteConfigFromDB($key) {
    try {
        if (!isInstallationCompleted()) {
            return false;
        }
        
        $pdo = createDbConnection();
        $stmt = $pdo->prepare("DELETE FROM system_config WHERE config_key = ?");
        $result = $stmt->execute([$key]);
        
        if ($result) {
            clearConfigCache();
        }
        
        return $result;
        
    } catch (PDOException $e) {
        error_log("Error eliminando configuración '$key': " . $e->getMessage());
        return false;
    }
}

if (!function_exists('esBsuid')) {
/**
 * ¿Este identificador es un BSUID de Meta y no un número de teléfono?
 *
 * Desde que WhatsApp permite ocultar el número, quien lo oculta llega
 * identificado solo por su BSUID, con la forma "CO.1761088155094242".
 * Ese valor ocupa el lugar del teléfono dentro del bot, así que hay que
 * distinguirlo antes de tratarlo como si fuera un número real: guardarlo
 * en un campo de teléfono deja un dato falso con toda la pinta de verdadero.
 *
 * @param  mixed $valor
 * @return bool
 */
function esBsuid($valor) {
    return (bool) preg_match('/^[A-Z]{2}\.\d+$/', (string) $valor);
}
}

/**
 * Limpia el cache estático de configuraciones
 */
function clearConfigCache() {
    // Necesitamos acceder a la variable estática de getConfigFromDB
    // Como no podemos modificar directamente variables estáticas de otra función,
    // establecemos una flag para que getConfigFromDB reinicie el cache
    global $config_cache_cleared;
    $config_cache_cleared = true;
}

/**
 * Crea la tabla system_config si no existe
 * @param PDO $pdo Conexión a la base de datos
 * @return bool True si se creó o ya existía
 */
function createSystemConfigTable($pdo) {
    try {
        $sql = "CREATE TABLE IF NOT EXISTS system_config (
            id INT AUTO_INCREMENT PRIMARY KEY,
            config_key VARCHAR(255) NOT NULL UNIQUE,
            config_value TEXT,
            description TEXT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            INDEX idx_config_key (config_key)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
        
        return $pdo->exec($sql) !== false;
        
    } catch (PDOException $e) {
        error_log("Error creando tabla system_config: " . $e->getMessage());
        return false;
    }
}

// WhatsApp Business API (prioridad: BD > .env > default)
define('WHATSAPP_TOKEN', getConfigFromDB('whatsapp_token', env('WHATSAPP_TOKEN', 'TU_TOKEN_DE_WHATSAPP_AQUI')));
define('WHATSAPP_PHONE_NUMBER_ID', getConfigFromDB('whatsapp_phone_number_id', env('WHATSAPP_PHONE_NUMBER_ID', 'TU_PHONE_ID_AQUI')));
define('WHATSAPP_API_URL', getConfigFromDB('whatsapp_api_url', env('WHATSAPP_API_URL', 'https://graph.facebook.com/v22.0/')));
define('WEBHOOK_VERIFY_TOKEN', getConfigFromDB('webhook_verify_token', env('WEBHOOK_VERIFY_TOKEN', 'mi_token_secreto_123')));

// Configuración general
define('APP_NAME', 'WhatsApp Bot System');
define('APP_VERSION', '2.0.0');
// URL auto-detectada según protocolo y host actuales
if (!defined('APP_URL')) {
    // Detectar HTTPS correctamente detrás de proxy reverso (Coolify, nginx, etc.)
    $__isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
              || ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https'
              || ($_SERVER['HTTP_X_FORWARDED_SSL']   ?? '') === 'on'
              || ($_SERVER['HTTP_FRONT_END_HTTPS']    ?? '') === 'on';
    $__proto = $__isHttps ? 'https' : 'http';
    $__host  = $_SERVER['HTTP_HOST'] ?? 'localhost';
    define('APP_URL', $__proto . '://' . $__host);
}
// BASE_URL: alias de APP_URL con barra final (usado en vistas de módulos ERP)
if (!defined('BASE_URL')) {
    define('BASE_URL', rtrim(APP_URL, '/') . '/');
}
// Token para runners de migración accesibles por web
if (!defined('MIGRATION_TOKEN')) {
    define('MIGRATION_TOKEN', 'lab2026migrate');
}
// API key server-to-server para ingesta de pacientes desde RIPS Manager
if (!defined('LAB_SYNC_KEY')) {
    define('LAB_SYNC_KEY', getenv('LAB_SYNC_KEY') ?: 'rips-lab-sync-2026');
}
// URL base del RIPS Manager (para consultas server-to-server)
if (!defined('RIPS_MANAGER_URL')) {
    define('RIPS_MANAGER_URL', getenv('RIPS_MANAGER_URL') ?: '');
}
define('TIMEZONE', 'America/Bogota');

// Información del desarrollador
define('DEVELOPER_NAME', 'U-Site.app');
define('DEVELOPER_URL', 'https://u-site.app');
define('DEVELOPER_EMAIL', 'support@u-site.app');
define('DEVELOPER_SUPPORT', 'https://u-site.app/support');

// Configuración de seguridad
define('INSTALLATION_LOCK', '.installation_completed'); // Archivo de bloqueo
define('SECRET_KEY', 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
define('ADMIN_USERNAME', 'admin'); // Usuario administrador por defecto
// ADMIN_PASSWORD movido arriba como parte del sistema unificado (línea 18)
define('SESSION_TIMEOUT', (int)(getenv('SESSION_TIMEOUT') ?: 1800)); // 30 minutos
define('MAX_LOGIN_ATTEMPTS', (int)(getenv('MAX_LOGIN_ATTEMPTS') ?: 3));
define('LOGIN_LOCKOUT_TIME', (int)(getenv('LOGIN_LOCKOUT_TIME') ?: 900)); // 15 minutos

// Configuración de logs (LOG_LEVEL sobreescribible desde env)
define('ENABLE_LOGGING', true);
define('LOG_LEVEL', getenv('LOG_LEVEL') ?: 'INFO'); // DEBUG, INFO, WARNING, ERROR
define('LOG_FILE', 'logs/system.log');

// Configuración de archivos
define('UPLOAD_MAX_SIZE', 10485760); // 10MB
define('ALLOWED_FILE_TYPES', ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx']);

// Configurar zona horaria
date_default_timezone_set(TIMEZONE);

// Configurar errores para desarrollo (se desactivará en producción)
if (!file_exists(dirname(__DIR__) . '/' . 'installation_completed')) {
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
} else {
    error_reporting(0);
    ini_set('display_errors', 0);
}

ini_set('log_errors', 1);

// Headers de seguridad
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: SAMEORIGIN');
header('X-XSS-Protection: 1; mode=block');

// Auto-detectar URL base si no está configurada
if (!defined('AUTO_DETECTED_URL')) {
    $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://';
    $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
    $path = dirname($_SERVER['SCRIPT_NAME'] ?? '');
    define('AUTO_DETECTED_URL', $protocol . $host . $path);
}


// ========================================
// SISTEMA DE AUTENTICACIÓN UNIFICADO
// ========================================

/**
 * Crea conexión a la base de datos
 * @return PDO
 */
function createDbConnection() {
    try {
        $pdo = new PDO(
            "mysql:host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET,
            DB_USER,
            DB_PASS,
            [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES => false,
            ]
        );
        return $pdo;
    } catch (PDOException $e) {
        error_log("Error de conexión BD: " . $e->getMessage());
        throw $e;
    }
}

/**
 * Autentica un usuario usando la base de datos
 * @param string $username
 * @param string $password
 * @return array|false Datos del usuario o false si falla
 */
function authenticateUser($username, $password) {
    try {
        $pdo = createDbConnection();
        
        // Primero verificar si existe la tabla admin_users
        $checkTable = $pdo->query("SHOW TABLES LIKE 'admin_users'");
        $tableExists = $checkTable->rowCount() > 0;
        
        if ($tableExists) {
            // Sistema moderno: autenticación por BD
            $stmt = $pdo->prepare("SELECT * FROM admin_users WHERE username = ? AND is_active = 1");
            $stmt->execute([$username]);
            $admin = $stmt->fetch();
            
            if ($admin && password_verify($password, $admin['password_hash'])) {
                // Actualizar último login
                $updateLogin = $pdo->prepare("UPDATE admin_users SET last_login = NOW() WHERE id = ?");
                $updateLogin->execute([$admin['id']]);

                // Cargar módulos del rol (slug y permiso)
                $modules            = [];
                $modulePermissions  = [];
                $roleSlug  = $admin['role'] ?? 'admin';
                $homePage  = null;
                if (!empty($admin['role_id'])) {
                    $modStmt = $pdo->prepare(
                        "SELECT module_slug, COALESCE(permission,'write') AS permission
                           FROM role_modules WHERE role_id = ?"
                    );
                    $modStmt->execute([$admin['role_id']]);
                    foreach ($modStmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
                        $modules[]                             = $row['module_slug'];
                        $modulePermissions[$row['module_slug']] = $row['permission'];
                    }
                    // Leer home_page del rol
                    $rpStmt = $pdo->prepare("SELECT home_page FROM roles WHERE id = ?");
                    $rpStmt->execute([$admin['role_id']]);
                    $rp = $rpStmt->fetch(PDO::FETCH_ASSOC);
                    $homePage = $rp['home_page'] ?? null;
                } elseif ($roleSlug === 'admin') {
                    // Fallback: admin sin role_id tiene todos los módulos
                    $modules = array_keys(SYSTEM_MODULES);
                } elseif ($roleSlug === 'enfermero') {
                    $modules = ['enfermero_portal', 'lab_formularios'];
                }

                return [
                    'id'                 => $admin['id'],
                    'username'           => $admin['username'],
                    'full_name'          => $admin['full_name'],
                    'email'              => $admin['email'],
                    'role'               => $roleSlug,
                    'role_id'            => $admin['role_id'] ?? null,
                    'home_page'          => $homePage,
                    'enfermera_id'       => $admin['enfermera_id'] ?? null,
                    'turnero_lugar_id'   => $admin['turnero_lugar_id'] ?? null,
                    'modules'            => $modules,
                    'module_permissions' => $modulePermissions,
                ];
            }
        } else {
            // Sistema legacy: usar constante ADMIN_PASSWORD
            if ($username === 'admin' && password_verify($password, ADMIN_PASSWORD)) {
                return [
                    'id' => 1,
                    'username' => 'admin',
                    'full_name' => 'Administrador',
                    'email' => 'admin@sistema.local'
                ];
            }
        }
    } catch (Exception $e) {
        error_log("Error en autenticación: " . $e->getMessage());
        return false;
    }
    
    return false;
}

/**
 * Verifica si el sistema está migrado a BD
 * @return bool
 */
function isAuthSystemMigrated() {
    try {
        $pdo = createDbConnection();
        $checkTable = $pdo->query("SHOW TABLES LIKE 'admin_users'");
        return $checkTable->rowCount() > 0;
    } catch (Exception $e) {
        return false;
    }
}

/**
 * Obtiene todos los administradores del sistema
 * @return array
 */
function getAdminUsers() {
    try {
        $pdo = createDbConnection();
        
        if (isAuthSystemMigrated()) {
            $stmt = $pdo->query("SELECT * FROM admin_users ORDER BY created_at DESC");
            return $stmt->fetchAll();
        } else {
            // Sistema legacy: retornar admin por defecto
            return [[
                'id' => 1,
                'username' => 'admin',
                'full_name' => 'Administrador',
                'email' => 'admin@sistema.local',
                'is_active' => 1,
                'created_at' => date('Y-m-d H:i:s'),
                'last_login' => null
            ]];
        }
    } catch (Exception $e) {
        error_log("Error obteniendo usuarios admin: " . $e->getMessage());
        return [];
    }
}

// Función para verificar login
if (!function_exists('isUserLoggedIn')) {
/**
 * Verifica si el usuario autenticado tiene el rol indicado.
 */
function userHasRole(string $role): bool {
    return isset($_SESSION['admin_user']['role']) && $_SESSION['admin_user']['role'] === $role;
}

/**
 * Verdadero si el usuario es enfermero.
 */
function isEnfermero(): bool {
    return userHasRole('enfermero');
}

/**
 * Devuelve el ID de enfermera del usuario en sesión, o null si no aplica.
 */
function enfermeraId(): ?int {
    $id = $_SESSION['admin_user']['enfermera_id'] ?? null;
    return $id ? (int)$id : null;
}

/**
 * Comprueba si el usuario en sesión tiene acceso al módulo indicado.
 * Los administradores sin modules cargados tienen acceso total (retrocompat.).
 */
function hasModule(string $slug): bool {
    $modules = $_SESSION['admin_user']['modules'] ?? null;
    // Si no hay módulos en sesión (usuario legacy) y es admin → acceso total
    if ($modules === null) {
        return userHasRole('admin');
    }
    return in_array($slug, $modules, true);
}

/**
 * Comprueba si el usuario tiene permiso de escritura (write) en el módulo.
 * Los administradores tienen acceso de escritura total.
 * Un usuario con permission='read' solo puede ver, no modificar.
 */
function hasModuleWrite(string $slug): bool {
    // Admins siempre pueden escribir
    if (userHasRole('admin')) return true;

    $permissions = $_SESSION['admin_user']['module_permissions'] ?? null;
    if ($permissions === null) {
        // Sesión legacy sin 'module_permissions': si tiene el módulo, se asume write
        return hasModule($slug);
    }
    return ($permissions[$slug] ?? '') === 'write';
}

/**
 * Detiene la ejecución si el usuario no tiene el rol requerido.
 */
function requireRole(string $role): void {
    if (!isUserLoggedIn()) {
        header('Location: login.php');
        exit;
    }
    if (!userHasRole($role)) {
        http_response_code(403);
        // Redirigir al portal correcto
        $destino = userHasRole('enfermero') ? 'enfermero_portal.php' : 'index.php';
        header("Location: $destino");
        exit;
    }
}

function isUserLoggedIn() {
    return isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true;
}
} 

// Función para verificar intentos de login
if (!function_exists('checkLoginAttempts')) {
function checkLoginAttempts($ip) {
    $attemptsFile = dirname(__DIR__) . '/.login_attempts.json';
    
    if (!file_exists($attemptsFile)) {
        return true;
    }
    
    $attempts = json_decode(file_get_contents($attemptsFile), true);
    
    if (!isset($attempts[$ip])) {
        return true;
    }
    
    $ipData = $attempts[$ip];
    
    if (time() > $ipData['locked_until']) {
        unset($attempts[$ip]);
        file_put_contents($attemptsFile, json_encode($attempts));
        return true;
    }
    
    return $ipData['attempts'] < MAX_LOGIN_ATTEMPTS;
}
} 

// Función para registrar intento de login fallido
if (!function_exists('recordFailedLogin')) {
function recordFailedLogin($ip) {
    $attemptsFile = dirname(__DIR__) . '/.login_attempts.json';
    
    $attempts = [];
    if (file_exists($attemptsFile)) {
        $attempts = json_decode(file_get_contents($attemptsFile), true);
    }
    
    if (!isset($attempts[$ip])) {
        $attempts[$ip] = ['attempts' => 0, 'locked_until' => 0];
    }
    
    $attempts[$ip]['attempts']++;
    
    if ($attempts[$ip]['attempts'] >= MAX_LOGIN_ATTEMPTS) {
        $attempts[$ip]['locked_until'] = time() + LOGIN_LOCKOUT_TIME;
    }
    
    file_put_contents($attemptsFile, json_encode($attempts));
}
}

// Función para limpiar intentos de login exitoso
if (!function_exists('clearLoginAttempts')) {
function clearLoginAttempts($ip) {
    $attemptsFile = dirname(__DIR__) . '/.login_attempts.json';
    
    if (file_exists($attemptsFile)) {
        $attempts = json_decode(file_get_contents($attemptsFile), true);
        unset($attempts[$ip]);
        file_put_contents($attemptsFile, json_encode($attempts));
    }
}
}

// Autoloader
spl_autoload_register(function ($class) {
    $directories = [
        dirname(__DIR__) . '/classes/',
        dirname(__DIR__) . '/services/',
        dirname(__DIR__) . '/controllers/'
    ];
    
    foreach ($directories as $directory) {
        $file = $directory . $class . '.php';
        if (file_exists($file)) {
            require_once $file;
            return;
        }
    }
});



/**
 * Guarda la configuración completa de WhatsApp
 * @param array $whatsappConfig Array con las configuraciones de WhatsApp
 * @return bool True si se guardó correctamente
 */
function saveWhatsAppConfigToDB($whatsappConfig) {
    $success = true;
    
    // Mapear configuraciones de WhatsApp y guardar individualmente
    if (isset($whatsappConfig['token'])) {
        $success &= saveConfigToDB('whatsapp_token', $whatsappConfig['token']);
    }
    
    if (isset($whatsappConfig['phone_number_id'])) {
        $success &= saveConfigToDB('whatsapp_phone_number_id', $whatsappConfig['phone_number_id']);
    }
    
    if (isset($whatsappConfig['business_account_id'])) {
        $success &= saveConfigToDB('whatsapp_business_account_id', $whatsappConfig['business_account_id']);
    }
    
    if (isset($whatsappConfig['api_url'])) {
        $success &= saveConfigToDB('whatsapp_api_url', $whatsappConfig['api_url']);
    }
    
    if (isset($whatsappConfig['webhook_verify_token'])) {
        $success &= saveConfigToDB('webhook_verify_token', $whatsappConfig['webhook_verify_token']);
    }
    
    if (isset($whatsappConfig['business_name'])) {
        $success &= saveConfigToDB('business_name', $whatsappConfig['business_name']);
    }
    
    if (isset($whatsappConfig['welcome_message'])) {
        $success &= saveConfigToDB('welcome_message', $whatsappConfig['welcome_message']);
    }

    if (isset($whatsappConfig['terms_message'])) {
        $success &= saveConfigToDB('terms_message', $whatsappConfig['terms_message']);
    }

    if (isset($whatsappConfig['terms_rejected_message'])) {
        $success &= saveConfigToDB('terms_rejected_message', $whatsappConfig['terms_rejected_message']);
    }
    
    return $success;
}

/**
 * Obtiene toda la configuración de WhatsApp desde BD
 * @return array Configuración completa de WhatsApp
 */
function getWhatsAppConfigFromDB() {
    // Usar constantes como fallback (ya tienen cascada BD > env > default)
    $token = getConfigFromDB('whatsapp_token', '');
    if (empty($token) && defined('WHATSAPP_TOKEN')) $token = WHATSAPP_TOKEN;
    if (empty($token)) $token = getenv('WHATSAPP_TOKEN') ?: '';

    $phoneId = getConfigFromDB('whatsapp_phone_number_id', '');
    if (empty($phoneId) && defined('WHATSAPP_PHONE_NUMBER_ID')) $phoneId = WHATSAPP_PHONE_NUMBER_ID;
    if (empty($phoneId)) $phoneId = getenv('WHATSAPP_PHONE_NUMBER_ID') ?: '';

    $apiUrl = getConfigFromDB('whatsapp_api_url', '');
    if (empty($apiUrl) && defined('WHATSAPP_API_URL')) $apiUrl = WHATSAPP_API_URL;
    if (empty($apiUrl)) $apiUrl = 'https://graph.facebook.com/v22.0/';

    $webhookToken = getConfigFromDB('webhook_verify_token', '');
    if (empty($webhookToken) && defined('WEBHOOK_VERIFY_TOKEN')) $webhookToken = WEBHOOK_VERIFY_TOKEN;

    return [
        // Mapear a nombres esperados por el código
        'token' => $token,
        'whatsapp_token' => $token,
        'phone_number_id' => $phoneId,
        'business_account_id' => getConfigFromDB('whatsapp_business_account_id', ''),
        'api_url' => $apiUrl,
        'whatsapp_api_url' => $apiUrl,
        'webhook_verify_token' => $webhookToken,
        'business_name' => getConfigFromDB('business_name', ''),
        'welcome_message' => getConfigFromDB('welcome_message', ''),
        'terms_message' => getConfigFromDB('terms_message', ''),
        'terms_rejected_message' => getConfigFromDB('terms_rejected_message', ''),
        'status' => validateWhatsAppConfigFromDB()
    ];
}

/**
 * Valida la configuración de WhatsApp desde BD
 * @return array Estado de validación
 */
function validateWhatsAppConfigFromDB() {
    $token = getConfigFromDB('whatsapp_token', '');
    $phoneId = getConfigFromDB('whatsapp_phone_number_id', '');
    $webhookToken = getConfigFromDB('webhook_verify_token', '');
    
    $errors = [];
    $warnings = [];
    
    if (empty($token) || $token === 'TU_TOKEN_DE_WHATSAPP_AQUI') {
        $errors[] = 'Token de WhatsApp no configurado';
    }
    
    if (empty($phoneId) || $phoneId === 'TU_PHONE_ID_AQUI') {
        $errors[] = 'Phone Number ID no configurado';
    } elseif ($phoneId === '858157464051987') {
        $warnings[] = 'Phone Number ID problemático detectado (858157464051987)';
    }
    
    if (empty($webhookToken)) {
        $errors[] = 'Token de verificación del webhook no configurado';
    }
    
    return [
        'valid' => empty($errors),
        'errors' => $errors,
        'warnings' => $warnings,
        'configured_fields' => [
            'token' => !empty($token) && $token !== 'TU_TOKEN_DE_WHATSAPP_AQUI',
            'phone_id' => !empty($phoneId) && $phoneId !== 'TU_PHONE_ID_AQUI',
            'webhook_token' => !empty($webhookToken)
        ]
    ];
}

/**
 * Inicializa configuraciones por defecto en la base de datos
 * @return bool True si se inicializaron correctamente
 */
function initializeDefaultConfigs() {
    $defaultConfigs = [
        // Configuración de WhatsApp
        'whatsapp_token' => env('WHATSAPP_TOKEN', 'TU_TOKEN_DE_WHATSAPP_AQUI'),
        'whatsapp_phone_number_id' => env('WHATSAPP_PHONE_NUMBER_ID', 'TU_PHONE_ID_AQUI'),
        'whatsapp_api_url' => env('WHATSAPP_API_URL', 'https://graph.facebook.com/v22.0/'),
        'webhook_verify_token' => env('WEBHOOK_VERIFY_TOKEN', 'mi_token_secreto_123'),
        
        // Configuración de la aplicación
        'app_name' => 'WhatsApp Bot System',
        'app_version' => '1.0.0',
        'timezone' => 'America/Bogota',
        'developer_name' => 'U-Site.app',
        'developer_url' => 'https://u-site.app',
        'developer_email' => 'support@u-site.app',
        
        // Configuración de logs y archivos
        'enable_logging' => '1',
        'log_level' => 'INFO',
        'upload_max_size' => '10485760',
        'allowed_file_types' => 'jpg,jpeg,png,gif,pdf,doc,docx',
        
        // Configuración de seguridad
        'session_timeout' => '1800',
        'max_login_attempts' => '3',
        'login_lockout_time' => '900'
    ];
    
    return saveMultipleConfigsToDB($defaultConfigs);
}

/**
 * Migra configuraciones desde constantes/archivos a la base de datos
 * @return bool True si la migración fue exitosa
 */
function migrateConfigsToDatabase() {
    try {
        writeLog('INFO', 'Iniciando migración de configuraciones a BD');
        
        // Crear tabla si no existe
        $pdo = createDbConnection();
        if (!createSystemConfigTable($pdo)) {
            writeLog('ERROR', 'No se pudo crear la tabla system_config');
            return false;
        }
        
        // Migrar configuraciones actuales
        $success = initializeDefaultConfigs();
        
        if ($success) {
            writeLog('INFO', 'Migración de configuraciones completada exitosamente');
        } else {
            writeLog('ERROR', 'Error durante la migración de configuraciones');
        }
        
        return $success;
        
    } catch (Exception $e) {
        writeLog('ERROR', 'Error crítico en migración de configuraciones: ' . $e->getMessage());
        return false;
    }
}

// Función para validar configuración de WhatsApp
function validateWhatsAppConfig() {
    $errors = [];
    
    if (WHATSAPP_TOKEN === 'TU_TOKEN_DE_WHATSAPP_AQUI' || empty(WHATSAPP_TOKEN)) {
        $errors[] = 'Token de WhatsApp no configurado';
    }
    
    if (WHATSAPP_PHONE_NUMBER_ID === 'TU_PHONE_ID_AQUI' || empty(WHATSAPP_PHONE_NUMBER_ID)) {
        $errors[] = 'Phone Number ID de WhatsApp no configurado';
    }
    
    // Detectar Phone Number ID problemático conocido
    if (WHATSAPP_PHONE_NUMBER_ID === '858157464051987') {
        $errors[] = '⚠️ PHONE NUMBER ID PROBLEMÁTICO: El ID 858157464051987 genera errores. <a href="fix_phone_number_id.php" class="alert-link"><strong>Corregir ahora</strong></a>';
    }
    
    if (empty(WEBHOOK_VERIFY_TOKEN)) {
        $errors[] = 'Token de verificación del webhook no configurado';
    }
    
    return $errors;
}

// Función para logging centralizado
if (!function_exists('writeLog')) {
function writeLog($level, $message, $context = []) {
    if (!ENABLE_LOGGING) return;
    
    $logLevels = ['DEBUG' => 0, 'INFO' => 1, 'WARNING' => 2, 'ERROR' => 3];
    $currentLevel = $logLevels[LOG_LEVEL] ?? 1;
    
    if (($logLevels[$level] ?? 1) < $currentLevel) {
        return;
    }
    
    $timestamp = date('Y-m-d H:i:s');
    $contextStr = $context ? ' ' . json_encode($context) : '';
    $logMessage = "[$timestamp] [$level] $message$contextStr" . PHP_EOL;
    
    $logDir = dirname(__DIR__) . '/logs';
    if (!is_dir($logDir)) {
        mkdir($logDir, 0755, true);
    }
    
    file_put_contents($logDir . '/system.log', $logMessage, FILE_APPEND | LOCK_EX);
}
} 

// Crear directorios necesarios
$requiredDirs = [
    dirname(__DIR__) . '/logs',
    dirname(__DIR__) . '/uploads'
];

foreach ($requiredDirs as $dir) {
    if (!is_dir($dir)) {
        mkdir($dir, 0755, true);
    }
}

// Inicializar sesión si no está activa
if (session_status() == PHP_SESSION_NONE) {
    session_start();
}

// Verificar timeout de sesión
if (isUserLoggedIn() && isset($_SESSION['last_activity'])) {
    if (time() - $_SESSION['last_activity'] > SESSION_TIMEOUT) {
        session_destroy();
        session_start();
    }
}

// Actualizar última actividad
if (isUserLoggedIn()) {
    $_SESSION['last_activity'] = time();
}

// Función para verificar autenticación en APIs
function requireAuthentication() {
    if (!isUserLoggedIn()) {
        http_response_code(401);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode([
            'success' => false, 
            'error' => 'No autorizado. Inicie sesión.',
            'redirect' => 'login.php'
        ]);
        exit;
    }
}
?>