//! Arena 3 tests: Materialized Views integration, fast-path verification, and freshness under load.

use std::sync::Arc;
use stemedb_core::types::{LifecycleStage, ObjectValue};
use stemedb_lens::VoteAwareConsensusLens;
use stemedb_query::{Materializer, Query, QueryEngine};
use stemedb_storage::{GenericVoteStore, KVStore};
use stemedb_wal::Journal;
use tokio::sync::Mutex;
use tracing::debug;

use crate::agent::Agent;
use crate::helpers::{
    cursor_key, verify_assertion_text, wait_until_ingested, write_assertion_to_wal,
};
use crate::types::{ErrorKind, SimulationError, SimulationResult};

// ============================================================================
// Arena 3.1: MV Integration Test
// ============================================================================

/// Test that Materializer creates MV keys after ingestion.
///
/// Steps:
/// 1. Write assertion to WAL
/// 2. Wait for ingestion
/// 3. Run Materializer step()
/// 4. Verify MV:{subject}:{predicate} key exists
/// 5. Verify MaterializedView contains correct winner
pub(crate) async fn run_mv_integration_test<S: KVStore + 'static>(
    journal: &Arc<Mutex<Journal>>,
    store: &Arc<S>,
    agents: &[Agent],
    result: &mut SimulationResult,
    ingestion_wait_ms: u64,
) -> bool {
    let agent = &agents[0];
    let subject = "MV_Test_Entity";
    let predicate = "test_property";

    // Write assertion to WAL
    let assertion = agent.sign_assertion_with_options(
        subject,
        predicate,
        ObjectValue::Text("mv_test_value".to_string()),
        LifecycleStage::Proposed,
        Some(3000),
    );

    // Check cursor state before writing
    let cursor_before = match store.get(&cursor_key()).await {
        Ok(Some(bytes)) => {
            if let Ok(arr) = <[u8; 8]>::try_from(bytes.as_slice()) {
                u64::from_le_bytes(arr)
            } else {
                0
            }
        }
        _ => 0,
    };
    debug!("  MV integration test: cursor before write = {}", cursor_before);

    let write_result = match write_assertion_to_wal(journal, &assertion).await {
        Ok(r) => r,
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::WriteFailure,
                message: format!("MV integration test: failed to write assertion: {}", e),
            });
            return false;
        }
    };
    result.assertions_written += 1;
    let last_offset = write_result.end_offset;
    debug!(last_offset, cursor_before, "MV integration test: wrote assertion");

    // Wait for ingestion to complete
    if let Err(e) = wait_until_ingested(&**store, last_offset, ingestion_wait_ms).await {
        result.errors.push(e);
        return false;
    }

    // Verify assertion was ingested by querying it
    let engine = QueryEngine::new(store.clone());
    let query = Query::builder().subject(subject).predicate(predicate).build();
    let query_result = match engine.execute(&query).await {
        Ok(r) => r,
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::QueryFailure,
                message: format!(
                    "MV integration test: query failed (assertion not ingested?): {}",
                    e
                ),
            });
            return false;
        }
    };

    if query_result.assertions.is_empty() {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::QueryFailure,
            message: format!(
                "MV integration test: assertion {}:{} not found after ingestion",
                subject, predicate
            ),
        });
        return false;
    }

    debug!(
        "  MV integration test: assertion found via QueryEngine ({} results)",
        query_result.assertions.len()
    );

    // Create Materializer with VoteAwareConsensusLens
    let vote_store = Arc::new(GenericVoteStore::new(store.clone()));
    let lens = VoteAwareConsensusLens::new(vote_store);
    let materializer = Materializer::new(store.clone(), Box::new(lens));

    // Directly materialize the specific subject+predicate pair
    // This is more targeted than step() which materializes ALL pairs
    let mv = match materializer.materialize_pair(subject, predicate).await {
        Ok(Some(view)) => {
            result.views_materialized += 1;
            view
        }
        Ok(None) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::MaterializerFailure,
                message: format!(
                    "MV integration test: materialize_pair returned None for {}:{}",
                    subject, predicate
                ),
            });
            return false;
        }
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::MaterializerFailure,
                message: format!("MV integration test: materialize_pair failed: {}", e),
            });
            return false;
        }
    };

    // Verify the MV was written to the store
    let stored_mv = match materializer.get_materialized_view(subject, predicate).await {
        Ok(Some(view)) => view,
        Ok(None) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::MaterializerFailure,
                message: format!(
                    "MV integration test: MV:{}:{} key does not exist after materialization",
                    subject, predicate
                ),
            });
            return false;
        }
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::MaterializerFailure,
                message: format!("MV integration test: failed to read MV: {}", e),
            });
            return false;
        }
    };

    // Sanity check: stored MV should match what materialize_pair returned
    let _ = stored_mv;

    // Verify winner matches our assertion
    if let Err(e) = verify_assertion_text(
        &mv.winner,
        subject,
        predicate,
        "mv_test_value",
        "MV integration test",
    ) {
        result.errors.push(e);
        return false;
    }

    debug!("  MV integration test passed: MV:{}:{} exists with correct winner", subject, predicate);
    true
}

