-- Migration 049: Human Memory Model v3.0 for Helion
-- Full identity, roles, session state, and organizational memory schema
-- Created: 2026-01-17

-- ============================================================================
-- L2: PLATFORM IDENTITY & ROLES (PIR)
-- ============================================================================

-- Global platform users (cross-channel identity)
CREATE TABLE IF NOT EXISTS platform_users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    status TEXT DEFAULT 'active', -- active, inactive, banned, deleted
    -- Aggregated profile (derived from channel_identities)
    preferred_language TEXT DEFAULT 'uk',
    timezone TEXT,
    metadata JSONB DEFAULT '{}'
);

-- Channel-specific identities (Telegram, Discord, Email, etc.)
CREATE TABLE IF NOT EXISTS channel_identities (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    platform_user_id UUID NOT NULL REFERENCES platform_users(id) ON DELETE CASCADE,
    channel TEXT NOT NULL, -- telegram, discord, email, web
    channel_user_id TEXT NOT NULL, -- Telegram from.id, Discord user_id, etc.
    username TEXT, -- @username
    display_name TEXT, -- first_name + last_name
    avatar_url TEXT,
    verified BOOLEAN DEFAULT FALSE,
    first_seen_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    last_seen_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    metadata JSONB DEFAULT '{}', -- channel-specific data
    UNIQUE(channel, channel_user_id)
);

CREATE INDEX IF NOT EXISTS idx_channel_identities_platform_user ON channel_identities(platform_user_id);
CREATE INDEX IF NOT EXISTS idx_channel_identities_channel_user ON channel_identities(channel, channel_user_id);

-- Platform roles (mentor, dev, investor, ops, moderator)
CREATE TABLE IF NOT EXISTS platform_roles (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    code TEXT NOT NULL UNIQUE, -- mentor, developer, investor, ops, moderator, admin
    display_name TEXT NOT NULL,
    scope TEXT NOT NULL DEFAULT 'platform', -- platform, group, thread
    description TEXT,
    permissions JSONB DEFAULT '[]', -- list of permission codes
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- User-role assignments
CREATE TABLE IF NOT EXISTS user_roles (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    platform_user_id UUID NOT NULL REFERENCES platform_users(id) ON DELETE CASCADE,
    role_id UUID NOT NULL REFERENCES platform_roles(id) ON DELETE CASCADE,
    scope_ref TEXT, -- group_id for group-scoped roles, null for platform-wide
    confidence REAL DEFAULT 1.0, -- 0.0-1.0, lower for inferred roles
    assigned_by TEXT, -- 'system', 'admin', 'helion_inferred', user_id
    assigned_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    revoked_at TIMESTAMP WITH TIME ZONE, -- null if active
    notes TEXT,
    UNIQUE(platform_user_id, role_id, scope_ref)
);

CREATE INDEX IF NOT EXISTS idx_user_roles_platform_user ON user_roles(platform_user_id);
CREATE INDEX IF NOT EXISTS idx_user_roles_active ON user_roles(platform_user_id) WHERE revoked_at IS NULL;

-- ============================================================================
-- L1: SESSION STATE MEMORY (SSM)
-- ============================================================================

-- Conversations (chat sessions)
CREATE TABLE IF NOT EXISTS helion_conversations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    channel TEXT NOT NULL, -- telegram, discord, web
    chat_id TEXT NOT NULL, -- Telegram chat_id, Discord channel_id
    thread_id TEXT, -- Optional thread within chat
    platform_user_id UUID REFERENCES platform_users(id), -- Primary user (for DMs)
    started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    last_activity_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    status TEXT DEFAULT 'active', -- active, archived, deleted
    metadata JSONB DEFAULT '{}',
    UNIQUE(channel, chat_id, thread_id)
);

CREATE INDEX IF NOT EXISTS idx_conversations_chat ON helion_conversations(channel, chat_id);
CREATE INDEX IF NOT EXISTS idx_conversations_user ON helion_conversations(platform_user_id);

