-- Phase 4: Community Detection Schema
-- Extends memory_community with label propagation execution and statistics

-- ============================================
-- STEP 1: Create label propagation run tracking
-- ============================================
CREATE TABLE IF NOT EXISTS label_propagation_run (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    project_id VARCHAR(255) NOT NULL,
    run_at TIMESTAMPTZ DEFAULT NOW(),
    algorithm VARCHAR(50) DEFAULT 'label_propagation',
    max_iterations INT DEFAULT 10,
    convergence_threshold FLOAT DEFAULT 0.01,
    iterations_completed INT,
    converged BOOLEAN DEFAULT FALSE,
    
    -- Execution metadata
    status VARCHAR(20) DEFAULT 'running'
        CHECK (status IN ('running', 'completed', 'failed')),
    error_message TEXT,
    duration_ms INT,
    
    -- Statistics
    communities_detected INT,
    communities_merged INT,
    communities_split INT,
    nodes_processed INT,
    edges_processed INT,
    
    -- Execution mode
    dry_run BOOLEAN DEFAULT FALSE,
    
    CONSTRAINT chk_iterations_valid CHECK (iterations_completed >= 0 AND iterations_completed <= max_iterations)
);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_label_prop_run_project 
    ON label_propagation_run(project_id, run_at DESC);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_label_prop_run_status 
    ON label_propagation_run(project_id, status)
    WHERE status IN ('running', 'failed');

-- ============================================
-- STEP 2: Create community member map
-- ============================================
CREATE TABLE IF NOT EXISTS community_member_map (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    project_id VARCHAR(255) NOT NULL,
    community_id UUID NOT NULL REFERENCES memory_community(id) ON DELETE CASCADE,
    entity_id UUID NOT NULL REFERENCES memory_entity(id) ON DELETE CASCADE,
    label_propagation_run_id UUID REFERENCES label_propagation_run(id) ON DELETE SET NULL,
    
    -- Label strength (0-1, higher = stronger membership)
    label_strength FLOAT DEFAULT 1.0,
    
    -- Membership tracking
    is_seed BOOLEAN DEFAULT FALSE,
    joined_at TIMESTAMPTZ DEFAULT NOW(),
    left_at TIMESTAMPTZ,
    
    -- Consistency
    CONSTRAINT uq_community_entity_project UNIQUE (project_id, community_id, entity_id),
    CONSTRAINT chk_label_strength CHECK (label_strength >= 0 AND label_strength <= 1)
);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_member_project 
    ON community_member_map(project_id, community_id);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_entity_lookup 
    ON community_member_map(entity_id, community_id);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_member_strength 
    ON community_member_map(community_id, label_strength DESC)
    WHERE left_at IS NULL;

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_seeds 
    ON community_member_map(project_id, is_seed)
    WHERE is_seed = TRUE;

-- ============================================
-- STEP 3: Create community statistics table
-- ============================================
CREATE TABLE IF NOT EXISTS community_statistics (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    project_id VARCHAR(255) NOT NULL,
    community_id UUID NOT NULL UNIQUE REFERENCES memory_community(id) ON DELETE CASCADE,
    label_propagation_run_id UUID NOT NULL REFERENCES label_propagation_run(id) ON DELETE CASCADE,
    
    -- Membership stats
    member_count INT DEFAULT 0,
    active_member_count INT DEFAULT 0,
    seed_member_count INT DEFAULT 0,
    
    -- Graph structure
    internal_edge_count INT DEFAULT 0,
    external_edge_count INT DEFAULT 0,
    
    -- Cohesion metrics
    density FLOAT DEFAULT 0.0,
    modularity FLOAT DEFAULT 0.0,
    
    -- Edge types within community
    relation_type_distribution JSONB DEFAULT '{}',
    
    -- Temporal metrics
    first_entity_created TIMESTAMPTZ,
    last_entity_accessed TIMESTAMPTZ,
    avg_entity_age_days FLOAT DEFAULT 0.0,
    
    -- Quality scores
    coherence_score FLOAT DEFAULT 0.5,
    stability_score FLOAT DEFAULT 0.5,
    significance_score FLOAT DEFAULT 0.5,
    
    CONSTRAINT chk_stats_nonnegative CHECK (
        member_count >= 0 AND 
        internal_edge_count >= 0 AND 
        external_edge_count >= 0
    ),
    CONSTRAINT chk_stats_bounded CHECK (
        density >= 0 AND density <= 1 AND
        modularity >= -1 AND modularity <= 1 AND
        coherence_score >= 0 AND coherence_score <= 1 AND
        stability_score >= 0 AND stability_score <= 1 AND
        significance_score >= 0 AND significance_score <= 1
    )
);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_stats_project 
    ON community_statistics(project_id, community_id);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_stats_run 
    ON community_statistics(label_propagation_run_id);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_stats_quality 
    ON community_statistics(project_id, coherence_score DESC, significance_score DESC)
    WHERE coherence_score > 0.7;