// ============================================================================
// Arena 3.2: Fast-Path Verification Test
// ============================================================================

/// Test that QueryEngine uses fast-path for MV reads.
///
/// Steps:
/// 1. Write assertion + materialize (reuse state from 3.1)
/// 2. Query via QueryEngine with subject+predicate
/// 3. Verify result matches MV winner
pub(crate) async fn run_fast_path_test<S: KVStore + 'static>(
    journal: &Arc<Mutex<Journal>>,
    store: &Arc<S>,
    agents: &[Agent],
    result: &mut SimulationResult,
    ingestion_wait_ms: u64,
) -> bool {
    let agent = &agents[0];
    let subject = "FastPath_Entity";
    let predicate = "fast_property";

    // Write assertion
    let assertion = agent.sign_assertion_with_options(
        subject,
        predicate,
        ObjectValue::Text("fast_path_value".to_string()),
        LifecycleStage::Proposed,
        Some(3100),
    );

    let write_result = match write_assertion_to_wal(journal, &assertion).await {
        Ok(r) => r,
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::WriteFailure,
                message: format!("Fast-path test: failed to write assertion: {}", e),
            });
            return false;
        }
    };
    result.assertions_written += 1;
    let last_offset = write_result.end_offset;

    // Wait for ingestion
    if let Err(e) = wait_until_ingested(&**store, last_offset, ingestion_wait_ms).await {
        result.errors.push(e);
        return false;
    }

    // Materialize
    let vote_store = Arc::new(GenericVoteStore::new(store.clone()));
    let lens = VoteAwareConsensusLens::new(vote_store);
    let materializer = Materializer::new(store.clone(), Box::new(lens));

    if let Err(e) = materializer.step().await {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::MaterializerFailure,
            message: format!("Fast-path test: materializer step failed: {}", e),
        });
        return false;
    }

    // Query via QueryEngine - this should use fast-path (MV lookup)
    let engine = QueryEngine::new(store.clone());
    let query = Query::builder().subject(subject).predicate(predicate).build();

    let query_result = match engine.execute(&query).await {
        Ok(r) => r,
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::QueryFailure,
                message: format!("Fast-path test: query failed: {}", e),
            });
            return false;
        }
    };

    // Verify result
    if query_result.assertions.is_empty() {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::QueryFailure,
            message: "Fast-path test: query returned no results".to_string(),
        });
        return false;
    }

    // When using fast-path (MV), QueryEngine returns exactly 1 result (the winner)
    if query_result.assertions.len() != 1 {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::MaterializerFailure,
            message: format!(
                "Fast-path test: expected 1 result (MV winner), got {}",
                query_result.assertions.len()
            ),
        });
        return false;
    }

    let winner = &query_result.assertions[0];
    if let Err(e) =
        verify_assertion_text(winner, subject, predicate, "fast_path_value", "Fast-path test")
    {
        result.errors.push(e);
        return false;
    }

    result.queries_executed += 1;
    debug!("  Fast-path test passed: QueryEngine returned MV winner");
    true
}

