CREATE TABLE channels (
    id BYTEA NOT NULL PRIMARY KEY CHECK(length(id) = 26),
    guild_id BYTEA  CHECK(length(guild_id) = 26),  -- NULL for DMs & group DMs
    parent_id BYTEA CHECK(length(parent_id) = 26),  -- for thread categories / channel groups

    -- Core identity & type
    name TEXT NOT NULL CHECK (LENGTH(name) >= 1 AND LENGTH(name) <= 100),
    channel_type INTEGER NOT NULL DEFAULT 0,  -- 0 = text, 1 = voice, 2 = category, 5 = news, etc.

    -- Permissions & visibility
    position INTEGER NOT NULL DEFAULT 0,
    permission_overwrites JSONB NOT NULL DEFAULT '[]'::jsonb,  -- array of overwrites
    rate_limit_per_user INTEGER NOT NULL DEFAULT 0,  -- slowmode in seconds (0–21600)

    -- NSFW & visibility flags
    nsfw BOOLEAN NOT NULL DEFAULT FALSE,
    loud BOOLEAN NOT NULL DEFAULT FALSE,  -- voice channel: triggers notifications

    -- Thread-specific fields (for threads spawned from messages)
    thread_metadata JSONB,  -- { "archived": bool, "auto_archive_duration": int, "archive_timestamp": timestamptz, "locked": bool }
    member_count INTEGER NOT NULL DEFAULT 0,  -- approximate member count (not real-time)
    message_count INTEGER NOT NULL DEFAULT 0,  -- cached message count (for UI previews)
    thread_owner_id BYTEA   CHECK(length(thread_owner_id) = 26),

    -- System metadata
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- Topic (for news, text channels)
    topic TEXT CHECK (LENGTH(COALESCE(topic, '')) <= 1024),

    -- Voice-specific
    user_limit INTEGER NOT NULL DEFAULT 0,  -- 0 = no limit
    region TEXT  -- voice region (e.g., "us-west"), NULL = automatic
);

-- Critical indexes for common operations
CREATE INDEX idx_channels_guild_id ON channels (guild_id) WHERE guild_id IS NOT NULL;
CREATE INDEX idx_channels_guild_id_position ON channels (guild_id, position);
CREATE INDEX idx_channels_parent_id ON channels (parent_id) WHERE parent_id IS NOT NULL;
CREATE INDEX idx_channels_channel_type ON channels (channel_type);
CREATE UNIQUE INDEX uidx_channels_channel_pos ON channels (guild_id, position);

-- Optional: cover index for message listing (join + sort)
CREATE INDEX idx_channels_guild_id_created_at ON channels (guild_id, created_at) WHERE guild_id IS NOT NULL;