//! Agent strategy patterns for simulation.
//!
//! Defines different agent behaviors (Scientist, Troll, Believer) and
//! the framework for strategy-driven simulation.

use stemedb_core::types::{Hash, ObjectValue};

/// Specification for creating agents with a particular strategy.
#[derive(Debug, Clone, Copy)]
pub struct AgentSpec {
    /// Number of agents to create with this strategy.
    pub count: usize,
    /// The strategy type for these agents.
    pub strategy: StrategyType,
}

/// Available strategy types for agents.
#[derive(Debug, Clone, Copy)]
pub enum StrategyType {
    /// Scientific agent: creates high-confidence assertions.
    Scientist,
    /// Troll agent: creates low-confidence contradictions.
    Troll,
    /// Believer agent: primarily votes, occasionally creates assertions.
    Believer,
}

/// Metrics tracking for a strategy.
#[derive(Debug, Clone, Default)]
pub struct StrategyMetrics {
    /// Number of assertions created.
    pub assertions_created: usize,
    /// Number of votes cast.
    pub votes_cast: usize,
    /// Number of queries executed.
    pub queries_executed: usize,
    /// Number of skip actions taken.
    pub skips: usize,
}

/// World state visible to agents when making decisions.
#[derive(Debug, Clone)]
pub struct WorldState {
    /// Current simulation tick.
    pub tick: usize,
    /// Existing assertions (hash, subject, predicate, object, confidence).
    pub existing_assertions: Vec<(Hash, String, String, ObjectValue, f32)>,
    /// Vote summaries (assertion_hash, total_weight).
    pub vote_summaries: Vec<(Hash, f32)>,
    /// Total number of agents in the simulation.
    pub agent_count: usize,
}

/// Actions an agent can take in a simulation tick.
#[derive(Debug, Clone)]
pub enum AgentAction {
    /// Create a new assertion.
    Assert {
        /// The subject entity.
        subject: String,
        /// The predicate/relation.
        predicate: String,
        /// The object value.
        object: ObjectValue,
    },
    /// Cast a vote for an assertion.
    Vote {
        /// Hash of the assertion to vote on.
        assertion_hash: Hash,
        /// Vote weight (0.0 to 1.0).
        weight: f32,
    },
    /// Execute a query.
    Query {
        /// The subject to query.
        subject: String,
        /// The predicate to query.
        predicate: String,
    },
    /// Skip this turn.
    Skip,
}

/// Trait defining agent strategy behavior.
pub trait AgentStrategy: Send {
    /// Human-readable strategy name.
    fn name(&self) -> &str;

    /// Base confidence level for assertions created by this strategy.
    fn base_confidence(&self) -> f32;

    /// Decide what action to take given the current world state.
    fn decide_action(&self, world: &WorldState) -> AgentAction;
}

/// Scientist strategy: creates high-confidence assertions.
struct ScientistStrategy;

impl AgentStrategy for ScientistStrategy {
    fn name(&self) -> &str {
        "Scientist"
    }

    fn base_confidence(&self) -> f32 {
        0.9
    }

    fn decide_action(&self, world: &WorldState) -> AgentAction {
        let subject = format!("Entity_{}", world.tick);
        let predicate = "has_property".to_string();
        let object = ObjectValue::Text(format!("value_{}", world.tick));

        AgentAction::Assert { subject, predicate, object }
    }
}

/// Troll strategy: creates low-confidence contradictions.
struct TrollStrategy;

impl AgentStrategy for TrollStrategy {
    fn name(&self) -> &str {
        "Troll"
    }

    fn base_confidence(&self) -> f32 {
        0.3
    }

    fn decide_action(&self, world: &WorldState) -> AgentAction {
        // If there are existing assertions, contradict one
        if !world.existing_assertions.is_empty() {
            let target = &world.existing_assertions[world.tick % world.existing_assertions.len()];
            return AgentAction::Assert {
                subject: target.1.clone(),
                predicate: target.2.clone(),
                object: ObjectValue::Text(format!("fake_value_{}", world.tick)),
            };
        }

        // Otherwise create a generic low-confidence assertion
        AgentAction::Assert {
            subject: format!("TrollEntity_{}", world.tick),
            predicate: "dubious_claim".to_string(),
            object: ObjectValue::Text("unverified".to_string()),
        }
    }
}

/// Believer strategy: primarily votes, occasionally creates assertions.
struct BelieverStrategy;

impl AgentStrategy for BelieverStrategy {
    fn name(&self) -> &str {
        "Believer"
    }

    fn base_confidence(&self) -> f32 {
        0.7
    }

    fn decide_action(&self, world: &WorldState) -> AgentAction {
        // Believers prefer to vote on existing high-confidence assertions
        if !world.existing_assertions.is_empty() {
            // Find highest confidence assertion
            if let Some((hash, _, _, _, confidence)) = world
                .existing_assertions
                .iter()
                .max_by(|a, b| a.4.partial_cmp(&b.4).unwrap_or(std::cmp::Ordering::Equal))
            {
                if *confidence > 0.5 {
                    return AgentAction::Vote { assertion_hash: *hash, weight: 1.0 };
                }
            }
        }

        // Occasionally create an assertion or skip
        if world.tick % 3 == 0 {
            AgentAction::Assert {
                subject: format!("BeliefEntity_{}", world.tick),
                predicate: "trusted_fact".to_string(),
                object: ObjectValue::Text(format!("trusted_value_{}", world.tick)),
            }
        } else {
            AgentAction::Skip
        }
    }
}

/// Create a strategy instance for the given type.
pub fn create_strategy(strategy_type: StrategyType) -> Box<dyn AgentStrategy> {
    match strategy_type {
        StrategyType::Scientist => Box::new(ScientistStrategy),
        StrategyType::Troll => Box::new(TrollStrategy),
        StrategyType::Believer => Box::new(BelieverStrategy),
    }
}
