<?php
/**
 * AURA FARMING — autenticação e operações do painel administrativo.
 * Login administrativo separado das contas de jogadores.
 */

if (!defined('ADMIN_AUTH_COOKIE_NAME')) define('ADMIN_AUTH_COOKIE_NAME', 'af_admin_auth');
if (!defined('ADMIN_SESSION_DAYS')) define('ADMIN_SESSION_DAYS', 7);
if (!defined('ADMIN_LOGIN_MAX_ATTEMPTS')) define('ADMIN_LOGIN_MAX_ATTEMPTS', 5);
if (!defined('ADMIN_LOCK_MINUTES')) define('ADMIN_LOCK_MINUTES', 15);

function afAdminPrivacyHash(string $value): string
{
    return hash_hmac('sha256', $value, defined('SECURITY_HASH_SALT') ? SECURITY_HASH_SALT : 'aura-admin');
}

function afAdminIpHash(): string
{
    return afAdminPrivacyHash((string)($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'));
}

function afAdminUserAgentHash(): string
{
    return afAdminPrivacyHash(substr((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 1000));
}

function afAdminCookieOptions(int $expires): array
{
    return [
        'expires' => $expires,
        'path' => '/',
        'secure' => function_exists('isHttps') ? isHttps() : (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'),
        'httponly' => true,
        'samesite' => 'Strict',
    ];
}

function afAdminId(): int
{
    return (int)($_SESSION['admin_usuario_id'] ?? 0);
}

function afAdminAtual(): array
{
    return is_array($_SESSION['admin_usuario'] ?? null) ? $_SESSION['admin_usuario'] : [];
}

function afAdminIsSuper(?array $admin = null): bool
{
    $admin = $admin ?: afAdminAtual();
    return (string)($admin['papel'] ?? '') === 'administrador';
}

function afAdminPasswordError(string $password): ?string
{
    if (strlen($password) < 10) return 'A senha deve ter pelo menos 10 caracteres.';
    if (!preg_match('/[a-z]/', $password)) return 'A senha precisa conter uma letra minúscula.';
    if (!preg_match('/[A-Z]/', $password)) return 'A senha precisa conter uma letra maiúscula.';
    if (!preg_match('/\d/', $password)) return 'A senha precisa conter pelo menos um número.';
    return null;
}

function afAdminAccessLog(PDO $pdo, ?int $adminId, string $evento, bool $sucesso, array $detalhes = []): void
{
    try {
        $st = $pdo->prepare('INSERT INTO af_admin_access_logs(admin_user_id,evento,sucesso,ip_hash,user_agent_hash,detalhes,criado_em) VALUES (?,?,?,?,?,?,NOW())');
        $st->execute([
            $adminId ?: null,
            substr($evento, 0, 80),
            $sucesso ? 1 : 0,
            afAdminIpHash(),
            afAdminUserAgentHash(),
            $detalhes ? json_encode($detalhes, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null,
        ]);
    } catch (Throwable $e) {
        if (defined('APP_DEBUG') && APP_DEBUG) error_log('[AURA-ADMIN-LOG] '.$e->getMessage());
    }
}

function afAdminClearLocalSession(): void
{
    unset(
        $_SESSION['admin_usuario_id'],
        $_SESSION['admin_usuario'],
        $_SESSION['admin_sessao_id'],
        $_SESSION['admin_ultima_validacao'],
        $_SESSION['admin_return_to']
    );
    setcookie(ADMIN_AUTH_COOKIE_NAME, '', afAdminCookieOptions(time() - 3600));
    unset($_COOKIE[ADMIN_AUTH_COOKIE_NAME]);
}

function afAdminCreateSession(PDO $pdo, array $admin, bool $remember = false): string
{
    session_regenerate_id(true);
    $raw = bin2hex(random_bytes(32));
    $hash = hash('sha256', $raw);
    $days = $remember ? max(ADMIN_SESSION_DAYS, 30) : ADMIN_SESSION_DAYS;
    $expires = (new DateTimeImmutable('+'.$days.' days'))->format('Y-m-d H:i:s');
    $st = $pdo->prepare('INSERT INTO af_admin_sessions(admin_user_id,token_hash,php_session_id,ip_hash,user_agent_hash,expira_em,ultima_atividade,criado_em) VALUES (?,?,?,?,?,?,NOW(),NOW())');
    $st->execute([(int)$admin['id'], $hash, session_id(), afAdminIpHash(), afAdminUserAgentHash(), $expires]);
    $sessionId = (int)$pdo->lastInsertId();

    setcookie(ADMIN_AUTH_COOKIE_NAME, $raw, afAdminCookieOptions(time() + ($days * 86400)));
    $_COOKIE[ADMIN_AUTH_COOKIE_NAME] = $raw;
    unset($admin['senha_hash']);
    $_SESSION['admin_usuario_id'] = (int)$admin['id'];
    $_SESSION['admin_usuario'] = $admin;
    $_SESSION['admin_sessao_id'] = $sessionId;
    $_SESSION['admin_ultima_validacao'] = time();

    $pdo->prepare('UPDATE af_admin_users SET ultimo_login=NOW(),tentativas_falhas=0,bloqueado_ate=NULL,atualizado_em=NOW() WHERE id=?')->execute([(int)$admin['id']]);
    afAdminAccessLog($pdo, (int)$admin['id'], 'login', true);
    return $raw;
}

function afAdminValidateSession(PDO $pdo, bool $force = false): bool
{
    $raw = (string)($_COOKIE[ADMIN_AUTH_COOKIE_NAME] ?? '');
    if ($raw === '') {
        afAdminClearLocalSession();
        return false;
    }

    if (!$force && !empty($_SESSION['admin_usuario_id']) && !empty($_SESSION['admin_sessao_id'])
        && time() - (int)($_SESSION['admin_ultima_validacao'] ?? 0) < 60) {
        return true;
    }

    $hash = hash('sha256', $raw);
    $st = $pdo->prepare("SELECT s.id sessao_id,s.admin_user_id,s.user_agent_hash,s.expira_em,a.id,a.nome,a.email,a.papel,a.status,a.force_password_change,a.ultimo_login,a.criado_em
                         FROM af_admin_sessions s
                         JOIN af_admin_users a ON a.id=s.admin_user_id
                         WHERE s.token_hash=? AND s.revogada_em IS NULL AND s.expira_em>NOW() AND a.status='ativo'
                         LIMIT 1");
    $st->execute([$hash]);
    $admin = $st->fetch();
    if (!$admin || !hash_equals((string)$admin['user_agent_hash'], afAdminUserAgentHash())) {
        afAdminClearLocalSession();
        return false;
    }

    $pdo->prepare('UPDATE af_admin_sessions SET ultima_atividade=NOW(),php_session_id=?,ip_hash=? WHERE id=?')->execute([session_id(), afAdminIpHash(), (int)$admin['sessao_id']]);
    $sessionId = (int)$admin['sessao_id'];
    unset($admin['sessao_id'], $admin['user_agent_hash'], $admin['expira_em']);
    $_SESSION['admin_usuario_id'] = (int)$admin['id'];
    $_SESSION['admin_usuario'] = $admin;
    $_SESSION['admin_sessao_id'] = $sessionId;
    $_SESSION['admin_ultima_validacao'] = time();
    return true;
}

function afAdminFailedAttempts(PDO $pdo, string $email): int
{
    $minutes = max(1, (int)ADMIN_LOCK_MINUTES);
    $st = $pdo->prepare("SELECT COUNT(*) FROM af_admin_login_attempts
                         WHERE sucesso=0 AND criado_em>=DATE_SUB(NOW(),INTERVAL {$minutes} MINUTE)
                         AND (identificador_hash=? OR ip_hash=?)");
    $st->execute([afAdminPrivacyHash(strtolower($email)), afAdminIpHash()]);
    return (int)$st->fetchColumn();
}

function afAdminRegisterLoginAttempt(PDO $pdo, string $email, bool $success, ?int $adminId = null, string $reason = ''): void
{
    $pdo->prepare('INSERT INTO af_admin_login_attempts(admin_user_id,identificador_hash,ip_hash,sucesso,motivo,user_agent_hash,criado_em) VALUES (?,?,?,?,?,?,NOW())')
        ->execute([$adminId ?: null, afAdminPrivacyHash(strtolower(trim($email))), afAdminIpHash(), $success ? 1 : 0, substr($reason, 0, 120) ?: null, afAdminUserAgentHash()]);
}

function afAdminLogin(PDO $pdo, string $email, string $password, bool $remember = false): array
{
    $email = strtolower(trim($email));
    if (!filter_var($email, FILTER_VALIDATE_EMAIL) || $password === '') {
        throw new RuntimeException('Informe e-mail e senha válidos.');
    }
    if (afAdminFailedAttempts($pdo, $email) >= ADMIN_LOGIN_MAX_ATTEMPTS) {
        afAdminAccessLog($pdo, null, 'login_bloqueado_rate_limit', false, ['email_hash'=>afAdminPrivacyHash($email)]);
        throw new RuntimeException('Muitas tentativas. Aguarde '.ADMIN_LOCK_MINUTES.' minutos e tente novamente.');
    }

    $st = $pdo->prepare('SELECT * FROM af_admin_users WHERE email=? LIMIT 1');
    $st->execute([$email]);
    $admin = $st->fetch();
    $valid = $admin && (string)$admin['status'] === 'ativo' && password_verify($password, (string)$admin['senha_hash']);
    if ($admin && !empty($admin['bloqueado_ate']) && strtotime((string)$admin['bloqueado_ate']) > time()) $valid = false;

    if (!$valid) {
        $adminId = $admin ? (int)$admin['id'] : null;
        afAdminRegisterLoginAttempt($pdo, $email, false, $adminId, 'credenciais_invalidas');
        if ($admin) {
            $fails = (int)$admin['tentativas_falhas'] + 1;
            $lock = $fails >= ADMIN_LOGIN_MAX_ATTEMPTS ? "DATE_ADD(NOW(),INTERVAL ".ADMIN_LOCK_MINUTES." MINUTE)" : 'bloqueado_ate';
            $pdo->exec("UPDATE af_admin_users SET tentativas_falhas={$fails},bloqueado_ate={$lock},atualizado_em=NOW() WHERE id=".(int)$admin['id']);
        }
        afAdminAccessLog($pdo, $adminId, 'login', false);
        throw new RuntimeException('E-mail ou senha incorretos.');
    }

    if (password_needs_rehash((string)$admin['senha_hash'], PASSWORD_DEFAULT)) {
        $newHash = password_hash($password, PASSWORD_DEFAULT);
        $pdo->prepare('UPDATE af_admin_users SET senha_hash=? WHERE id=?')->execute([$newHash, (int)$admin['id']]);
        $admin['senha_hash'] = $newHash;
    }
    afAdminRegisterLoginAttempt($pdo, $email, true, (int)$admin['id'], 'sucesso');
    $pdo->prepare('DELETE FROM af_admin_login_attempts WHERE sucesso=0 AND identificador_hash=?')->execute([afAdminPrivacyHash($email)]);
    $token = afAdminCreateSession($pdo, $admin, $remember);
    $current = afAdminAtual();
    $current['_token'] = $token;
    return $current;
}

function afAdminLogout(PDO $pdo): void
{
    $adminId = afAdminId();
    $sid = (int)($_SESSION['admin_sessao_id'] ?? 0);
    if ($sid) $pdo->prepare('UPDATE af_admin_sessions SET revogada_em=NOW() WHERE id=?')->execute([$sid]);
    if ($adminId) afAdminAccessLog($pdo, $adminId, 'logout', true);
    afAdminClearLocalSession();
}

function afAdminRequireAuth(array $roles = []): void
{
    global $pdo;
    if (!isset($pdo) || !$pdo instanceof PDO || !afAdminValidateSession($pdo)) {
        if ($_SERVER['REQUEST_METHOD'] === 'GET') $_SESSION['admin_return_to'] = (string)($_SERVER['REQUEST_URI'] ?? '');
        header('Location: '.SITE_URL.'/admin/login.php');
        exit;
    }
    $admin = afAdminAtual();
    if ($roles && !in_array((string)($admin['papel'] ?? ''), $roles, true)) {
        http_response_code(403);
        exit('Acesso não autorizado para este nível administrativo.');
    }
    if (!empty($admin['force_password_change']) && !str_ends_with((string)($_SERVER['SCRIPT_NAME'] ?? ''), '/admin/conta.php')) {
        setFlash('error', 'Defina uma nova senha antes de continuar.');
        header('Location: '.SITE_URL.'/admin/conta.php');
        exit;
    }
}

function afRequireSuperAdmin(): void
{
    afAdminRequireAuth(['administrador']);
}

function afAdminCount(PDO $pdo): int
{
    return (int)$pdo->query('SELECT COUNT(*) FROM af_admin_users')->fetchColumn();
}

function afAdminCreateUser(PDO $pdo, string $name, string $email, string $password, string $role = 'moderador', bool $forceChange = true, bool $setup = false): int
{
    if (!$setup) afRequireSuperAdmin();
    $name = trim(strip_tags($name));
    $email = strtolower(trim($email));
    if (mb_strlen($name) < 3 || mb_strlen($name) > 120) throw new RuntimeException('Informe o nome completo do administrador.');
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) throw new RuntimeException('Informe um e-mail válido.');
    if (!in_array($role, ['administrador','moderador'], true)) throw new RuntimeException('Nível administrativo inválido.');
    if ($error = afAdminPasswordError($password)) throw new RuntimeException($error);
    $started = false;
    if ($setup && !$pdo->inTransaction()) {
        $pdo->beginTransaction();
        $started = true;
        $first = $pdo->query('SELECT id FROM af_admin_users ORDER BY id LIMIT 1 FOR UPDATE')->fetchColumn();
        if ($first) { $pdo->rollBack(); throw new RuntimeException('O primeiro administrador já foi criado.'); }
    }
    try {
        $st = $pdo->prepare('SELECT id FROM af_admin_users WHERE email=? LIMIT 1');
        $st->execute([$email]);
        if ($st->fetchColumn()) throw new RuntimeException('Já existe um administrador com este e-mail.');
        $st = $pdo->prepare("INSERT INTO af_admin_users(nome,email,senha_hash,papel,status,force_password_change,senha_alterada_em,criado_em,atualizado_em) VALUES (?,?,?,?,'ativo',?,NOW(),NOW(),NOW())");
        $st->execute([$name, $email, password_hash($password, PASSWORD_DEFAULT), $role, $forceChange ? 1 : 0]);
        $id = (int)$pdo->lastInsertId();
        if ($started) $pdo->commit();
    } catch (Throwable $e) {
        if ($started && $pdo->inTransaction()) $pdo->rollBack();
        throw $e;
    }
    afAdminAccessLog($pdo, $id, $setup ? 'primeiro_admin_criado' : 'admin_criado', true, ['papel'=>$role,'criado_por'=>afAdminId() ?: null]);
    if (!$setup) afAdminLog($pdo, 'admin_criado', 'admin_user', $id, ['email'=>$email,'papel'=>$role]);
    return $id;
}

function afAdminRequestPasswordReset(PDO $pdo, string $email): void
{
    $email = strtolower(trim($email));
    $st = $pdo->prepare("SELECT id,nome,email FROM af_admin_users WHERE email=? AND status='ativo' LIMIT 1");
    $st->execute([$email]);
    $admin = $st->fetch();
    if (!$admin) return;

    $raw = bin2hex(random_bytes(32));
    $pdo->prepare('UPDATE af_admin_password_resets SET usado_em=NOW() WHERE admin_user_id=? AND usado_em IS NULL')->execute([(int)$admin['id']]);
    $pdo->prepare('INSERT INTO af_admin_password_resets(admin_user_id,token_hash,expira_em,criado_em) VALUES (?,?,DATE_ADD(NOW(),INTERVAL 60 MINUTE),NOW())')
        ->execute([(int)$admin['id'], hash('sha256', $raw)]);
    $link = SITE_URL.'/admin/redefinir.php?token='.rawurlencode($raw);
    $subject = 'Redefinição de senha — Aura Admin';
    $message = "Olá, {$admin['nome']}.\n\nUse o link abaixo para redefinir sua senha administrativa. Ele expira em 60 minutos:\n{$link}\n\nSe você não solicitou, ignore esta mensagem.";
    $headers = 'From: '.MAIL_FROM_NAME.' <'.MAIL_FROM.">\r\nContent-Type: text/plain; charset=UTF-8";
    @mail((string)$admin['email'], $subject, $message, $headers);
    afAdminAccessLog($pdo, (int)$admin['id'], 'recuperacao_solicitada', true);
}

function afAdminResetPassword(PDO $pdo, string $token, string $password): void
{
    if ($error = afAdminPasswordError($password)) throw new RuntimeException($error);
    if (!preg_match('/^[a-f0-9]{64}$/', $token)) throw new RuntimeException('Link de recuperação inválido.');
    $st = $pdo->prepare("SELECT r.id reset_id,r.admin_user_id,a.status FROM af_admin_password_resets r JOIN af_admin_users a ON a.id=r.admin_user_id WHERE r.token_hash=? AND r.usado_em IS NULL AND r.expira_em>NOW() LIMIT 1");
    $st->execute([hash('sha256', $token)]);
    $row = $st->fetch();
    if (!$row || (string)$row['status'] !== 'ativo') throw new RuntimeException('O link é inválido ou expirou.');
    $pdo->beginTransaction();
    try {
        $pdo->prepare('UPDATE af_admin_users SET senha_hash=?,force_password_change=0,tentativas_falhas=0,bloqueado_ate=NULL,senha_alterada_em=NOW(),atualizado_em=NOW() WHERE id=?')
            ->execute([password_hash($password, PASSWORD_DEFAULT), (int)$row['admin_user_id']]);
        $pdo->prepare('UPDATE af_admin_password_resets SET usado_em=NOW() WHERE id=?')->execute([(int)$row['reset_id']]);
        $pdo->prepare('UPDATE af_admin_sessions SET revogada_em=NOW() WHERE admin_user_id=? AND revogada_em IS NULL')->execute([(int)$row['admin_user_id']]);
        $pdo->commit();
        afAdminAccessLog($pdo, (int)$row['admin_user_id'], 'senha_redefinida', true);
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        throw $e;
    }
}

function afAdminLog(PDO $pdo, string $action, string $entityType = '', int|string|null $entityId = null, array $details = []): void
{
    $adminId = afAdminId();
    $st = $pdo->prepare('INSERT INTO af_admin_logs(admin_id,admin_user_id,acao,entidade_tipo,entidade_id,detalhes,ip_hash,criado_em) VALUES (NULL,?,?,?,?,?,?,NOW())');
    $st->execute([
        $adminId ?: null,
        substr($action, 0, 80),
        substr($entityType, 0, 60) ?: null,
        $entityId !== null ? substr((string)$entityId, 0, 80) : null,
        $details ? json_encode($details, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null,
        afAdminIpHash(),
    ]);
    if (function_exists('afStage24AuditLog')) {
        afStage24AuditLog($pdo, $action, $entityType, $entityId, [], [], [
            'actor_type' => 'admin',
            'actor_admin_id' => $adminId ?: null,
            'source' => 'admin_panel',
            'details' => $details,
            'severity' => str_contains($action, 'bloquear') || str_contains($action, 'suspender') ? 'high' : 'info',
        ]);
    }
}

function afAdminDashboard(PDO $pdo): array
{
    $queries = [
        'usuarios' => 'SELECT COUNT(*) FROM af_usuarios',
        'usuarios_ativos' => "SELECT COUNT(*) FROM af_usuarios WHERE status=1 AND (suspenso_ate IS NULL OR suspenso_ate<NOW())",
        'novos_hoje' => 'SELECT COUNT(*) FROM af_usuarios WHERE data_cadastro>=CURDATE()',
        'partidas_hoje' => 'SELECT COUNT(*) FROM af_partidas WHERE criada_em>=CURDATE()',
        'partidas_finalizadas' => "SELECT COUNT(*) FROM af_partidas WHERE status='finalizada' AND admin_status<>'anulada'",
        'ao_vivo' => "SELECT COUNT(*) FROM af_partidas WHERE status='em_andamento'",
        'revisao' => "SELECT COUNT(*) FROM af_partidas WHERE status='finalizada' AND antifraude_status='revisao' AND admin_status<>'anulada'",
        'denuncias' => "SELECT COUNT(*) FROM af_safety_reports WHERE status IN ('open','triage','in_review')",
        'competicoes' => "SELECT COUNT(*) FROM af_competicoes WHERE status IN ('inscricoes','em_andamento')",
        'pontos_validados' => "SELECT COALESCE(SUM(pontuacao_validada),0) FROM af_partidas WHERE status='finalizada' AND antifraude_status='aprovada' AND admin_status<>'anulada'",
    ];
    $out = [];
    foreach ($queries as $key => $sql) $out[$key] = (float)$pdo->query($sql)->fetchColumn();
    return $out;
}

function afAdminPagination(int $total, int $perPage = 25): array
{
    $perPage = max(10, min(100, $perPage));
    $pages = max(1, (int)ceil($total / $perPage));
    $page = max(1, min($pages, (int)($_GET['page'] ?? 1)));
    return ['page'=>$page,'pages'=>$pages,'per_page'=>$perPage,'offset'=>($page-1)*$perPage,'total'=>$total];
}

function afAdminConfigValue(string $value, string $type): mixed
{
    return match ($type) {
        'int' => (string)(int)$value,
        'float' => (string)(float)$value,
        'bool' => in_array(strtolower(trim($value)), ['1','true','sim','on'], true) ? '1' : '0',
        'json' => json_encode(json_decode($value, true, 512, JSON_THROW_ON_ERROR), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        default => trim($value),
    };
}

function afAdminNotify(PDO $pdo, int $adminId, string $title, string $message, string $segment, ?int $userId = null): int
{
    $adminId = $adminId ?: afAdminId();
    $title = substr(sanitize($title), 0, 150);
    $message = trim(strip_tags($message));
    if ($title === '' || $message === '') throw new RuntimeException('Informe título e mensagem.');
    $params = [];
    $where = '1=1';
    if ($userId) { $where = 'u.id=?'; $params[] = $userId; }
    elseif ($segment === 'ativos') $where = 'u.status=1';
    elseif ($segment === 'inativos') $where = 'u.status=0';
    elseif ($segment === 'bloqueados') $where = "u.status=2 OR u.seguranca_status IN ('restrito','banido')";
    $st = $pdo->prepare("SELECT u.id FROM af_usuarios u WHERE {$where}");
    $st->execute($params);
    $ids = $st->fetchAll(PDO::FETCH_COLUMN);
    if (!$ids) throw new RuntimeException('Nenhum destinatário encontrado.');

    $pdo->beginTransaction();
    try {
        $ins = $pdo->prepare("INSERT INTO af_notificacoes(usuario_id,tipo,titulo,mensagem,dados,criada_em) VALUES (?,'admin',?,?,?,NOW())");
        foreach ($ids as $id) $ins->execute([(int)$id, $title, $message, json_encode(['admin_user_id'=>$adminId], JSON_UNESCAPED_UNICODE)]);
        $pdo->prepare("INSERT INTO af_admin_broadcasts(admin_id,admin_user_id,titulo,mensagem,segmento,usuario_alvo_id,total_enviados,status,criado_em) VALUES (NULL,?,?,?,?,?,?,'enviado',NOW())")
            ->execute([$adminId, $title, $message, $userId ? 'individual' : $segment, $userId, count($ids)]);
        $pdo->commit();
        afAdminLog($pdo, 'notificacao_enviada', 'broadcast', null, ['segmento'=>$segment,'usuario_id'=>$userId,'total'=>count($ids)]);
        return count($ids);
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        throw $e;
    }
}

function afAdminUserAction(PDO $pdo, int $targetId, string $action, array $data = []): void
{
    if ($targetId < 1) throw new RuntimeException('Usuário inválido.');
    switch ($action) {
        case 'ativar':
            $pdo->prepare("UPDATE af_usuarios SET status=1,seguranca_status='normal',suspenso_ate=NULL WHERE id=?")->execute([$targetId]);
            break;
        case 'inativar':
            $pdo->prepare('UPDATE af_usuarios SET status=0 WHERE id=?')->execute([$targetId]);
            break;
        case 'bloquear':
            $pdo->prepare("UPDATE af_usuarios SET status=2,seguranca_status='restrito',suspenso_ate=DATE_ADD(NOW(),INTERVAL 7 DAY) WHERE id=?")->execute([$targetId]);
            break;
        case 'banir':
            afRequireSuperAdmin();
            $pdo->prepare("UPDATE af_usuarios SET status=2,seguranca_status='banido',suspenso_ate=NULL WHERE id=?")->execute([$targetId]);
            break;
        default:
            throw new RuntimeException('Ação administrativa inválida.');
    }
    $pdo->prepare('UPDATE af_sessoes SET revogada_em=NOW() WHERE usuario_id=? AND revogada_em IS NULL')->execute([$targetId]);
    afAdminLog($pdo, 'usuario_'.$action, 'usuario', $targetId, $data);
}
