<?php
// ============================================================
// Autenticación — Argon2id, registro, login, sesión, roles.
// ============================================================

// --- Hashing de contraseñas: Argon2id (fallback bcrypt) ------
function hash_password(string $pw): string {
    if (defined('PASSWORD_ARGON2ID')) {
        return password_hash($pw, PASSWORD_ARGON2ID, [
            'memory_cost' => 65536, // 64 MB
            'time_cost'   => 4,
            'threads'     => 2,
        ]);
    }
    // Fallback muy seguro si el host no compiló Argon2.
    return password_hash($pw, PASSWORD_BCRYPT, ['cost' => 12]);
}

function verify_password(string $pw, string $hash): bool {
    return password_verify($pw, $hash);
}

// --- Rate limit: máx 6 intentos fallidos por IP en 15 min ----
function login_attempts_recent(string $ip): int {
    $st = db()->prepare(
        'SELECT COUNT(*) FROM login_attempts WHERE ip = ? AND created_at > (NOW() - INTERVAL 15 MINUTE)'
    );
    $st->execute([$ip]);
    return (int) $st->fetchColumn();
}

function login_attempt_log(string $ip, ?string $username): void {
    $st = db()->prepare('INSERT INTO login_attempts (ip, username) VALUES (?, ?)');
    $st->execute([$ip, $username]);
}

function login_attempts_clear(string $ip): void {
    $st = db()->prepare('DELETE FROM login_attempts WHERE ip = ?');
    $st->execute([$ip]);
}

// --- Registro. Devuelve [ok(bool), error(string)] -----------
function register_user(string $username, string $email, string $pw): array {
    $username = trim($username);
    $email    = trim($email);

    if (!preg_match('/^[A-Za-z0-9_]{3,32}$/', $username)) {
        return [false, 'El usuario debe tener 3-32 caracteres (letras, números o _).'];
    }
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        return [false, 'El correo no es válido.'];
    }
    if (strlen($pw) < 8) {
        return [false, 'La contraseña debe tener al menos 8 caracteres.'];
    }

    // ¿ya existe usuario o email?
    $st = db()->prepare('SELECT 1 FROM users WHERE username = ? OR email = ? LIMIT 1');
    $st->execute([$username, $email]);
    if ($st->fetchColumn()) {
        return [false, 'Ese usuario o correo ya está registrado.'];
    }

    $st = db()->prepare(
        'INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)'
    );
    $st->execute([$username, $email, hash_password($pw)]);
    return [true, ''];
}

// --- Login. Devuelve [ok(bool), error(string)] --------------
function login_user(string $login, string $pw, string $ip): array {
    if (login_attempts_recent($ip) >= 6) {
        return [false, 'Demasiados intentos. Espera 15 minutos e inténtalo de nuevo.'];
    }

    $login = trim($login);
    $st = db()->prepare(
        'SELECT * FROM users WHERE username = ? OR email = ? LIMIT 1'
    );
    $st->execute([$login, $login]);
    $user = $st->fetch();

    if (!$user || !verify_password($pw, $user['password_hash'])) {
        login_attempt_log($ip, $login);
        return [false, 'Usuario o contraseña incorrectos.'];
    }

    // Re-hash si cambian los parámetros/algoritmo en el futuro.
    if (password_needs_rehash($user['password_hash'],
        defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_BCRYPT)) {
        $up = db()->prepare('UPDATE users SET password_hash = ? WHERE id = ?');
        $up->execute([hash_password($pw), $user['id']]);
    }

    // Sesión: regenerar id para evitar fijación de sesión.
    session_regenerate_id(true);
    $_SESSION['uid']  = (int) $user['id'];
    $_SESSION['uname'] = $user['username'];
    $_SESSION['role'] = $user['role'];

    login_attempts_clear($ip);
    db()->prepare('UPDATE users SET last_login = NOW() WHERE id = ?')->execute([$user['id']]);
    return [true, ''];
}

function logout_user(): void {
    $_SESSION = [];
    if (ini_get('session.use_cookies')) {
        $p = session_get_cookie_params();
        setcookie(session_name(), '', time() - 42000, $p['path'], $p['domain'], $p['secure'], $p['httponly']);
    }
    session_destroy();
}

// --- Helpers de sesión / roles ------------------------------
function is_logged_in(): bool { return !empty($_SESSION['uid']); }
function is_admin(): bool { return is_logged_in() && ($_SESSION['role'] ?? '') === 'admin'; }

function current_user(): ?array {
    if (!is_logged_in()) return null;
    static $u = null;
    if ($u === null) {
        $st = db()->prepare('SELECT id, username, email, role, created_at, last_login FROM users WHERE id = ?');
        $st->execute([$_SESSION['uid']]);
        $u = $st->fetch() ?: null;
    }
    return $u;
}

function require_login(): void {
    if (!is_logged_in()) { redirect('login.php'); }
}

function require_admin(): void {
    if (!is_admin()) { http_response_code(403); exit('Acceso denegado.'); }
}

// --- Licencias --------------------------------------------
function user_has_license(int $userId, string $plugin): bool {
    $st = db()->prepare(
        "SELECT 1 FROM licenses WHERE user_id = ? AND plugin_slug = ? AND status = 'active' LIMIT 1"
    );
    $st->execute([$userId, $plugin]);
    return (bool) $st->fetchColumn();
}
