<?php
function current_user(PDO $pdo): ?array {
    if (empty($_SESSION['user_id'])) return null;
    $stmt = $pdo->prepare('SELECT id, name, email, role, created_at FROM '.qtbl('users').' WHERE id = ?');
    $stmt->execute([$_SESSION['user_id']]);
    return $stmt->fetch() ?: null;
}
function require_login(PDO $pdo): array {
    $user = current_user($pdo);
    if (!$user) { header('Location: login.php'); exit; }
    return $user;
}
function is_superadmin(array $user): bool { return ($user['role'] ?? 'user') === 'superadmin'; }
function require_superadmin(PDO $pdo): array {
    $user = require_login($pdo);
    if (!is_superadmin($user)) { http_response_code(403); exit('Keine Berechtigung.'); }
    return $user;
}
function superadmin_count(PDO $pdo): int {
    return (int)$pdo->query("SELECT COUNT(*) FROM ".qtbl('users')." WHERE role = 'superadmin'")->fetchColumn();
}
function login(PDO $pdo, string $email, string $password): bool {
    unset($_SESSION['login_message']);
    $genericMessage = 'Login fehlgeschlagen oder Konto vorübergehend gesperrt.';
    $email = strtolower(trim($email));
    $stmt = $pdo->prepare('SELECT id, password_hash, email_verified_at, failed_login_count, login_locked_until FROM '.qtbl('users').' WHERE email = ?');
    $stmt->execute([$email]);
    $user = $stmt->fetch();
    if (!$user) { $_SESSION['login_message'] = $genericMessage; return false; }

    if (!empty($user['login_locked_until']) && strtotime($user['login_locked_until']) > time()) {
        $_SESSION['login_message'] = $genericMessage;
        return false;
    }

    if (!password_verify($password, $user['password_hash'])) {
        if (auth_user_has_column($pdo, 'failed_login_count') && auth_user_has_column($pdo, 'login_locked_until')) {
            $failed = (int)($user['failed_login_count'] ?? 0) + 1;
            $lockedUntil = $failed >= 10 ? date('Y-m-d H:i:s', time() + 15 * 60) : null;
            $upd = $pdo->prepare('UPDATE '.qtbl('users').' SET failed_login_count=?, login_locked_until=? WHERE id=?');
            $upd->execute([$failed, $lockedUntil, (int)$user['id']]);
        }
        $_SESSION['login_message'] = $genericMessage;
        return false;
    }

    if (array_key_exists('email_verified_at', $user) && empty($user['email_verified_at'])) {
        $_SESSION['pending_verification_email'] = $email;
        $_SESSION['login_message'] = $genericMessage;
        return false;
    }
    if (auth_user_has_column($pdo, 'failed_login_count') && auth_user_has_column($pdo, 'login_locked_until')) {
        $upd = $pdo->prepare('UPDATE '.qtbl('users').' SET failed_login_count=0, login_locked_until=NULL WHERE id=?');
        $upd->execute([(int)$user['id']]);
    }
    session_regenerate_id(true);
    unset($_SESSION['pending_verification_email'], $_SESSION['login_message']);
    $_SESSION['user_id'] = (int)$user['id'];
    return true;
}
function logout_user(): void { $_SESSION = []; session_destroy(); }
function password_is_strong(string $password): bool {
    if (strlen($password) < 12) return false;
    if (!preg_match('/[A-Za-zÄÖÜäöü]/u', $password)) return false;
    if (!preg_match('/[0-9\\W_]/u', $password)) return false;
    return true;
}
function password_requirements_text(): string {
    return 'Das Passwort braucht mindestens 12 Zeichen, mindestens einen Buchstaben sowie mindestens eine Zahl oder ein Sonderzeichen.';
}
function register_user(PDO $pdo, string $email, string $password, string $role = 'user', bool $verified = false, string $name = ''): array {
    $email = strtolower(trim($email));
    $role = $role === 'superadmin' ? 'superadmin' : 'user';
    $name = trim($name);
    if (function_exists('mb_strlen') && mb_strlen($name) > 100) $name = mb_substr($name, 0, 100);
    elseif (strlen($name) > 100) $name = substr($name, 0, 100);
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) return [false, 'Bitte eine gültige E-Mail-Adresse erfassen.', null];
    if (!password_is_strong($password)) return [false, password_requirements_text(), null];
    try {
        if (auth_user_has_column($pdo, 'name')) {
            $columns = 'name,email,password_hash,role';
            $placeholders = '?,?,?,?';
            $values = [$name !== '' ? $name : null, $email, password_hash($password, PASSWORD_DEFAULT), $role];
        } else {
            $columns = 'email,password_hash,role';
            $placeholders = '?,?,?';
            $values = [$email, password_hash($password, PASSWORD_DEFAULT), $role];
        }
        if (auth_user_has_column($pdo, 'email_verified_at')) {
            $columns .= ',email_verified_at';
            $placeholders .= ',?';
            $values[] = $verified ? date('Y-m-d H:i:s') : null;
        }
        $stmt = $pdo->prepare('INSERT INTO '.qtbl('users').'('.$columns.') VALUES('.$placeholders.')');
        $stmt->execute($values);
        return [true, 'Benutzer wurde erstellt.', (int)$pdo->lastInsertId()];
    } catch (PDOException $e) {
        if (($e->errorInfo[1] ?? null) == 1062) return [false, 'Diese E-Mail-Adresse ist bereits registriert.', null];
        return [false, 'Registrierung fehlgeschlagen.', null];
    }
}
function update_account_profile(PDO $pdo, int $userId, string $email, string $name = '', string $currentPassword = ''): array {
    $email = strtolower(trim($email));
    $name = trim($name);
    if (function_exists('mb_strlen') && mb_strlen($name) > 100) $name = mb_substr($name, 0, 100);
    elseif (strlen($name) > 100) $name = substr($name, 0, 100);
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) return [false, 'Bitte eine gültige E-Mail-Adresse erfassen.'];

    $stmt = $pdo->prepare('SELECT email, password_hash'.(auth_user_has_column($pdo, 'name') ? ', name' : '').' FROM '.qtbl('users').' WHERE id = ?');
    $stmt->execute([$userId]);
    $existing = $stmt->fetch();
    if (!$existing) return [false, 'Benutzerkonto wurde nicht gefunden.'];

    $oldName = trim((string)($existing['name'] ?? ''));
    $newName = $name;
    $changed = strtolower((string)$existing['email']) !== $email || $oldName !== $newName;
    if ($changed && !password_verify($currentPassword, (string)$existing['password_hash'])) {
        return [false, 'Bitte bestätige die Änderung mit deinem aktuellen Passwort.'];
    }

    try {
        if (auth_user_has_column($pdo, 'name')) {
            $upd = $pdo->prepare('UPDATE '.qtbl('users').' SET name = ?, email = ? WHERE id = ?');
            $upd->execute([$name !== '' ? $name : null, $email, $userId]);
        } else {
            $upd = $pdo->prepare('UPDATE '.qtbl('users').' SET email = ? WHERE id = ?');
            $upd->execute([$email, $userId]);
        }
        return [true, $changed ? 'Konto wurde aktualisiert.' : 'Keine Änderung erkannt.'];
    } catch (PDOException $e) {
        if (($e->errorInfo[1] ?? null) == 1062) return [false, 'Diese E-Mail-Adresse wird bereits verwendet.'];
        return [false, 'Aktualisierung fehlgeschlagen.'];
    }
}
function update_account_email(PDO $pdo, int $userId, string $email): array {
    return update_account_profile($pdo, $userId, $email, '', '');
}
function delete_user_completely(PDO $pdo, int $userId): void {
    delete_user_logos($pdo, $userId);
    $pdo->beginTransaction();
    $delScans = $pdo->prepare('DELETE s FROM '.qtbl('qr_scans').' s INNER JOIN '.qtbl('qr_codes').' q ON q.id = s.qr_code_id WHERE q.user_id = ?');
    $delScans->execute([$userId]);
    $delQr = $pdo->prepare('DELETE FROM '.qtbl('qr_codes').' WHERE user_id = ?');
    $delQr->execute([$userId]);
    $delUser = $pdo->prepare('DELETE FROM '.qtbl('users').' WHERE id = ?');
    $delUser->execute([$userId]);
    $pdo->commit();
}
function delete_account(PDO $pdo, int $userId, string $password): bool {
    $stmt = $pdo->prepare('SELECT password_hash, role FROM '.qtbl('users').' WHERE id = ?');
    $stmt->execute([$userId]);
    $user = $stmt->fetch();
    if (!$user || !password_verify($password, $user['password_hash'])) return false;
    if (($user['role'] ?? 'user') === 'superadmin' && superadmin_count($pdo) <= 1) return false;
    delete_user_completely($pdo, $userId);
    logout_user();
    return true;
}
function set_user_role(PDO $pdo, int $targetUserId, string $role, int $actorUserId): array {
    $role = $role === 'superadmin' ? 'superadmin' : 'user';
    $stmt = $pdo->prepare('SELECT id, role FROM '.qtbl('users').' WHERE id = ?');
    $stmt->execute([$targetUserId]);
    $target = $stmt->fetch();
    if (!$target) return [false, 'Benutzer nicht gefunden.'];
    if ((int)$target['id'] === $actorUserId && ($target['role'] ?? '') === 'superadmin' && $role === 'user' && superadmin_count($pdo) <= 1) {
        return [false, 'Der letzte Superadmin kann nicht zum normalen Benutzer herabgestuft werden.'];
    }
    $upd = $pdo->prepare('UPDATE '.qtbl('users').' SET role = ? WHERE id = ?');
    $upd->execute([$role, $targetUserId]);
    return [true, 'Rolle wurde aktualisiert.'];
}
function change_password(PDO $pdo, int $userId, string $currentPassword, string $newPassword): bool {
    if (!password_is_strong($newPassword)) return false;
    $stmt = $pdo->prepare('SELECT password_hash FROM '.qtbl('users').' WHERE id = ?');
    $stmt->execute([$userId]);
    $user = $stmt->fetch();
    if (!$user || !password_verify($currentPassword, $user['password_hash'])) return false;
    $upd = $pdo->prepare('UPDATE '.qtbl('users').' SET password_hash = ?, reset_token_hash = NULL, reset_expires_at = NULL WHERE id = ?');
    $upd->execute([password_hash($newPassword, PASSWORD_DEFAULT), $userId]);
    return true;
}
function create_reset_token(PDO $pdo, string $email): ?string {
    $stmt = $pdo->prepare('SELECT id FROM '.qtbl('users').' WHERE email = ?');
    $stmt->execute([strtolower(trim($email))]);
    $user = $stmt->fetch();
    if (!$user) return null;
    $token = bin2hex(random_bytes(32));
    $hash = hash('sha256', $token);
    $upd = $pdo->prepare('UPDATE '.qtbl('users').' SET reset_token_hash = ?, reset_expires_at = DATE_ADD(NOW(), INTERVAL 1 HOUR), reset_requested_at = NOW() WHERE id = ?');
    $upd->execute([$hash, $user['id']]);
    return $token;
}
function find_user_by_reset_token(PDO $pdo, string $token): ?array {
    if (!preg_match('/^[a-f0-9]{64}$/', $token)) return null;
    $hash = hash('sha256', $token);
    $stmt = $pdo->prepare('SELECT id, email FROM '.qtbl('users').' WHERE reset_token_hash = ? AND reset_expires_at > NOW()');
    $stmt->execute([$hash]);
    return $stmt->fetch() ?: null;
}
function reset_password(PDO $pdo, string $token, string $newPassword): bool {
    if (!password_is_strong($newPassword)) return false;
    $user = find_user_by_reset_token($pdo, $token);
    if (!$user) return false;
    $upd = $pdo->prepare('UPDATE '.qtbl('users').' SET password_hash = ?, reset_token_hash = NULL, reset_expires_at = NULL WHERE id = ?');
    $upd->execute([password_hash($newPassword, PASSWORD_DEFAULT), $user['id']]);
    return true;
}

