-- v1.1.8 User Management — app-user sessions.
--
-- Distinct from `admin_sessions`. Mirror the schema shape (token_hash
-- PK, user FK cascading) but add:
--
--   * app_id   — every v1.1+ data-plane table starts with the app_id
--                FK so cross-app isolation is bright at the SQL level
--                and ON DELETE CASCADE wipes sessions when an app is
--                deleted (in addition to the user-FK cascade).
--   * absolute_expires_at — hard cap on the sliding window. The
--                application caps new_expires_at at this value on each
--                touch; beyond it, force re-login.
--   * revoked_at — explicit revocation (admin "revoke all sessions"
--                button, password reset, logout). The lookup query
--                rejects revoked rows so a revoked session is dead
--                immediately, before the GC sweep runs.
--
-- `token_hash` stores SHA-256(raw_token) as hex; the raw lives only in
-- the script's return value from `users::login` / `users::accept_invite`
-- and the realtime subscriber's Authorization header.

CREATE TABLE app_user_sessions (
    token_hash           TEXT PRIMARY KEY,
    app_id               UUID NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
    user_id              UUID NOT NULL REFERENCES app_users(id) ON DELETE CASCADE,
    created_at           TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    last_used_at         TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    expires_at           TIMESTAMPTZ NOT NULL,
    absolute_expires_at  TIMESTAMPTZ NOT NULL,
    revoked_at           TIMESTAMPTZ
);

CREATE INDEX idx_app_user_sessions_user ON app_user_sessions (app_id, user_id);
CREATE INDEX idx_app_user_sessions_expiry
    ON app_user_sessions (expires_at) WHERE revoked_at IS NULL;