-- Session state (SSM) - per conversation
CREATE TABLE IF NOT EXISTS helion_conversation_state (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    conversation_id UUID NOT NULL REFERENCES helion_conversations(id) ON DELETE CASCADE,
    
    -- Addressing state
    last_addressed_to_helion BOOLEAN DEFAULT FALSE,
    last_user_id TEXT,
    last_user_nick TEXT,
    
    -- Topic/context tracking
    active_topic_id TEXT,
    active_context_open BOOLEAN DEFAULT FALSE,
    closed_context_ids TEXT[] DEFAULT '{}',
    
    -- Media handling
    last_media_id TEXT,
    last_media_type TEXT,
    last_media_handled BOOLEAN DEFAULT FALSE,
    
    -- Anti-repeat mechanism
    last_answer_fingerprint TEXT,
    last_answer_at TIMESTAMP WITH TIME ZONE,
    
    -- Group settings
    group_trust_mode BOOLEAN DEFAULT FALSE,
    apprentice_mode BOOLEAN DEFAULT FALSE,
    
    -- Proactive question limits
    proactive_questions_today INTEGER DEFAULT 0,
    last_proactive_question_at TIMESTAMP WITH TIME ZONE,
    proactive_question_reset_date DATE DEFAULT CURRENT_DATE,
    
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    state_json JSONB DEFAULT '{}', -- Additional flexible state
    
    UNIQUE(conversation_id)
);

CREATE INDEX IF NOT EXISTS idx_conversation_state_conv ON helion_conversation_state(conversation_id);

-- Media index (for tracking processed media)
CREATE TABLE IF NOT EXISTS helion_media_index (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    conversation_id UUID NOT NULL REFERENCES helion_conversations(id) ON DELETE CASCADE,
    message_id TEXT NOT NULL,
    media_type TEXT NOT NULL, -- photo, voice, video, document, video_note
    file_id TEXT NOT NULL,
    file_hash TEXT, -- For deduplication
    handled BOOLEAN DEFAULT FALSE,
    handled_at TIMESTAMP WITH TIME ZONE,
    result_summary TEXT, -- Brief summary of what was extracted
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    UNIQUE(conversation_id, file_id)
);

CREATE INDEX IF NOT EXISTS idx_media_index_conv ON helion_media_index(conversation_id);
CREATE INDEX IF NOT EXISTS idx_media_index_file ON helion_media_index(file_id);

-- ============================================================================
-- L3: ORGANIZATIONAL MEMORY (OM)
-- ============================================================================

-- Extended memory items (long-term facts)
CREATE TABLE IF NOT EXISTS helion_memory_items (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    platform_user_id UUID REFERENCES platform_users(id) ON DELETE SET NULL,
    
    -- Memory classification
    type TEXT NOT NULL, -- preference, decision, agreement, profile_fact, mentor_lesson, project_fact
    category TEXT, -- personal, technical, organizational, stylistic
    
    -- Content
    text TEXT NOT NULL,
    summary TEXT, -- Short summary for retrieval brief
    
    -- Source tracking
    source_ref TEXT, -- conversation_id, message_id, or external source
    source_type TEXT, -- conversation, manual, import, inferred
    
    -- Confidence and verification
    confidence REAL DEFAULT 0.7, -- 0.0-1.0
    verified BOOLEAN DEFAULT FALSE,
    verified_by TEXT,
    verified_at TIMESTAMP WITH TIME ZONE,
    
    -- Visibility and scope
    visibility TEXT DEFAULT 'platform', -- private_dm, group_only, platform
    scope_ref TEXT, -- group_id if group_only
    
    -- Lifecycle
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    expires_at TIMESTAMP WITH TIME ZONE, -- null = never expires
    archived_at TIMESTAMP WITH TIME ZONE,
    
    -- Vector reference
    embedding_id TEXT, -- Qdrant point ID
    
    metadata JSONB DEFAULT '{}'
);

CREATE INDEX IF NOT EXISTS idx_memory_items_user ON helion_memory_items(platform_user_id);
CREATE INDEX IF NOT EXISTS idx_memory_items_type ON helion_memory_items(type);
CREATE INDEX IF NOT EXISTS idx_memory_items_visibility ON helion_memory_items(visibility);
CREATE INDEX IF NOT EXISTS idx_memory_items_active ON helion_memory_items(platform_user_id) 
    WHERE archived_at IS NULL AND (expires_at IS NULL OR expires_at > NOW());

