-- name: GetUserByUsername :one
SELECT id, username, password, avatar, role_id, totp_secret, status,
       created_at, last_seen, banned, ban_reason, ban_expires, identity_public_key,
       display_name, about, custom_status
FROM users WHERE username = ? COLLATE NOCASE;

-- name: GetUserByID :one
SELECT id, username, password, avatar, role_id, totp_secret, status,
       created_at, last_seen, banned, ban_reason, ban_expires, identity_public_key,
       display_name, about, custom_status
FROM users WHERE id = ?;

-- name: CreateUser :execresult
INSERT INTO users (username, password, role_id) VALUES (?, ?, ?);

-- name: UpdateUserStatus :exec
UPDATE users SET status = ?, last_seen = datetime('now') WHERE id = ?;

-- name: UpdateUserTOTPSecret :exec
UPDATE users SET totp_secret = ? WHERE id = ?;

-- name: UpdateUserIdentityKey :exec
UPDATE users SET identity_public_key = ? WHERE id = ?;

-- name: MarkUserDisconnected :exec
-- Disconnect bookkeeping. It clears only 'online', which is the one status
-- that means "has a live session"; idle, dnd and invisible are choices the
-- user made and are what the next connect reads instead of stamping online
-- (db.ConnectStatus). A stale choice never renders as "present" because the
-- read path treats a member with no live connection as offline regardless.
UPDATE users
SET status = CASE WHEN status = 'online' THEN 'offline' ELSE status END,
    last_seen = datetime('now')
WHERE id = ?;

-- name: ResetAllUserStatuses :exec
-- Startup reset: nothing is connected yet, so every 'online' is a leftover
-- from the previous process. Chosen statuses survive for the same reason they
-- survive a disconnect.
UPDATE users SET status = 'offline' WHERE status = 'online';

-- name: BanUser :exec
UPDATE users SET banned = 1, ban_reason = ?, ban_expires = ? WHERE id = ?;

-- name: UnbanUser :exec
UPDATE users SET banned = 0, ban_reason = NULL, ban_expires = NULL WHERE id = ?;

-- The ready payload's member roster. docs/protocol.md documents members[] as
-- "All registered users", so this must not silently truncate: the previous
-- LIMIT 1000 dropped every member past the first thousand with no has_more
-- signal, leaving those users unrenderable and unmentionable on the client
-- with nothing to indicate the list was incomplete.
-- name: ListMembers :many
SELECT u.id, u.username, u.avatar, u.status, LOWER(r.name), u.identity_public_key,
       u.display_name, u.custom_status
FROM users u
JOIN roles r ON u.role_id = r.id
WHERE (u.banned = 0 OR (u.ban_expires IS NOT NULL AND replace(u.ban_expires, ' ', 'T') <= strftime('%Y-%m-%dT%H:%M:%SZ', 'now')))
ORDER BY u.username ASC;

-- name: CountUsers :one
SELECT COUNT(*) FROM users;

-- name: CountUsersWithoutTOTP :one
SELECT COUNT(*) FROM users WHERE banned = 0 AND totp_secret IS NULL;
