-- Migration: 001_create_messenger_schema
-- Description: Create Messenger module tables (Matrix-aware)
-- Date: 2025-11-24

-- ============================================================================
-- CHANNELS (Matrix rooms wrapper)
-- ============================================================================

CREATE TABLE channels (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  slug            TEXT NOT NULL,
  name            TEXT NOT NULL,
  description     TEXT,
  
  -- MicroDAO context
  microdao_id     TEXT NOT NULL,           -- microdao:7
  team_id         UUID NULL,               -- FK to teams table (if exists)
  
  -- Matrix integration
  matrix_room_id  TEXT NOT NULL UNIQUE,    -- !roomid:daarion.city
  matrix_version  TEXT DEFAULT '10',        -- Matrix room version
  
  -- Visibility and access
  visibility      TEXT NOT NULL DEFAULT 'microdao',  -- public | private | microdao
  is_direct       BOOLEAN NOT NULL DEFAULT false,    -- DM channel
  is_encrypted    BOOLEAN NOT NULL DEFAULT false,    -- E2EE enabled
  
  -- Metadata
  topic           TEXT,
  avatar_url      TEXT,                     -- mxc:// URL
  
  -- Audit
  created_by      TEXT NOT NULL,            -- user:... | agent:...
  created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
  archived_at     TIMESTAMPTZ NULL,
  
  -- Constraints
  CONSTRAINT channels_slug_microdao_unique UNIQUE (slug, microdao_id),
  CONSTRAINT channels_visibility_check CHECK (visibility IN ('public', 'private', 'microdao'))
);

-- Indexes
CREATE INDEX channels_microdao_idx ON channels(microdao_id);
CREATE INDEX channels_matrix_room_idx ON channels(matrix_room_id);
CREATE INDEX channels_created_at_idx ON channels(created_at DESC);
CREATE INDEX channels_visibility_idx ON channels(visibility) WHERE archived_at IS NULL;

COMMENT ON TABLE channels IS 'DAARION channels mapped to Matrix rooms';
COMMENT ON COLUMN channels.matrix_room_id IS 'Matrix room ID (!roomid:server)';
COMMENT ON COLUMN channels.visibility IS 'public = city-wide, microdao = microDAO members only, private = invited only';

-- ============================================================================
-- MESSAGES (Index of Matrix events, not primary storage)
-- ============================================================================

CREATE TABLE messages (
  id                UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  channel_id        UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
  
  -- Matrix event
  matrix_event_id   TEXT NOT NULL UNIQUE,   -- $event:server
  matrix_type       TEXT NOT NULL,           -- m.room.message, m.reaction, etc
  
  -- Sender
  sender_id         TEXT NOT NULL,           -- user:... | agent:...
  sender_type       TEXT NOT NULL,           -- human | agent
  sender_matrix_id  TEXT NOT NULL,           -- @user:server
  
  -- Content (indexed copy, full content in Matrix)
  content_preview   TEXT NOT NULL,           -- truncated plaintext
  content_type      TEXT NOT NULL DEFAULT 'text',  -- text | image | file | audio | video
  
  -- Threading
  thread_id         UUID NULL REFERENCES messages(id),  -- reply to message
  
  -- Metadata
  edited_at         TIMESTAMPTZ NULL,
  deleted_at        TIMESTAMPTZ NULL,
  created_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
  
  -- Constraints
  CONSTRAINT messages_sender_type_check CHECK (sender_type IN ('human', 'agent'))
);

-- Indexes
CREATE INDEX messages_channel_created_idx ON messages(channel_id, created_at DESC);
CREATE INDEX messages_sender_idx ON messages(sender_id);
CREATE INDEX messages_thread_idx ON messages(thread_id) WHERE thread_id IS NOT NULL;
CREATE INDEX messages_matrix_event_idx ON messages(matrix_event_id);

COMMENT ON TABLE messages IS 'Index of Matrix events, full content stored in Matrix';
COMMENT ON COLUMN messages.content_preview IS 'Truncated/plaintext summary for quick access';
COMMENT ON COLUMN messages.matrix_event_id IS 'Matrix event ID ($eventid:server)';

-- ============================================================================
-- CHANNEL_MEMBERS (Permissions and roles)
-- ============================================================================

