<?php
/**
 * @intranet-file common/auth.php
 * @intranet-file-version 2026.03.13.1
 * @intranet-file-date 2026-03-13
 * @intranet-file-owner CSIF Intranet
 */
require_once 'config.php';
require_once 'db.php';
require_once __DIR__ . '/roles.php';

function ldap_auth_escape_filter_value(string $value): string {
    if (function_exists('ldap_escape')) {
        if (defined('LDAP_ESCAPE_FILTER')) {
            return ldap_escape($value, '', LDAP_ESCAPE_FILTER);
        }
        return ldap_escape($value, '');
    }

    return strtr($value, [
        '\\' => '\\5c',
        '*' => '\\2a',
        '(' => '\\28',
        ')' => '\\29',
        "\x00" => '\\00',
    ]);
}

function ldap_auth_parse_server_url(string $server): array {
    $parts = @parse_url(trim($server));
    if ($parts === false || empty($parts['host'])) {
        return [null, null, null];
    }
    $scheme = strtolower((string)($parts['scheme'] ?? 'ldap'));
    $host = (string)$parts['host'];
    $port = (int)($parts['port'] ?? ($scheme === 'ldaps' ? 636 : 389));
    return [$scheme, $host, $port];
}

function ldap_auth_last_diagnostic($conn): string {
    $diag = '';
    if (function_exists('ldap_get_option') && defined('LDAP_OPT_DIAGNOSTIC_MESSAGE')) {
        @ldap_get_option($conn, LDAP_OPT_DIAGNOSTIC_MESSAGE, $diag);
    }
    return trim((string)$diag);
}


function authenticateUser($username, $password) {
    if (empty($username) || empty($password)) return ['success' => false, 'msg' => 'Credenciales vacías'];

    [$ldapScheme, $ldapHost, $ldapPort] = ldap_auth_parse_server_url(LDAP_SERVER);

    $conn = ldap_connect(LDAP_SERVER);
    if (!$conn) return ['success' => false, 'msg' => 'Sin conexión LDAP'];

    ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3);
    ldap_set_option($conn, LDAP_OPT_REFERRALS, 0);
    if (defined('LDAP_OPT_NETWORK_TIMEOUT')) {
        @ldap_set_option($conn, LDAP_OPT_NETWORK_TIMEOUT, 5);
    }
    if (defined('LDAP_OPT_TIMEOUT')) {
        @ldap_set_option($conn, LDAP_OPT_TIMEOUT, 10);
    }

    // 1. Bind como administrador
    if (!@ldap_bind($conn, LDAP_ADMIN_DN, LDAP_ADMIN_PASS)) {
        $diag = ldap_auth_last_diagnostic($conn);
        $target = ($ldapHost && $ldapPort) ? " en {$ldapHost}:{$ldapPort}" : '';
        error_log("LDAP Admin Bind Failed{$target}" . ($diag !== '' ? " | {$diag}" : ''));
        return ['success' => false, 'msg' => 'Error interno de autenticación'];
    }

    // 2. Buscar usuario
    $safeUsername = ldap_auth_escape_filter_value((string)$username);
    $filter = "(sAMAccountName={$safeUsername})";
    $search = ldap_search($conn, LDAP_BASE_DN, $filter, ['dn', 'givenname', 'memberof']);
    $entries = ldap_get_entries($conn, $search);

    if ($entries['count'] === 0) {
        return ['success' => false, 'msg' => 'Usuario no encontrado'];
    }

    $userDn = $entries[0]['dn'];
    $givenName = $entries[0]['givenname'][0] ?? $username;

    // 3. Verificar contraseña
    if (!@ldap_bind($conn, $userDn, $password)) {
        return ['success' => false, 'msg' => 'Contraseña incorrecta'];
    }

    // 4. Mapeo de Grupos (AD -> Roles Internos)
    // Lista de grupos que nos importan. Todo en minúsculas para comparar.
    $monitoredGroups = intranet_known_roles();

    $adGroups = $entries[0]['memberof'] ?? [];
    unset($adGroups['count']);

    $detectedRoles = [];

    foreach ($adGroups as $groupDN) {
        $groupLower = strtolower($groupDN);

        // Verificamos si el DN del grupo contiene alguna de nuestras palabras clave
        foreach ($monitoredGroups as $targetGroup) {
            // Buscamos "CN=nombre_grupo," para ser específicos
            if (strpos($groupLower, 'cn=' . $targetGroup . ',') !== false || strpos($groupLower, 'cn=' . $targetGroup . ')') !== false) {
                // Guardamos el rol limpio (ej: 'app_rrhh')
                if (!in_array($targetGroup, $detectedRoles)) {
                    $detectedRoles[] = $targetGroup;
                }
            }
        }
    }

    // 4b. Detectar departamentos (grupos de seguridad que empiezan por "dept_")
    // Guardamos el CN exacto (ej: dept_informatica)
    $departments = [];
    foreach ($adGroups as $groupDN) {
        if (preg_match('/CN=([^,]+)/i', (string)$groupDN, $m)) {
            $cn = (string)$m[1];
            if (stripos($cn, 'dept_') === 0) {
                $departments[] = $cn;
            }
        }
    }
    $departments = array_values(array_unique($departments));
    $primaryDepartment = $departments[0] ?? null;

    // Si no tiene ningún grupo de los monitorizados, denegar acceso
    if (empty($detectedRoles)) {
        return ['success' => false, 'msg' => 'No tienes permisos de acceso (Sin grupos asignados).'];
    }

    // 5. Definir Rol Primario (Para visualización: Badge de color)
    // Prioridad: Admin > Responsable > Otros
    $primaryRole = 'usuario'; // Default
    if (in_array('administradores', $detectedRoles)) {
        $primaryRole = 'administradores';
    } elseif (in_array('app_responsables', $detectedRoles)) {
        $primaryRole = 'app_responsables';
    } else {
        // Si tiene app_rrhh, app_entregas, etc., tomamos el primero como etiqueta
        $primaryRole = $detectedRoles[0]; 
    }

    return [
        'success' => true,
        'data' => [
            'username' => $username,
            'givenName' => $givenName,
            'roles' => $detectedRoles, // Array con TODOS los permisos
            'primaryRole' => $primaryRole, // String para visualización
            'departments' => $departments, // Grupos dept_* detectados
            'primaryDepartment' => $primaryDepartment,
            'loginTime' => date('Y-m-d H:i:s')
        ]
    ];
}
?>
