//! Compound index tests for QueryEngine.

use std::sync::Arc;
use stemedb_core::types::{LifecycleStage, ObjectValue};
use stemedb_storage::HybridStore;

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

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

    // Create multiple assertions with different subject/predicate combinations
    let tesla_revenue = create_test_assertion("Tesla", "revenue", LifecycleStage::Approved);
    let tesla_profit = create_test_assertion("Tesla", "profit", LifecycleStage::Approved);
    let apple_revenue = create_test_assertion("Apple", "revenue", LifecycleStage::Approved);

    store_assertion(&store, &tesla_revenue).await;
    store_assertion(&store, &tesla_profit).await;
    store_assertion(&store, &apple_revenue).await;

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

    // Query with both subject and predicate should use compound index
    let query = Query::builder().subject("Tesla").predicate("revenue").build();

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

    assert_eq!(result.assertions.len(), 1);
    assert_eq!(result.assertions[0].subject, "Tesla");
    assert_eq!(result.assertions[0].predicate, "revenue");
}

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

    // Store multiple assertions with same subject+predicate but different values/timestamps
    let mut assertion1 = create_test_assertion("Tesla", "revenue", LifecycleStage::Proposed);
    assertion1.timestamp = 1000;

    let mut assertion2 = create_test_assertion("Tesla", "revenue", LifecycleStage::Approved);
    assertion2.timestamp = 2000;

    let mut assertion3 = create_test_assertion("Tesla", "revenue", LifecycleStage::Approved);
    assertion3.object = ObjectValue::Number(200.0); // Different value to ensure different hash

    store_assertion(&store, &assertion1).await;
    store_assertion(&store, &assertion2).await;
    store_assertion(&store, &assertion3).await;

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

    // Should find all three assertions
    let query = Query::builder().subject("Tesla").predicate("revenue").build();

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

    assert_eq!(result.assertions.len(), 3);
    for assertion in &result.assertions {
        assert_eq!(assertion.subject, "Tesla");
        assert_eq!(assertion.predicate, "revenue");
    }
}

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

    let tesla_revenue = create_test_assertion("Tesla", "revenue", LifecycleStage::Approved);
    let tesla_profit = create_test_assertion("Tesla", "profit", LifecycleStage::Approved);

    store_assertion(&store, &tesla_revenue).await;
    store_assertion(&store, &tesla_profit).await;

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

    // Query with only subject should use subject index
    let query = Query::builder().subject("Tesla").build();

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

    assert_eq!(result.assertions.len(), 2);
    for assertion in &result.assertions {
        assert_eq!(assertion.subject, "Tesla");
    }
}

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

    let tesla_revenue = create_test_assertion("Tesla", "revenue", LifecycleStage::Approved);

    store_assertion(&store, &tesla_revenue).await;

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

    // Query for non-existent combination
    let query = Query::builder().subject("Tesla").predicate("nonexistent").build();

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

    assert_eq!(result.assertions.len(), 0);
}