CREATE TABLE channel_members (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  channel_id      UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
  
  -- Member identity
  member_id       TEXT NOT NULL,            -- user:... | agent:...
  member_type     TEXT NOT NULL,            -- human | agent
  matrix_user_id  TEXT NOT NULL,            -- @user:server | @agent:server
  
  -- Role
  role            TEXT NOT NULL DEFAULT 'member',  -- owner | admin | member | guest | agent
  
  -- Capabilities (DAARION-specific)
  can_read        BOOLEAN NOT NULL DEFAULT true,
  can_write       BOOLEAN NOT NULL DEFAULT true,
  can_invite      BOOLEAN NOT NULL DEFAULT false,
  can_kick        BOOLEAN NOT NULL DEFAULT false,
  can_create_tasks BOOLEAN NOT NULL DEFAULT false,
  
  -- Matrix power level
  matrix_power_level INTEGER NOT NULL DEFAULT 0,  -- 0-100
  
  -- Audit
  invited_by      TEXT NULL,                -- who invited
  joined_at       TIMESTAMPTZ NOT NULL DEFAULT now(),
  left_at         TIMESTAMPTZ NULL,
  
  -- Constraints
  CONSTRAINT channel_members_unique UNIQUE (channel_id, member_id),
  CONSTRAINT channel_members_type_check CHECK (member_type IN ('human', 'agent')),
  CONSTRAINT channel_members_role_check CHECK (role IN ('owner', 'admin', 'member', 'guest', 'agent'))
);

-- Indexes
CREATE INDEX channel_members_channel_idx ON channel_members(channel_id) WHERE left_at IS NULL;
CREATE INDEX channel_members_member_idx ON channel_members(member_id) WHERE left_at IS NULL;

COMMENT ON TABLE channel_members IS 'Channel membership and permissions';
COMMENT ON COLUMN channel_members.matrix_power_level IS 'Matrix room power level (0=user, 50=moderator, 100=admin)';

-- ============================================================================
-- MESSAGE_REACTIONS (Emoji reactions, mapped to Matrix reactions)
-- ============================================================================

CREATE TABLE message_reactions (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  message_id      UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
  
  -- Reactor
  reactor_id      TEXT NOT NULL,            -- user:... | agent:...
  reactor_type    TEXT NOT NULL,            -- human | agent
  
  -- Reaction
  emoji           TEXT NOT NULL,            -- 👍, ❤️, etc
  matrix_event_id TEXT NOT NULL,            -- $reaction_event:server
  
  -- Audit
  created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
  removed_at      TIMESTAMPTZ NULL,
  
  -- Constraints
  CONSTRAINT message_reactions_unique UNIQUE (message_id, reactor_id, emoji),
  CONSTRAINT message_reactions_type_check CHECK (reactor_type IN ('human', 'agent'))
);

-- Indexes
CREATE INDEX message_reactions_message_idx ON message_reactions(message_id) WHERE removed_at IS NULL;

COMMENT ON TABLE message_reactions IS 'Message reactions (mapped to m.reaction events)';

-- ============================================================================
-- CHANNEL_EVENTS (Audit log of channel actions)
-- ============================================================================

CREATE TABLE channel_events (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  channel_id      UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
  
  -- Event
  event_type      TEXT NOT NULL,            -- channel.created, member.joined, member.left, etc
  actor_id        TEXT NOT NULL,            -- who did it
  target_id       TEXT NULL,                -- who/what was affected
  
  -- Payload
  metadata        JSONB NULL,
  
  -- Audit
  created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
  
  -- Constraints
  CONSTRAINT channel_events_type_check CHECK (
    event_type IN (
      'channel.created', 'channel.updated', 'channel.archived',
      'member.joined', 'member.left', 'member.invited', 'member.kicked',
      'member.role_changed', 'message.pinned', 'message.unpinned'
    )
  )
);

-- Indexes
CREATE INDEX channel_events_channel_idx ON channel_events(channel_id, created_at DESC);
CREATE INDEX channel_events_type_idx ON channel_events(event_type);

COMMENT ON TABLE channel_events IS 'Audit log of channel-level actions';

-- ============================================================================
-- Functions and Triggers
-- ============================================================================

-- Update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = now();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER channels_updated_at
  BEFORE UPDATE ON channels
  FOR EACH ROW
  EXECUTE FUNCTION update_updated_at();

-- ============================================================================
-- Seed Data (Example channels for DAARION.city)
-- ============================================================================

-- DAARION.city MicroDAO (assuming it exists as microdao:daarion)
INSERT INTO channels (slug, name, description, microdao_id, matrix_room_id, visibility, created_by)
VALUES 
  ('general', 'General', 'Main DAARION.city channel', 'microdao:daarion', '!general:daarion.city', 'public', 'system:daarion'),
  ('announcements', 'Announcements', 'Official announcements', 'microdao:daarion', '!announcements:daarion.city', 'public', 'system:daarion'),
  ('agent-hub', 'Agent Hub', 'Chat with Team Assistant', 'microdao:daarion', '!agent-hub:daarion.city', 'microdao', 'system:daarion')
ON CONFLICT DO NOTHING;

COMMENT ON SCHEMA public IS 'Messenger schema v1 - Matrix-aware implementation';






