//! Tests for conflict score filtering.

use std::sync::Arc;
use stemedb_core::types::{LifecycleStage, MaterializedView};
use stemedb_storage::{key_codec, HybridStore, KVStore};

use super::{create_test_assertion, store_assertion, QueryEngine};
use crate::query::Query;

/// Helper to store a materialized view with a custom conflict score.
async fn store_mv_with_conflict(
    store: &Arc<HybridStore>,
    subject: &str,
    predicate: &str,
    conflict_score: f32,
) {
    let assertion = create_test_assertion(subject, predicate, LifecycleStage::Approved);
    store_assertion(store, &assertion).await;

    let view = MaterializedView {
        winner: assertion,
        lens_name: "VoteAwareConsensus".to_string(),
        resolution_confidence: 0.95,
        candidates_count: 3,
        materialized_at: 1000,
        conflict_score,
    };

    let key = key_codec::mv_key(subject, predicate);
    let bytes = stemedb_core::serde::serialize(&view).expect("serialize MV");
    store.put(&key, &bytes).await.expect("put MV");
}

#[tokio::test]
async fn test_min_conflict_score_returns_empty_when_below_threshold() {
    let store = Arc::new(HybridStore::open_temp().expect("store"));

    // Store MV with low conflict (agreement)
    store_mv_with_conflict(&store, "Aspirin", "cardiovascular_benefit", 0.15).await;

    let engine = QueryEngine::new(Arc::new(store));

    let query = Query::builder()
        .subject("Aspirin")
        .predicate("cardiovascular_benefit")
        .min_conflict_score(0.7) // Looking for high conflict
        .build();

    let result = engine.execute(&query).await.expect("execute");

    // Should return empty because conflict_score (0.15) < min_conflict_score (0.7)
    assert_eq!(result.assertions.len(), 0);
    assert_eq!(result.total_count, 0);
}

#[tokio::test]
async fn test_min_conflict_score_returns_result_when_above_threshold() {
    let store = Arc::new(HybridStore::open_temp().expect("store"));

    // Store MV with high conflict (disagreement)
    store_mv_with_conflict(&store, "Semaglutide", "muscle_effect", 0.85).await;

    let engine = QueryEngine::new(Arc::new(store));

    let query = Query::builder()
        .subject("Semaglutide")
        .predicate("muscle_effect")
        .min_conflict_score(0.7) // Looking for high conflict
        .build();

    let result = engine.execute(&query).await.expect("execute");

    // Should return result because conflict_score (0.85) >= min_conflict_score (0.7)
    assert_eq!(result.assertions.len(), 1);
    assert_eq!(result.assertions[0].subject, "Semaglutide");
}

#[tokio::test]
async fn test_min_conflict_score_edge_case_exact_match() {
    let store = Arc::new(HybridStore::open_temp().expect("store"));

    // Store MV with conflict score exactly at threshold
    store_mv_with_conflict(&store, "Drug", "effect", 0.5).await;

    let engine = QueryEngine::new(Arc::new(store));

    let query = Query::builder()
        .subject("Drug")
        .predicate("effect")
        .min_conflict_score(0.5) // Exact match
        .build();

    let result = engine.execute(&query).await.expect("execute");

    // Should return result because conflict_score (0.5) >= min_conflict_score (0.5)
    assert_eq!(result.assertions.len(), 1);
}

#[tokio::test]
async fn test_max_conflict_score_returns_result_when_below_threshold() {
    let store = Arc::new(HybridStore::open_temp().expect("store"));

    // Store MV with low conflict (agreement)
    store_mv_with_conflict(&store, "Aspirin", "cardiovascular_benefit", 0.15).await;

    let engine = QueryEngine::new(Arc::new(store));

    let query = Query::builder()
        .subject("Aspirin")
        .predicate("cardiovascular_benefit")
        .max_conflict_score(0.2) // Looking for agreement
        .build();

    let result = engine.execute(&query).await.expect("execute");

    // Should return result because conflict_score (0.15) <= max_conflict_score (0.2)
    assert_eq!(result.assertions.len(), 1);
    assert_eq!(result.assertions[0].subject, "Aspirin");
}

#[tokio::test]
async fn test_max_conflict_score_returns_empty_when_above_threshold() {
    let store = Arc::new(HybridStore::open_temp().expect("store"));

    // Store MV with high conflict (disagreement)
    store_mv_with_conflict(&store, "Semaglutide", "muscle_effect", 0.85).await;

    let engine = QueryEngine::new(Arc::new(store));

    let query = Query::builder()
        .subject("Semaglutide")
        .predicate("muscle_effect")
        .max_conflict_score(0.2) // Looking for agreement
        .build();

    let result = engine.execute(&query).await.expect("execute");

    // Should return empty because conflict_score (0.85) > max_conflict_score (0.2)
    assert_eq!(result.assertions.len(), 0);
    assert_eq!(result.total_count, 0);
}

#[tokio::test]
async fn test_max_conflict_score_edge_case_exact_match() {
    let store = Arc::new(HybridStore::open_temp().expect("store"));

    // Store MV with conflict score exactly at threshold
    store_mv_with_conflict(&store, "Drug", "effect", 0.5).await;

    let engine = QueryEngine::new(Arc::new(store));

    let query = Query::builder()
        .subject("Drug")
        .predicate("effect")
        .max_conflict_score(0.5) // Exact match
        .build();

    let result = engine.execute(&query).await.expect("execute");

    // Should return result because conflict_score (0.5) <= max_conflict_score (0.5)
    assert_eq!(result.assertions.len(), 1);
}