// ============================================================================
// Arena 3.3: MV Freshness Under Load Test
// ============================================================================

/// Test that MV reflects latest state under rapid writes.
///
/// Steps:
/// 1. Write 10 assertions in rapid succession for same subject+predicate
/// 2. Each with incrementing timestamp
/// 3. Wait for ingestion
/// 4. Run Materializer step()
/// 5. Verify MV winner is the NEWEST assertion (highest timestamp)
pub(crate) async fn run_mv_freshness_test<S: KVStore + 'static>(
    journal: &Arc<Mutex<Journal>>,
    store: &Arc<S>,
    agents: &[Agent],
    result: &mut SimulationResult,
    ingestion_wait_ms: u64,
) -> bool {
    let agent = &agents[0];
    let subject = "Freshness_Entity";
    let predicate = "rapid_update";
    let num_assertions: usize = 10;
    let base_timestamp = 4000u64;

    // Write 10 assertions with incrementing timestamps
    let mut last_offset = 0u64;
    for i in 0..num_assertions {
        let assertion = agent.sign_assertion_with_options(
            subject,
            predicate,
            ObjectValue::Text(format!("value_{}", i)),
            LifecycleStage::Proposed,
            Some(base_timestamp + i as u64),
        );

        match write_assertion_to_wal(journal, &assertion).await {
            Ok(r) => {
                result.assertions_written += 1;
                last_offset = r.end_offset;
            }
            Err(e) => {
                result.errors.push(SimulationError {
                    tick: 0,
                    kind: ErrorKind::WriteFailure,
                    message: format!("MV freshness test: failed to write assertion {}: {}", i, e),
                });
                return false;
            }
        }
    }

    // Wait for all assertions to be ingested
    if let Err(e) = wait_until_ingested(&**store, last_offset, ingestion_wait_ms).await {
        result.errors.push(e);
        return false;
    }

    // Materialize
    let vote_store = Arc::new(GenericVoteStore::new(store.clone()));
    let lens = VoteAwareConsensusLens::new(vote_store);
    let materializer = Materializer::new(store.clone(), Box::new(lens));

    let report = match materializer.step().await {
        Ok(r) => r,
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::MaterializerFailure,
                message: format!("MV freshness test: materializer step failed: {}", e),
            });
            return false;
        }
    };

    result.views_materialized += report.views_updated as u64;

    // Verify MV winner has the highest timestamp
    let mv = match materializer.get_materialized_view(subject, predicate).await {
        Ok(Some(view)) => view,
        Ok(None) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::MaterializerFailure,
                message: format!(
                    "MV freshness test: MV:{}:{} key does not exist after materialization",
                    subject, predicate
                ),
            });
            return false;
        }
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::MaterializerFailure,
                message: format!("MV freshness test: failed to read MV: {}", e),
            });
            return false;
        }
    };

    // The winner should have the highest timestamp (base_timestamp + 9 = 4009)
    let expected_timestamp = base_timestamp + (num_assertions - 1) as u64;
    if mv.winner.timestamp != expected_timestamp {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::MaterializerFailure,
            message: format!(
                "MV freshness test: winner has wrong timestamp. Expected {}, got {}",
                expected_timestamp, mv.winner.timestamp
            ),
        });
        return false;
    }

    // Verify the correct value (value_9 for the last assertion)
    let expected_value = format!("value_{}", num_assertions - 1);
    if let Err(e) =
        verify_assertion_text(&mv.winner, subject, predicate, &expected_value, "MV freshness test")
    {
        result.errors.push(e);
        return false;
    }

    // Verify candidates_count reflects all 10 assertions
    if mv.candidates_count != num_assertions {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::MaterializerFailure,
            message: format!(
                "MV freshness test: expected {} candidates, got {}",
                num_assertions, mv.candidates_count
            ),
        });
        return false;
    }

    debug!(
        "  MV freshness test passed: winner has timestamp {} with {} candidates",
        mv.winner.timestamp, mv.candidates_count
    );
    true
}