-- ============================================
-- STEP 4: Create community merge history
-- ============================================
CREATE TABLE IF NOT EXISTS community_merge_history (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    project_id VARCHAR(255) NOT NULL,
    source_community_id UUID NOT NULL REFERENCES memory_community(id) ON DELETE CASCADE,
    target_community_id UUID NOT NULL REFERENCES memory_community(id) ON DELETE CASCADE,
    merge_reason VARCHAR(100),
    merged_at TIMESTAMPTZ DEFAULT NOW(),
    label_propagation_run_id UUID REFERENCES label_propagation_run(id) ON DELETE SET NULL,
    
    -- Rollback capability
    dry_run BOOLEAN DEFAULT FALSE,
    
    -- Statistics before merge
    source_member_count INT,
    target_member_count INT,
    
    -- Impact
    members_moved INT,
    edges_reattached INT
);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_merge_history_project 
    ON community_merge_history(project_id, merged_at DESC);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_merge_history_communities 
    ON community_merge_history(source_community_id, target_community_id);

-- ============================================
-- STEP 5: Add community detection status to memory_community
-- ============================================
ALTER TABLE memory_community 
    ADD COLUMN IF NOT EXISTS last_detection_run_id UUID REFERENCES label_propagation_run(id) ON DELETE SET NULL,
    ADD COLUMN IF NOT EXISTS detection_score FLOAT DEFAULT 0.5,
    ADD COLUMN IF NOT EXISTS is_permanent BOOLEAN DEFAULT FALSE,
    ADD COLUMN IF NOT EXISTS merge_into_id UUID REFERENCES memory_community(id) ON DELETE SET NULL;

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_detection_run 
    ON memory_community(last_detection_run_id, detection_score DESC)
    WHERE detection_score > 0.7;

-- ============================================
-- STEP 6: Add community-level summary generation tracking
-- ============================================
CREATE TABLE IF NOT EXISTS community_summary_generation (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    project_id VARCHAR(255) NOT NULL,
    community_id UUID NOT NULL REFERENCES memory_community(id) ON DELETE CASCADE,
    generated_at TIMESTAMPTZ DEFAULT NOW(),
    generated_by VARCHAR(255),
    
    -- LLM usage
    llm_model VARCHAR(100),
    input_tokens INT,
    output_tokens INT,
    cost_usd FLOAT,
    
    -- Generation method
    method VARCHAR(50) DEFAULT 'extractive',  -- 'extractive' or 'abstractive'
    
    -- Quality
    coherence_rating INT CHECK (coherence_rating >= 1 AND coherence_rating <= 5),
    user_feedback TEXT,
    
    -- Result
    summary_text TEXT NOT NULL,
    summary_embedding VECTOR(768),
    
    -- Versioning
    version INT DEFAULT 1,
    is_latest BOOLEAN DEFAULT TRUE
);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_summary_latest 
    ON community_summary_generation(community_id, generated_at DESC)
    WHERE is_latest = TRUE;

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_summary_embedding 
    ON community_summary_generation USING hnsw (summary_embedding vector_cosine_ops)
    WITH (m = 16, ef_construction = 200)
    WHERE is_latest = TRUE;

-- ============================================
-- ROLLBACK INSTRUCTIONS
-- ============================================
-- DROP TABLE IF EXISTS community_summary_generation;
-- DROP TABLE IF EXISTS community_merge_history;
-- DROP TABLE IF EXISTS community_statistics;
-- DROP TABLE IF EXISTS community_member_map;
-- DROP TABLE IF EXISTS label_propagation_run;
-- ALTER TABLE memory_community DROP COLUMN IF EXISTS last_detection_run_id;
-- ALTER TABLE memory_community DROP COLUMN IF EXISTS detection_score;
-- ALTER TABLE memory_community DROP COLUMN IF EXISTS is_permanent;
-- ALTER TABLE memory_community DROP COLUMN IF EXISTS merge_into_id;