#[tokio::test]
async fn test_both_conflict_scores_filters_range() {
    let store = Arc::new(HybridStore::open_temp().expect("store"));

    // Store MVs with different conflict scores
    store_mv_with_conflict(&store, "Drug_A", "effect", 0.1).await; // Too low
    store_mv_with_conflict(&store, "Drug_B", "effect", 0.4).await; // In range
    store_mv_with_conflict(&store, "Drug_C", "effect", 0.6).await; // In range
    store_mv_with_conflict(&store, "Drug_D", "effect", 0.9).await; // Too high

    let engine = QueryEngine::new(Arc::new(store));

    // Query for each and check filtering
    let query_a = Query::builder()
        .subject("Drug_A")
        .predicate("effect")
        .min_conflict_score(0.3)
        .max_conflict_score(0.7)
        .build();
    let result_a = engine.execute(&query_a).await.expect("execute");
    assert_eq!(result_a.assertions.len(), 0, "Drug_A should be filtered out (0.1 < 0.3)");

    let query_b = Query::builder()
        .subject("Drug_B")
        .predicate("effect")
        .min_conflict_score(0.3)
        .max_conflict_score(0.7)
        .build();
    let result_b = engine.execute(&query_b).await.expect("execute");
    assert_eq!(result_b.assertions.len(), 1, "Drug_B should pass (0.3 <= 0.4 <= 0.7)");

    let query_c = Query::builder()
        .subject("Drug_C")
        .predicate("effect")
        .min_conflict_score(0.3)
        .max_conflict_score(0.7)
        .build();
    let result_c = engine.execute(&query_c).await.expect("execute");
    assert_eq!(result_c.assertions.len(), 1, "Drug_C should pass (0.3 <= 0.6 <= 0.7)");

    let query_d = Query::builder()
        .subject("Drug_D")
        .predicate("effect")
        .min_conflict_score(0.3)
        .max_conflict_score(0.7)
        .build();
    let result_d = engine.execute(&query_d).await.expect("execute");
    assert_eq!(result_d.assertions.len(), 0, "Drug_D should be filtered out (0.9 > 0.7)");
}

#[tokio::test]
async fn test_no_conflict_filters_returns_all() {
    let store = Arc::new(HybridStore::open_temp().expect("store"));

    // Store MVs with different conflict scores
    store_mv_with_conflict(&store, "Drug_A", "effect", 0.1).await;
    store_mv_with_conflict(&store, "Drug_B", "effect", 0.9).await;

    let engine = QueryEngine::new(Arc::new(store));

    // Query without conflict filters
    let query_a = Query::builder().subject("Drug_A").predicate("effect").build();
    let result_a = engine.execute(&query_a).await.expect("execute");
    assert_eq!(result_a.assertions.len(), 1, "Should return low conflict result");

    let query_b = Query::builder().subject("Drug_B").predicate("effect").build();
    let result_b = engine.execute(&query_b).await.expect("execute");
    assert_eq!(result_b.assertions.len(), 1, "Should return high conflict result");
}

#[tokio::test]
async fn test_conflict_filters_combine_with_lifecycle() {
    let store = Arc::new(HybridStore::open_temp().expect("store"));

    // Store MV with high conflict and Approved lifecycle
    let approved = create_test_assertion("Drug", "effect", LifecycleStage::Approved);
    store_assertion(&store, &approved).await;

    let view = MaterializedView {
        winner: approved,
        lens_name: "VoteAwareConsensus".to_string(),
        resolution_confidence: 0.95,
        candidates_count: 3,
        materialized_at: 1000,
        conflict_score: 0.8,
    };

    let key = key_codec::mv_key("Drug", "effect");
    let bytes = stemedb_core::serde::serialize(&view).expect("serialize MV");
    store.put(&key, &bytes).await.expect("put MV");

    let engine = QueryEngine::new(Arc::new(store));

    // Query with conflict filter AND lifecycle filter
    let query = Query::builder()
        .subject("Drug")
        .predicate("effect")
        .lifecycle(LifecycleStage::Approved)
        .min_conflict_score(0.7)
        .build();

    let result = engine.execute(&query).await.expect("execute");

    // Should pass both filters
    assert_eq!(result.assertions.len(), 1);
    assert_eq!(result.assertions[0].lifecycle, LifecycleStage::Approved);
}

#[tokio::test]
async fn test_conflict_filters_with_wrong_lifecycle_returns_empty() {
    let store = Arc::new(HybridStore::open_temp().expect("store"));

    // Store MV with high conflict but Approved lifecycle
    let approved = create_test_assertion("Drug", "effect", LifecycleStage::Approved);
    store_assertion(&store, &approved).await;

    let view = MaterializedView {
        winner: approved,
        lens_name: "VoteAwareConsensus".to_string(),
        resolution_confidence: 0.95,
        candidates_count: 3,
        materialized_at: 1000,
        conflict_score: 0.8,
    };

    let key = key_codec::mv_key("Drug", "effect");
    let bytes = stemedb_core::serde::serialize(&view).expect("serialize MV");
    store.put(&key, &bytes).await.expect("put MV");

    let engine = QueryEngine::new(Arc::new(store));

    // Query for Proposed but MV has Approved - should fall through to slow path
    let query = Query::builder()
        .subject("Drug")
        .predicate("effect")
        .lifecycle(LifecycleStage::Proposed)
        .min_conflict_score(0.7)
        .build();

    let result = engine.execute(&query).await.expect("execute");

    // Should return empty because lifecycle doesn't match (slow path has no candidates)
    assert_eq!(result.assertions.len(), 0);
}
