//! Core simulation runner - the main orchestration logic.

use ed25519_dalek::{Signature, Verifier, VerifyingKey};
use std::sync::Arc;
use stemedb_core::types::{LifecycleStage, ObjectValue};
use stemedb_ingest::Ingestor;
use stemedb_query::{Query, QueryEngine};
use stemedb_storage::HybridStore;
use stemedb_wal::Journal;
use tokio::sync::Mutex;
use tracing::{debug, info, warn};

use crate::agent::Agent;
use crate::arenas::{
    run_audit_test, run_fast_path_test, run_lifecycle_test, run_mv_freshness_test,
    run_mv_integration_test, run_recency_lens_test, run_troll_resistance_test,
    run_vote_consensus_test,
};
use crate::helpers::{
    compute_assertion_hash, wait_until_ingested, write_assertion_to_wal, write_vote_to_wal,
};
use crate::strategy::{self, AgentAction, AgentStrategy, StrategyMetrics, WorldState};
use crate::types::{
    ErrorKind, SimulationConfig, SimulationError, SimulationResult, SimulationSetupError,
};

/// Run the simulation with the given configuration.
///
/// This is the primary entry point for the simulation. It:
/// 1. Sets up storage infrastructure (WAL + KV)
/// 2. Creates agents with cryptographic identities
/// 3. Runs assertion ticks (write to WAL)
/// 4. Waits for ingestion
/// 5. Verifies assertions via QueryEngine (Arena 1)
/// 6. Tests Recency Lens (Arena 1)
/// 7. Tests Lifecycle Filtering (Arena 1)
/// 8. Verifies Query Audit Trail (Arena 1)
///
/// # Returns
///
/// - `Ok(SimulationResult)` on completion (even with verification errors)
/// - `Err(SimulationSetupError)` if infrastructure setup fails
pub async fn run_simulation(
    config: SimulationConfig,
) -> Result<SimulationResult, SimulationSetupError> {
    let agent_count = config.agent_count();
    info!("🚀 Starting StemeDB Simulation: 'The Arena' (Phase 1: Query Path)");
    info!(
        "   Config: {} agents, {} ticks, {}ms ingestion wait",
        agent_count, config.tick_count, config.ingestion_wait_ms
    );

    // 1. Setup Storage (WAL + KV)
    let temp_wal_dir =
        tempfile::tempdir().map_err(|e| SimulationSetupError::WalDirectory(e.to_string()))?;
    let temp_db_dir =
        tempfile::tempdir().map_err(|e| SimulationSetupError::StoreDirectory(e.to_string()))?;

    let journal = Arc::new(Mutex::new(
        Journal::open(temp_wal_dir.path())
            .map_err(|e| SimulationSetupError::JournalOpen(e.to_string()))?,
    ));
    let store = Arc::new(
        HybridStore::open(temp_db_dir.path())
            .map_err(|e| SimulationSetupError::StoreOpen(e.to_string()))?,
    );

    debug!("  WAL initialized at {:?}", temp_wal_dir.path());
    debug!("  KV Store initialized at {:?}", temp_db_dir.path());

    // 2. Start Ingestor
    let mut ingestor = Ingestor::new(journal.clone(), store.clone())
        .await
        .map_err(|e| SimulationSetupError::IngestorCreate(e.to_string()))?;
    ingestor.start();
    debug!("  Ingestor started (background worker).");

    // 3. Setup Agents with strategies
    let mut agents: Vec<Agent> = Vec::with_capacity(agent_count);
    let mut strategies: Vec<Box<dyn AgentStrategy>> = Vec::with_capacity(agent_count);
    let mut agent_idx = 0usize;
    for spec in &config.agents {
        for j in 0..spec.count {
            let strategy = strategy::create_strategy(spec.strategy);
            let name = format!("{}_{}", strategy.name(), agent_idx);
            agents.push(Agent::new(&name));
            strategies.push(strategy);
            let _ = j; // suppress unused
            agent_idx += 1;
        }
    }
    info!("  Swarm of {} agents instantiated.", agents.len());

    // 4. Initialize Result
    let mut result = SimulationResult {
        assertions_written: 0,
        assertions_verified: 0,
        queries_executed: 0,
        votes_written: 0,
        recency_test_passed: false,
        lifecycle_test_passed: false,
        audit_test_passed: false,
        vote_consensus_test_passed: false,
        troll_resistance_test_passed: false,
        mv_integration_test_passed: false,
        fast_path_test_passed: false,
        mv_freshness_test_passed: false,
        persona_test_passed: false,
        strategy_metrics: Vec::new(),
        views_materialized: 0,
        errors: Vec::new(),
        agent_count,
        tick_count: config.tick_count,
    };

    // Per-strategy metrics tracking (indexed same as agents/strategies vecs)
    let mut per_agent_metrics: Vec<StrategyMetrics> =
        vec![StrategyMetrics::default(); agents.len()];
    // Track world state for strategy decisions
    let mut world_assertions: Vec<(stemedb_core::types::Hash, String, String, ObjectValue, f32)> =
        Vec::new();
    let mut vote_summaries: Vec<(stemedb_core::types::Hash, f32)> = Vec::new();

    // 5. Run Simulation Ticks (Strategy-Driven Write Phase)
    let mut assertions_data: Vec<(stemedb_core::types::Assertion, Vec<u8>)> =
        Vec::with_capacity(config.tick_count);
    let mut last_journal_offset = 0u64;

    for tick in 0..config.tick_count {
        let agent_idx = tick % agents.len();
        let agent = &agents[agent_idx];
        let strategy = &strategies[agent_idx];

        let world_state = WorldState {
            tick,
            existing_assertions: world_assertions.clone(),
            vote_summaries: vote_summaries.clone(),
            agent_count,
        };

        let action = strategy.decide_action(&world_state);
        debug!(
            "  [Tick {}] Agent '{}' ({}) -> {:?}",
            tick,
            agent.id,
            strategy.name(),
            match &action {
                AgentAction::Assert { subject, .. } => format!("Assert({})", subject),
                AgentAction::Vote { assertion_hash, .. } =>
                    format!("Vote({}..)", hex::encode(&assertion_hash[..4])),
                AgentAction::Query { subject, .. } => format!("Query({})", subject),
                AgentAction::Skip => "Skip".to_string(),
            }
        );

        match action {
            AgentAction::Assert { subject, predicate, object } => {
                let assertion = agent.sign_assertion_with_options(
                    &subject,
                    &predicate,
                    object.clone(),
                    LifecycleStage::Proposed,
                    None,
                );
                // Override confidence with strategy's base_confidence
                let mut assertion = assertion;
                assertion.confidence = strategy.base_confidence();

                match write_assertion_to_wal(&journal, &assertion).await {
                    Ok(wal_result) => {
                        result.assertions_written += 1;
                        last_journal_offset = wal_result.end_offset;

                        let hash = compute_assertion_hash(&assertion);
                        world_assertions.push((
                            hash,
                            subject.clone(),
                            predicate.clone(),
                            object,
                            strategy.base_confidence(),
                        ));
                        assertions_data.push((assertion, wal_result.raw_bytes));
                        per_agent_metrics[agent_idx].assertions_created += 1;
                    }
                    Err(e) => {
                        result.errors.push(SimulationError {
                            tick: tick as u64,
                            kind: ErrorKind::WriteFailure,
                            message: e,
                        });
                    }
                }
            }
            AgentAction::Vote { assertion_hash, weight } => {
                let vote = agent.vote(assertion_hash, weight);
                match write_vote_to_wal(&journal, &vote).await {
                    Ok(offset) => {
                        result.votes_written += 1;
                        last_journal_offset = offset;

                        // Update vote summaries
                        if let Some(entry) =
                            vote_summaries.iter_mut().find(|(h, _)| *h == assertion_hash)
                        {
                            entry.1 += weight;
                        } else {
                            vote_summaries.push((assertion_hash, weight));
                        }
                        per_agent_metrics[agent_idx].votes_cast += 1;
                    }
                    Err(e) => {
                        result.errors.push(SimulationError {
                            tick: tick as u64,
                            kind: ErrorKind::VoteWriteFailure,
                            message: e,
                        });
                    }
                }
            }
            AgentAction::Query { subject, predicate } => {
                debug!("  [Tick {}] Query for {}:{}", tick, subject, predicate);
                per_agent_metrics[agent_idx].queries_executed += 1;
            }
            AgentAction::Skip => {
                debug!("  [Tick {}] Agent '{}' skipped", tick, agent.id);
                per_agent_metrics[agent_idx].skips += 1;
            }
        }
    }

    info!("  {} assertions written to WAL.", result.assertions_written);

    // 6. Wait for Ingestion (cursor-based sync)
    info!("⏳ Waiting for ingestion to reach offset {}...", last_journal_offset);
    if let Err(e) =
        wait_until_ingested(&*store, last_journal_offset, config.ingestion_wait_ms).await
    {
        result.errors.push(e);
    }

    // ========================================================================
    // ARENA 1: Query Path Verification
    // ========================================================================

    info!("🔬 Arena 1: Verifying Query Path...");

    // 7. Create QueryEngine and verify assertions via queries
    let engine = QueryEngine::new(store.clone());

    for (i, (original_assertion, _original_bytes)) in assertions_data.iter().enumerate() {
        let tick = i as u64;

        // Query via QueryEngine (not direct KV access)
        let query = Query::builder()
            .subject(&original_assertion.subject)
            .predicate(&original_assertion.predicate)
            .build();

        match engine.execute(&query).await {
            Ok(query_result) => {
                result.queries_executed += 1;

                if query_result.assertions.is_empty() {
                    result.errors.push(SimulationError {
                        tick,
                        kind: ErrorKind::QueryFailure,
                        message: format!(
                            "Query returned no results for {}:{}",
                            original_assertion.subject, original_assertion.predicate
                        ),
                    });
                    continue;
                }

                // Find the matching assertion (multiple may exist for same subject+predicate
                // due to strategy-driven assertions, e.g. Troll contradictions)
                let found = match query_result.assertions.iter().find(|a| {
                    a.subject == original_assertion.subject && a.object == original_assertion.object
                }) {
                    Some(a) => a,
                    None => {
                        result.errors.push(SimulationError {
                            tick,
                            kind: ErrorKind::StorageCorruption,
                            message: format!(
                                "No matching assertion found for {}:{} with object {:?}",
                                original_assertion.subject,
                                original_assertion.predicate,
                                original_assertion.object
                            ),
                        });
                        continue;
                    }
                };

                // Verify signature
                let sig_entry = &found.signatures[0];
                let verifying_key = match VerifyingKey::from_bytes(&sig_entry.agent_id) {
                    Ok(k) => k,
                    Err(e) => {
                        result.errors.push(SimulationError {
                            tick,
                            kind: ErrorKind::SignatureInvalid,
                            message: format!("Invalid agent public key: {}", e),
                        });
                        continue;
                    }
                };
                let signature = Signature::from_bytes(&sig_entry.signature);
                let message = format!("{}:{}", found.subject, found.predicate);

                if let Err(e) = verifying_key.verify(message.as_bytes(), &signature) {
                    result.errors.push(SimulationError {
                        tick,
                        kind: ErrorKind::SignatureInvalid,
                        message: format!("Signature verification failed: {}", e),
                    });
                    continue;
                }

                debug!(
                    "  [Query {}] Assertion for '{}' verified via QueryEngine.",
                    i, found.subject
                );
                result.assertions_verified += 1;
            }
            Err(e) => {
                result.errors.push(SimulationError {
                    tick,
                    kind: ErrorKind::QueryFailure,
                    message: format!("Query execution failed: {}", e),
                });
            }
        }
    }

    // ========================================================================
    // 8. Arena 1.2: Recency Lens Test
    // ========================================================================
    info!("🔬 Arena 1.2: Testing Recency Lens...");
    result.recency_test_passed =
        run_recency_lens_test(&journal, &store, &agents, &mut result, config.ingestion_wait_ms)
            .await;

    // ========================================================================
    // 9. Arena 1.3: Lifecycle Filtering Test
    // ========================================================================
    info!("🔬 Arena 1.3: Testing Lifecycle Filtering...");
    result.lifecycle_test_passed =
        run_lifecycle_test(&journal, &store, &agents, &mut result, config.ingestion_wait_ms).await;

    // ========================================================================
    // 10. Arena 1.4: Query Audit Verification
    // ========================================================================
    info!("🔬 Arena 1.4: Testing Query Audit Trail...");
    result.audit_test_passed = run_audit_test(&store, &agents, &mut result).await;

    // ========================================================================
    // ARENA 2: Voting & Consensus
    // ========================================================================

    info!("🗳️ Arena 2: Verifying Voting & Consensus...");

    // ========================================================================
    // 11. Arena 2.2: Conflicting Assertions with Votes
    // ========================================================================
    info!("🗳️ Arena 2.2: Testing Vote-Aware Consensus...");
    result.vote_consensus_test_passed =
        run_vote_consensus_test(&journal, &store, &agents, &mut result, config.ingestion_wait_ms)
            .await;

    // ========================================================================
    // 12. Arena 2.3: Troll Vote Resistance
    // ========================================================================
    info!("🗳️ Arena 2.3: Testing Troll Vote Resistance...");
    result.troll_resistance_test_passed =
        run_troll_resistance_test(&journal, &store, &agents, &mut result, config.ingestion_wait_ms)
            .await;

    // ========================================================================
    // ARENA 3: Materialized Views
    // ========================================================================

    info!("✨ Arena 3: Testing Materialized Views...");

    // ========================================================================
    // 13. Arena 3.1: MV Integration
    // ========================================================================
    info!("✨ Arena 3.1: Testing MV Integration...");
    result.mv_integration_test_passed =
        run_mv_integration_test(&journal, &store, &agents, &mut result, config.ingestion_wait_ms)
            .await;

    // ========================================================================
    // 14. Arena 3.2: Fast-Path Verification
    // ========================================================================
    info!("✨ Arena 3.2: Testing Fast-Path Verification...");
    result.fast_path_test_passed =
        run_fast_path_test(&journal, &store, &agents, &mut result, config.ingestion_wait_ms).await;

    // ========================================================================
    // 15. Arena 3.3: MV Freshness Under Load
    // ========================================================================
    info!("✨ Arena 3.3: Testing MV Freshness Under Load...");
    result.mv_freshness_test_passed =
        run_mv_freshness_test(&journal, &store, &agents, &mut result, config.ingestion_wait_ms)
            .await;

    // ========================================================================
    // ARENA 4: Agent Persona Verification
    // ========================================================================

    info!("🎭 Arena 4: Verifying Agent Persona Differentiation...");

    // Aggregate per-strategy metrics
    let mut strategy_map: std::collections::HashMap<&str, StrategyMetrics> =
        std::collections::HashMap::new();
    for (idx, metrics) in per_agent_metrics.iter().enumerate() {
        let name = strategies[idx].name();
        let entry = strategy_map.entry(name).or_default();
        entry.assertions_created += metrics.assertions_created;
        entry.votes_cast += metrics.votes_cast;
        entry.queries_executed += metrics.queries_executed;
        entry.skips += metrics.skips;
    }

    // Log per-strategy metrics
    for (name, metrics) in &strategy_map {
        info!(
            "  {} -> asserts={}, votes={}, queries={}, skips={}",
            name,
            metrics.assertions_created,
            metrics.votes_cast,
            metrics.queries_executed,
            metrics.skips
        );
    }

    // Verify differentiated behavior.
    // Only require activity from strategies that actually got at least one turn.
    let has_activity = |m: &StrategyMetrics| -> bool {
        m.assertions_created + m.votes_cast + m.queries_executed + m.skips > 0
    };
    let scientist_ok = strategy_map
        .get("Scientist")
        .map(|m| !has_activity(m) || m.assertions_created > 0)
        .unwrap_or(true);
    let troll_ok = strategy_map
        .get("Troll")
        .map(|m| !has_activity(m) || m.assertions_created > 0 || m.skips > 0)
        .unwrap_or(true);
    let believer_ok = strategy_map
        .get("Believer")
        .map(|m| !has_activity(m) || m.votes_cast > 0 || m.skips > 0)
        .unwrap_or(true);

    result.persona_test_passed = scientist_ok && troll_ok && believer_ok;

    if result.persona_test_passed {
        info!("  Arena 4: Persona differentiation PASSED");
    } else {
        warn!(
            "  Arena 4: Persona differentiation FAILED (scientist={}, troll={}, believer={})",
            scientist_ok, troll_ok, believer_ok
        );
    }

    // Store strategy metrics in result
    result.strategy_metrics =
        strategy_map.into_iter().map(|(name, metrics)| (name.to_string(), metrics)).collect();

    // 16. Shut down the ingestor gracefully
    //
    // This is critical: we must stop the background ingestion task BEFORE
    // the TempDir is dropped, otherwise the task will try to read from
    // deleted WAL files.
    info!("Shutting down ingestor...");
    ingestor.shutdown(std::time::Duration::from_secs(2)).await;

    // 17. Log summary
    if result.is_success() {
        info!("{}", result.summary());
    } else {
        warn!("{}", result.summary());
        for err in &result.errors {
            warn!("  [Tick {}] {:?}: {}", err.tick, err.kind, err.message);
        }
    }

    Ok(result)
}
