CREATE TABLE IF NOT EXISTS users ( id uuid PRIMARY KEY, email text NOT NULL UNIQUE, nick text NOT NULL UNIQUE, data jsonb NOT NULL ); CREATE TABLE IF NOT EXISTS sessions ( hash text PRIMARY KEY, user_id uuid NOT NULL REFERENCES users(id), expires_at timestamptz NOT NULL ); CREATE INDEX IF NOT EXISTS sessions_expiry ON sessions(expires_at); CREATE TABLE IF NOT EXISTS rooms ( id uuid PRIMARY KEY, code text NOT NULL UNIQUE, game_id text NOT NULL, game_version text NOT NULL, visibility text NOT NULL CHECK (visibility IN ('public','private')), status text NOT NULL CHECK (status IN ('waiting','playing','finished')), created_at timestamptz NOT NULL, data jsonb NOT NULL ); CREATE INDEX IF NOT EXISTS rooms_status ON rooms(status,created_at DESC); CREATE INDEX IF NOT EXISTS rooms_players ON rooms USING gin ((data->'players')); CREATE TABLE IF NOT EXISTS match_events ( room_id uuid NOT NULL REFERENCES rooms(id), sequence integer NOT NULL, action_id text NOT NULL, actor_id uuid NOT NULL REFERENCES users(id), data jsonb NOT NULL, PRIMARY KEY(room_id,sequence), UNIQUE(room_id,action_id) ); CREATE TABLE IF NOT EXISTS chat_messages ( id uuid PRIMARY KEY, room_id uuid NOT NULL REFERENCES rooms(id), user_id uuid NOT NULL REFERENCES users(id), data jsonb NOT NULL ); CREATE INDEX IF NOT EXISTS chat_room ON chat_messages(room_id); CREATE TABLE IF NOT EXISTS match_results ( room_id uuid NOT NULL REFERENCES rooms(id), user_id uuid NOT NULL REFERENCES users(id), outcome text NOT NULL CHECK (outcome IN ('win','loss','draw')), placement integer NOT NULL CHECK (placement>0), reason text NOT NULL, PRIMARY KEY(room_id,user_id) ); CREATE INDEX IF NOT EXISTS results_user ON match_results(user_id);