//! Arena 2 tests: Voting, Consensus, and Troll Resistance.

use std::sync::Arc;
use stemedb_core::types::{LifecycleStage, ObjectValue};
use stemedb_lens::{AsyncLens, VoteAwareConsensusLens};
use stemedb_query::{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::{
    compute_assertion_hash, wait_until_ingested, write_assertion_to_wal, write_vote_to_wal,
};
use crate::types::{ErrorKind, SimulationError, SimulationResult};

// ============================================================================
// Arena 2.2: Vote-Aware Consensus Test
// ============================================================================

/// Test that VoteAwareConsensusLens correctly selects the assertion with most votes.
///
/// Scenario:
/// - Scientist_Alpha asserts "Protein_X binds Receptor_Y" (confidence 0.8)
/// - Scientist_Beta asserts "Protein_X binds Receptor_Z" (confidence 0.8)
/// - Alpha votes for own assertion (weight 1.0)
/// - Beta votes for own assertion (weight 1.0)
/// - Believer (third agent) votes for Alpha's assertion
/// - Query with VoteAwareConsensusLens
/// - Verify Alpha's assertion wins (2 votes vs 1)
pub(crate) async fn run_vote_consensus_test<S: KVStore + 'static>(
    journal: &Arc<Mutex<Journal>>,
    store: &Arc<S>,
    agents: &[Agent],
    result: &mut SimulationResult,
    ingestion_wait_ms: u64,
) -> bool {
    // Need at least 3 agents: Alpha, Beta, Believer
    if agents.len() < 3 {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::VoteConsensusFailure,
            message: "Vote consensus test: need at least 3 agents".to_string(),
        });
        return false;
    }

    let alpha = &agents[0];
    let beta = &agents[1];
    let believer = &agents[2];

    let subject = "Protein_X";
    let predicate = "binds";

    // Alpha's assertion: Protein_X binds Receptor_Y
    let alpha_assertion = alpha.sign_assertion_with_options(
        subject,
        predicate,
        ObjectValue::Text("Receptor_Y".to_string()),
        LifecycleStage::Proposed,
        Some(1000),
    );

    // Beta's conflicting assertion: Protein_X binds Receptor_Z
    let beta_assertion = beta.sign_assertion_with_options(
        subject,
        predicate,
        ObjectValue::Text("Receptor_Z".to_string()),
        LifecycleStage::Proposed,
        Some(1001),
    );

    // Write both assertions to WAL and track last offset
    let _alpha_result = match write_assertion_to_wal(journal, &alpha_assertion).await {
        Ok(r) => r,
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::WriteFailure,
                message: format!("Vote consensus test: failed to write Alpha assertion: {}", e),
            });
            return false;
        }
    };
    result.assertions_written += 1;

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

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

    // Compute assertion hashes
    let alpha_hash = compute_assertion_hash(&alpha_assertion);
    let beta_hash = compute_assertion_hash(&beta_assertion);

    // Alpha votes for own assertion (weight 1.0)
    let alpha_vote = alpha.vote(alpha_hash, 1.0);
    if let Err(e) = write_vote_to_wal(journal, &alpha_vote).await {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::VoteWriteFailure,
            message: format!("Vote consensus test: failed to write Alpha vote: {}", e),
        });
        return false;
    }
    result.votes_written += 1;

    // Beta votes for own assertion (weight 1.0)
    let beta_vote = beta.vote(beta_hash, 1.0);
    if let Err(e) = write_vote_to_wal(journal, &beta_vote).await {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::VoteWriteFailure,
            message: format!("Vote consensus test: failed to write Beta vote: {}", e),
        });
        return false;
    }
    result.votes_written += 1;

    // Believer votes for Alpha's assertion (weight 1.0) - this tips the balance
    let believer_vote = believer.vote(alpha_hash, 1.0);
    last_offset = match write_vote_to_wal(journal, &believer_vote).await {
        Ok(offset) => offset,
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::VoteWriteFailure,
                message: format!("Vote consensus test: failed to write Believer vote: {}", e),
            });
            return false;
        }
    };
    result.votes_written += 1;

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

    // Query to get candidates
    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!("Vote consensus test: query failed: {}", e),
            });
            return false;
        }
    };

    if query_result.assertions.len() < 2 {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::QueryFailure,
            message: format!(
                "Vote consensus test: expected 2 assertions, got {}",
                query_result.assertions.len()
            ),
        });
        return false;
    }

    // Apply VoteAwareConsensusLens
    let vote_store = Arc::new(GenericVoteStore::new(store.clone()));
    let lens = VoteAwareConsensusLens::new(vote_store);
    let resolution = lens.resolve_async(&query_result.assertions).await;

    match resolution.winner {
        Some(winner) => {
            // Winner should be Alpha's assertion (Receptor_Y) - it has 2 votes vs Beta's 1
            if let ObjectValue::Text(ref value) = winner.object {
                if value == "Receptor_Y" {
                    debug!(
                        "  Vote-aware consensus correctly selected Alpha's assertion (2 votes vs 1)"
                    );
                    result.queries_executed += 1;
                    return true;
                }
            }
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::VoteConsensusFailure,
                message: format!(
                    "Vote consensus test: wrong winner selected (expected Receptor_Y, got {:?})",
                    winner.object
                ),
            });
            false
        }
        None => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::VoteConsensusFailure,
                message: "Vote consensus test: lens returned no winner".to_string(),
            });
            false
        }
    }
}