-- Memory events (audit log)
CREATE TABLE IF NOT EXISTS helion_memory_events (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    platform_user_id UUID REFERENCES platform_users(id),
    memory_item_id UUID REFERENCES helion_memory_items(id) ON DELETE SET NULL,
    event_type TEXT NOT NULL, -- add, update, verify, revoke, retrieve, archive
    actor TEXT NOT NULL, -- user, helion, admin, system
    actor_ref TEXT, -- user_id or service name
    payload_json JSONB DEFAULT '{}',
    ts TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_memory_events_user ON helion_memory_events(platform_user_id);
CREATE INDEX IF NOT EXISTS idx_memory_events_item ON helion_memory_events(memory_item_id);
CREATE INDEX IF NOT EXISTS idx_memory_events_ts ON helion_memory_events(ts);

-- ============================================================================
-- MENTORS & TRUSTED GROUPS (Updated from v2.3)
-- ============================================================================

-- Mentors table (platform-wide or group-specific)
CREATE TABLE IF NOT EXISTS helion_mentors (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    platform_user_id UUID REFERENCES platform_users(id),
    scope TEXT DEFAULT 'platform', -- platform, group
    scope_ref TEXT, -- group chat_id if group-scoped
    
    -- Telegram info (for matching)
    telegram_user_id TEXT,
    telegram_username TEXT,
    display_name TEXT,
    phone_hash TEXT, -- Hashed phone for matching (privacy)
    
    -- Status
    confidence TEXT DEFAULT 'configured', -- configured, confirmed, inferred
    active BOOLEAN DEFAULT TRUE,
    
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    
    UNIQUE(platform_user_id, scope, scope_ref)
);

CREATE INDEX IF NOT EXISTS idx_mentors_telegram ON helion_mentors(telegram_user_id);
CREATE INDEX IF NOT EXISTS idx_mentors_username ON helion_mentors(telegram_username);

-- Trusted groups/chats
CREATE TABLE IF NOT EXISTS helion_trusted_groups (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    channel TEXT NOT NULL DEFAULT 'telegram',
    chat_id TEXT NOT NULL,
    chat_username TEXT, -- @energyunionofficial
    chat_title TEXT,
    
    -- Settings
    trust_mode BOOLEAN DEFAULT TRUE,
    apprentice_mode BOOLEAN DEFAULT TRUE,
    auto_respond BOOLEAN DEFAULT FALSE, -- Respond even without mention
    
    -- Limits
    proactive_questions_per_day INTEGER DEFAULT 3,
    
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    
    UNIQUE(channel, chat_id)
);

-- ============================================================================
-- INITIAL DATA
-- ============================================================================

-- Insert default roles
INSERT INTO platform_roles (code, display_name, scope, description) VALUES
    ('admin', 'Administrator', 'platform', 'Full platform access'),
    ('mentor', 'Mentor', 'platform', 'Can teach Helion, elevated trust'),
    ('developer', 'Developer', 'platform', 'Technical team member'),
    ('investor', 'Investor', 'platform', 'Token holder or investor'),
    ('ops', 'Operations', 'platform', 'Operations team member'),
    ('moderator', 'Moderator', 'group', 'Group moderator'),
    ('member', 'Member', 'platform', 'Regular platform member')
ON CONFLICT (code) DO NOTHING;

-- Insert known mentors
INSERT INTO helion_mentors (telegram_username, display_name, confidence) VALUES
    ('@ivantytar', 'Іван Титар', 'configured'),
    ('@archenvis', 'Александр Вертій', 'configured'),
    ('@olegarch88', 'Олег Ковальчук', 'configured')
ON CONFLICT DO NOTHING;

-- Insert trusted groups
INSERT INTO helion_trusted_groups (channel, chat_id, chat_username, chat_title, trust_mode, apprentice_mode) VALUES
    ('telegram', '-1001234567890', '@energyunionofficial', 'Energy Union Official', TRUE, TRUE),
    ('telegram', '-1009876543210', '@energyunionteam', 'Energy Union Team', TRUE, TRUE)
ON CONFLICT (channel, chat_id) DO UPDATE SET 
    chat_username = EXCLUDED.chat_username,
    chat_title = EXCLUDED.chat_title;

-- ============================================================================
-- HELPER FUNCTIONS
-- ============================================================================

-- Function to resolve or create platform user from channel identity
CREATE OR REPLACE FUNCTION resolve_platform_user(
    p_channel TEXT,
    p_channel_user_id TEXT,
    p_username TEXT DEFAULT NULL,
    p_display_name TEXT DEFAULT NULL
) RETURNS UUID AS $$
DECLARE
    v_platform_user_id UUID;
    v_identity_id UUID;
BEGIN
    -- Check if identity exists
    SELECT platform_user_id INTO v_platform_user_id
    FROM channel_identities
    WHERE channel = p_channel AND channel_user_id = p_channel_user_id;
    
    IF v_platform_user_id IS NULL THEN
        -- Create new platform user
        INSERT INTO platform_users (status) VALUES ('active')
        RETURNING id INTO v_platform_user_id;
        
        -- Create channel identity
        INSERT INTO channel_identities (platform_user_id, channel, channel_user_id, username, display_name)
        VALUES (v_platform_user_id, p_channel, p_channel_user_id, p_username, p_display_name);
    ELSE
        -- Update last seen and username if changed
        UPDATE channel_identities
        SET last_seen_at = NOW(),
            username = COALESCE(p_username, username),
            display_name = COALESCE(p_display_name, display_name)
        WHERE channel = p_channel AND channel_user_id = p_channel_user_id;
    END IF;
    
    RETURN v_platform_user_id;
END;
$$ LANGUAGE plpgsql;

-- Function to get or create conversation
CREATE OR REPLACE FUNCTION get_or_create_conversation(
    p_channel TEXT,
    p_chat_id TEXT,
    p_thread_id TEXT DEFAULT NULL,
    p_platform_user_id UUID DEFAULT NULL
) RETURNS UUID AS $$
DECLARE
    v_conversation_id UUID;
BEGIN
    SELECT id INTO v_conversation_id
    FROM helion_conversations
    WHERE channel = p_channel 
      AND chat_id = p_chat_id 
      AND COALESCE(thread_id, '') = COALESCE(p_thread_id, '');
    
    IF v_conversation_id IS NULL THEN
        INSERT INTO helion_conversations (channel, chat_id, thread_id, platform_user_id)
        VALUES (p_channel, p_chat_id, p_thread_id, p_platform_user_id)
        RETURNING id INTO v_conversation_id;
    ELSE
        UPDATE helion_conversations
        SET last_activity_at = NOW()
        WHERE id = v_conversation_id;
    END IF;
    
    RETURN v_conversation_id;
END;
$$ LANGUAGE plpgsql;

-- Function to check if user is mentor
CREATE OR REPLACE FUNCTION is_mentor(
    p_telegram_user_id TEXT DEFAULT NULL,
    p_telegram_username TEXT DEFAULT NULL
) RETURNS BOOLEAN AS $$
BEGIN
    RETURN EXISTS (
        SELECT 1 FROM helion_mentors
        WHERE active = TRUE
          AND (telegram_user_id = p_telegram_user_id OR telegram_username = p_telegram_username)
    );
END;
$$ LANGUAGE plpgsql;

-- Function to check if chat is trusted
CREATE OR REPLACE FUNCTION is_trusted_group(
    p_channel TEXT,
    p_chat_id TEXT
) RETURNS BOOLEAN AS $$
BEGIN
    RETURN EXISTS (
        SELECT 1 FROM helion_trusted_groups
        WHERE channel = p_channel AND chat_id = p_chat_id AND trust_mode = TRUE
    );
END;
$$ LANGUAGE plpgsql;

-- ============================================================================
-- COMMENTS
-- ============================================================================

COMMENT ON TABLE platform_users IS 'Global user identity across all channels (L2 PIR)';
COMMENT ON TABLE channel_identities IS 'Channel-specific user identities linked to platform users';
COMMENT ON TABLE platform_roles IS 'Available roles in the platform';
COMMENT ON TABLE user_roles IS 'User role assignments with scope and confidence';
COMMENT ON TABLE helion_conversations IS 'Chat sessions/conversations';
COMMENT ON TABLE helion_conversation_state IS 'Session state memory (L1 SSM) per conversation';
COMMENT ON TABLE helion_media_index IS 'Index of processed media to avoid re-processing';
COMMENT ON TABLE helion_memory_items IS 'Long-term memory facts (L3 OM)';
COMMENT ON TABLE helion_memory_events IS 'Audit log of memory operations';
COMMENT ON TABLE helion_mentors IS 'Configured and confirmed mentors';
COMMENT ON TABLE helion_trusted_groups IS 'Trusted groups with special settings';