// v14 helpers: shared QR permissions for non-layout endpoints and email verification.
if (!function_exists('qr_access_where')) {
function qr_access_where(string $alias='q'): string { return '('.$alias.'.user_id=? OR EXISTS (SELECT 1 FROM '.qtbl('qr_group_shares').' sh WHERE sh.group_id='.$alias.'.group_id AND sh.user_id=?))'; }
}
if (!function_exists('qr_edit_where')) {
function qr_edit_where(string $alias='q'): string { return '('.$alias.'.user_id=? OR EXISTS (SELECT 1 FROM '.qtbl('qr_group_shares').' sh WHERE sh.group_id='.$alias.'.group_id AND sh.user_id=? AND sh.access_role="edit"))'; }
}
function auth_user_has_column(PDO $pdo, string $column): bool {
    return column_exists($pdo, tbl('users'), $column);
}
function create_email_verification_token(PDO $pdo, int $userId): string {
    $token = bin2hex(random_bytes(32));
    $hash = hash('sha256', $token);
    $stmt = $pdo->prepare('UPDATE '.qtbl('users').' SET verification_token_hash=?, verification_expires_at=DATE_ADD(NOW(), INTERVAL 24 HOUR) WHERE id=?');
    $stmt->execute([$hash, $userId]);
    return $token;
}
function verify_email_token(PDO $pdo, string $token): bool {
    if (!preg_match('/^[a-f0-9]{64}$/', $token)) return false;
    $hash = hash('sha256', $token);
    $stmt = $pdo->prepare('SELECT id FROM '.qtbl('users').' WHERE verification_token_hash=? AND verification_expires_at > NOW() AND email_verified_at IS NULL');
    $stmt->execute([$hash]);
    $id = $stmt->fetchColumn();
    if (!$id) return false;
    $upd = $pdo->prepare('UPDATE '.qtbl('users').' SET email_verified_at=NOW(), verification_token_hash=NULL, verification_expires_at=NULL WHERE id=?');
    $upd->execute([(int)$id]);
    return true;
}
function resend_verification(PDO $pdo, array $config, string $email): bool {
    $stmt = $pdo->prepare('SELECT id,email,email_verified_at FROM '.qtbl('users').' WHERE email=?');
    $stmt->execute([strtolower(trim($email))]);
    $u = $stmt->fetch();
    if (!$u || !empty($u['email_verified_at'])) return true;
    $token = create_email_verification_token($pdo, (int)$u['id']);
    return mailer_send_email_verification($config, $u['email'], base_url($config).'/admin/verify-email.php?token='.$token);
}
