CREATE TABLE guild_members (
    guild_id BYTEA NOT NULL CHECK(length(guild_id) = 26),
    user_id BYTEA NOT NULL CHECK(length(user_id) = 26),

    -- Core identity
    nick TEXT CHECK (LENGTH(COALESCE(nick, '')) <= 32),  -- display name in guild (NULL = uses user global name)
    joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- Roles & permissions
    roles BYTEA[] NOT NULL DEFAULT ARRAY[]::BYTEA[],  -- array of role IDs (26-byte each)
    boosting_since TIMESTAMPTZ,  -- when they started boosting (NULL = not boosting)

    -- Voice state (lightweight caching)
    voice_channel_id BYTEA CHECK(length(voice_channel_id) = 26),
    deafened BOOLEAN NOT NULL DEFAULT FALSE,
    muted BOOLEAN NOT NULL DEFAULT FALSE,

    -- Moderation & management
    pending BOOLEAN NOT NULL DEFAULT FALSE,  -- requires membership screening
    timed_out_until TIMESTAMPTZ,  -- NULL = not timed out

    -- Audit & integrity
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- Composite primary key
    PRIMARY KEY (guild_id, user_id)
);

-- Critical indexes
CREATE INDEX idx_guild_members_user_id ON guild_members (user_id);
CREATE INDEX idx_guild_members_guild_id ON guild_members (guild_id);
CREATE INDEX idx_guild_members_roles ON guild_members USING GIN (roles);  -- for role-based lookups
CREATE INDEX idx_guild_members_voice_channel_id ON guild_members (voice_channel_id) WHERE voice_channel_id IS NOT NULL;

-- For quick "all members in guild" queries (covering index)
CREATE INDEX idx_guild_members_guild_id_nick ON guild_members (guild_id, nick);