// ============================================================================
// Arena 2.3: Troll Vote Resistance Test
// ============================================================================

/// Test that trolls cannot overturn consensus with self-votes.
///
/// Scenario:
/// - Scientist asserts high-confidence fact about Entity_X
/// - Scientist votes for own assertion
/// - Troll creates low-confidence contradicting assertion
/// - Troll votes for own assertion
/// - Another scientist (ally) also votes for Scientist's assertion
/// - Verify Scientist's assertion still wins despite Troll vote
pub(crate) async fn run_troll_resistance_test<S: KVStore + 'static>(
    journal: &Arc<Mutex<Journal>>,
    store: &Arc<S>,
    agents: &[Agent],
    result: &mut SimulationResult,
    ingestion_wait_ms: u64,
) -> bool {
    // Need at least 3 agents: Scientist, Troll, Ally
    if agents.len() < 3 {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::VoteConsensusFailure,
            message: "Troll resistance test: need at least 3 agents".to_string(),
        });
        return false;
    }

    let scientist = &agents[0];
    let troll = &agents[1];
    let ally = &agents[2];

    let subject = "Entity_X";
    let predicate = "property";

    // Scientist's high-confidence assertion
    let scientist_assertion = scientist.sign_assertion_with_options(
        subject,
        predicate,
        ObjectValue::Text("verified_value".to_string()),
        LifecycleStage::Proposed,
        Some(2000),
    );

    // Troll's low-confidence contradicting assertion
    let troll_assertion = troll.sign_assertion_with_options(
        subject,
        predicate,
        ObjectValue::Text("fake_value".to_string()),
        LifecycleStage::Proposed,
        Some(2001),
    );

    // Write both assertions to WAL and track last offset
    let _scientist_result = match write_assertion_to_wal(journal, &scientist_assertion).await {
        Ok(r) => r,
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::WriteFailure,
                message: format!(
                    "Troll resistance test: failed to write scientist assertion: {}",
                    e
                ),
            });
            return false;
        }
    };
    result.assertions_written += 1;

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

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

    // Compute assertion hashes
    let scientist_hash = compute_assertion_hash(&scientist_assertion);
    let troll_hash = compute_assertion_hash(&troll_assertion);

    // Scientist votes for own assertion (weight 1.0)
    let scientist_vote = scientist.vote(scientist_hash, 1.0);
    if let Err(e) = write_vote_to_wal(journal, &scientist_vote).await {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::VoteWriteFailure,
            message: format!("Troll resistance test: failed to write scientist vote: {}", e),
        });
        return false;
    }
    result.votes_written += 1;

    // Troll votes for own assertion (weight 1.0)
    let troll_vote = troll.vote(troll_hash, 1.0);
    if let Err(e) = write_vote_to_wal(journal, &troll_vote).await {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::VoteWriteFailure,
            message: format!("Troll resistance test: failed to write troll vote: {}", e),
        });
        return false;
    }
    result.votes_written += 1;

    // Ally votes for scientist's assertion (weight 1.0) - tips balance in scientist's favor
    let ally_vote = ally.vote(scientist_hash, 1.0);
    last_offset = match write_vote_to_wal(journal, &ally_vote).await {
        Ok(offset) => offset,
        Err(e) => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::VoteWriteFailure,
                message: format!("Troll resistance test: failed to write ally vote: {}", e),
            });
            return false;
        }
    };
    result.votes_written += 1;

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

    // Query to get candidates
    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!("Troll resistance test: query failed: {}", e),
            });
            return false;
        }
    };

    if query_result.assertions.len() < 2 {
        result.errors.push(SimulationError {
            tick: 0,
            kind: ErrorKind::QueryFailure,
            message: format!(
                "Troll resistance test: expected 2 assertions, got {}",
                query_result.assertions.len()
            ),
        });
        return false;
    }

    // Apply VoteAwareConsensusLens
    let vote_store = Arc::new(GenericVoteStore::new(store.clone()));
    let lens = VoteAwareConsensusLens::new(vote_store);
    let resolution = lens.resolve_async(&query_result.assertions).await;

    match resolution.winner {
        Some(winner) => {
            // Winner should be scientist's assertion (verified_value) - it has 2 votes vs troll's 1
            if let ObjectValue::Text(ref value) = winner.object {
                if value == "verified_value" {
                    debug!("  Troll resistance: consensus correctly resisted troll (2 votes vs 1)");
                    result.queries_executed += 1;
                    return true;
                }
            }
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::VoteConsensusFailure,
                message: format!(
                    "Troll resistance test: troll won! (expected verified_value, got {:?})",
                    winner.object
                ),
            });
            false
        }
        None => {
            result.errors.push(SimulationError {
                tick: 0,
                kind: ErrorKind::VoteConsensusFailure,
                message: "Troll resistance test: lens returned no winner".to_string(),
            });
            false
        }
    }
}
