// Copyright 2019-2026 Apilium Technologies OÜ. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 OR Commercial

//! The core `Agent` trait and a simple, concrete implementation.
//!
//! This module provides the fundamental building blocks for creating agents
//! within the Kaneru framework.

use crate::action::{Action, ActionResult};
use crate::config::AgentConfig;
use crate::goal::{Goal, GoalManager};
use crate::learning::{ActionId, LearningConfig, LearningEngine, StateId};
use crate::observation::{Observation, ObservationBuffer};
use crate::policy::{Policy, PolicyEngine, Rule};
use crate::types::Timestamp;
use serde::{Deserialize, Serialize};

/// A unique identifier for an agent.
///
/// Agent IDs are automatically generated by combining the agent's name with a timestamp,
/// ensuring global uniqueness even when multiple agents share the same name.
///
/// # Examples
///
/// ```
/// # use kaneru::AgentId;
/// let id = AgentId::new("sensor_agent");
/// // ID will be something like "sensor_agent_1234567890"
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AgentId(pub String);

impl AgentId {
    /// Creates a new `AgentId` using a name and the current timestamp to ensure uniqueness.
    ///
    /// The timestamp ensures that even if two agents are created with the same name,
    /// they will have different IDs.
    ///
    /// # Arguments
    ///
    /// * `name` - The base name for the agent. This will be combined with a timestamp.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::AgentId;
    /// let id1 = AgentId::new("agent");
    /// let id2 = AgentId::new("agent");
    /// assert_ne!(id1, id2); // Different due to timestamps
    /// ```
    pub fn new(name: &str) -> Self {
        Self(format!("{}_{}", name, Timestamp::now().0))
    }
}

/// Represents the current operational state of an agent.
///
/// The agent state tracks what phase of the agent's lifecycle it is currently in,
/// from initialization through execution and learning, to eventual termination.
///
/// # State Transitions
///
/// Normal operation flow:
/// - `Initializing` -> `Idle` -> `Processing` -> `Executing` -> `Learning` -> `Idle`
///
/// The agent can be paused/resumed or stopped at any time:
/// - Any state -> `Paused` -> Any previous state
/// - Any state -> `Stopped` (terminal)
/// - Any state -> `Error` (terminal, requires restart)
///
/// # Examples
///
/// ```
/// # use kaneru::AgentState;
/// let state = AgentState::default();
/// assert_eq!(state, AgentState::Initializing);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum AgentState {
    /// The agent is being initialized.
    #[default]
    Initializing,
    /// The agent is active but waiting for new observations or events.
    Idle,
    /// The agent is actively processing new observations.
    Processing,
    /// The agent is executing an action in its environment.
    Executing,
    /// The agent is updating its internal models based on recent outcomes.
    Learning,
    /// The agent's operation is temporarily suspended.
    Paused,
    /// The agent has terminated its operation.
    Stopped,
    /// The agent has encountered an unrecoverable error.
    Error,
}

/// The core trait defining the capabilities and lifecycle of all agents.
///
/// The `Agent` trait defines the fundamental interface that all agents in the Kaneru framework
/// must implement. It follows a sense-decide-act-learn cycle inspired by reinforcement learning
/// and autonomous systems design.
///
/// # Lifecycle
///
/// A typical agent lifecycle involves:
/// 1. **Observe** - Receive observations from the environment
/// 2. **Decide** - Choose an action based on current state and goals
/// 3. **Execute** - Perform the action in the environment
/// 4. **Learn** - Update internal models based on outcomes
///
/// # Examples
///
/// ```
/// # use kaneru::{Agent, SimpleAgent, Observation, Action};
/// let mut agent = SimpleAgent::new("example");
///
/// // Observe
/// agent.observe(Observation::sensor("temperature", 25.0));
///
/// // Decide
/// let action = agent.decide();
///
/// // Execute
/// let result = agent.execute(action.clone());
///
/// // Learn
/// let obs = Observation::sensor("temperature", 24.0);
/// agent.learn(&obs, &action, &result);
/// ```
///
/// # See Also
///
/// - [`SimpleAgent`] for a concrete implementation
/// - `KaneruAgent` for an advanced implementation with hierarchical goals and learning
pub trait Agent {
    /// Returns the unique identifier of the agent.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent};
    /// let agent = SimpleAgent::new("my_agent");
    /// println!("Agent ID: {:?}", agent.id());
    /// ```
    fn id(&self) -> &AgentId;

    /// Returns the human-readable name of the agent.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent};
    /// let agent = SimpleAgent::new("my_agent");
    /// assert_eq!(agent.name(), "my_agent");
    /// ```
    fn name(&self) -> &str;

    /// Returns the current operational state of the agent.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, AgentState};
    /// let agent = SimpleAgent::new("my_agent");
    /// assert_eq!(agent.state(), AgentState::Idle);
    /// ```
    fn state(&self) -> AgentState;

    /// Provides a new observation to the agent.
    ///
    /// This is the primary way an agent perceives its environment. Observations
    /// can come from sensors, network events, user inputs, or any other source.
    /// The agent stores observations in an internal buffer for decision-making.
    ///
    /// # Arguments
    ///
    /// * `observation` - The observation to process
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, Observation};
    /// let mut agent = SimpleAgent::new("sensor_agent");
    /// agent.observe(Observation::sensor("temperature", 23.5));
    /// assert_eq!(agent.stats().observations_received, 1);
    /// ```
    fn observe(&mut self, observation: Observation);

    /// Makes a decision based on the agent's current state, observations, and goals.
    ///
    /// The decision process involves evaluating the current observations against
    /// the agent's policies and goals to select an appropriate action. If no
    /// action is warranted, this returns a [`Action::noop()`].
    ///
    /// # Returns
    ///
    /// An [`Action`] to be executed. This may be a `NoOp` action if no action is needed.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, Observation, Action};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// agent.observe(Observation::sensor("temperature", 25.0));
    /// let action = agent.decide();
    /// // Action may be NoOp or a specific action based on policies
    /// ```
    fn decide(&self) -> Action;

    /// Executes a given action in the environment.
    ///
    /// This method performs the action and returns a result indicating success or failure.
    /// The execution may interact with external systems, update internal state, or
    /// trigger other operations.
    ///
    /// # Arguments
    ///
    /// * `action` - The action to execute
    ///
    /// # Returns
    ///
    /// An [`ActionResult`] indicating the outcome of the action, including success status,
    /// return values, and any error messages.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, Action};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// let action = Action::store("key", "value");
    /// let result = agent.execute(action);
    /// assert!(result.success);
    /// ```
    fn execute(&mut self, action: Action) -> ActionResult;

    /// Allows the agent to learn from the outcome of its actions.
    ///
    /// This is a key part of reinforcement learning. The agent updates its internal
    /// models (such as Q-values in Q-learning) based on the observation, action, and
    /// result. Over time, this allows the agent to improve its decision-making.
    ///
    /// # Arguments
    ///
    /// * `observation` - The new observation received after the action was executed
    /// * `action` - The action that was taken
    /// * `result` - The result of that action
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, Observation, Action, ActionResult};
    /// let mut agent = SimpleAgent::new("learning_agent");
    /// let obs = Observation::sensor("temperature", 25.0);
    /// let action = Action::store("data", "value");
    /// let result = ActionResult::success(&action.id);
    ///
    /// agent.learn(&obs, &action, &result);
    /// ```
    fn learn(&mut self, observation: &Observation, action: &Action, result: &ActionResult);

    /// Runs a single, complete step of the agent's lifecycle: decide and execute.
    ///
    /// This is a convenience method that combines the decide and execute phases.
    /// If the decided action is a `NoOp`, this method returns `None` without executing.
    ///
    /// # Returns
    ///
    /// `Some(ActionResult)` if an action was executed, `None` if the action was `NoOp`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, Observation};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// agent.observe(Observation::sensor("temperature", 25.0));
    ///
    /// if let Some(result) = agent.step() {
    ///     println!("Action executed: success={}", result.success);
    /// }
    /// ```
    fn step(&mut self) -> Option<ActionResult> {
        let action = self.decide();
        if action.is_noop() {
            return None;
        }
        Some(self.execute(action))
    }

    /// Returns a reference to the agent's configuration.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent};
    /// let agent = SimpleAgent::new("my_agent");
    /// println!("Max goals: {}", agent.config().max_goals);
    /// ```
    fn config(&self) -> &AgentConfig;
}

/// A simple, concrete implementation of the [`Agent`] trait.
///
/// `SimpleAgent` provides a reactive architecture with rule-based decision-making and
/// optional reinforcement learning capabilities. It is designed for both simple reactive
/// behaviors and more complex goal-oriented tasks.
///
/// # Features
///
/// - **Policy-based decisions**: Uses a [`PolicyEngine`] to evaluate rules and conditions
/// - **Goal management**: Can track and pursue multiple goals simultaneously
/// - **Learning**: Optional Q-learning support for adaptive behavior
/// - **Observation buffering**: Maintains a history of recent observations
/// - **Statistics tracking**: Monitors performance metrics
///
/// # Examples
///
/// ## Basic Usage
///
/// ```
/// # use kaneru::{Agent, SimpleAgent, Observation, Rule, Condition, Action};
/// let mut agent = SimpleAgent::new("temperature_monitor");
///
/// // Add a rule
/// let rule = Rule::new(
///     "high_temp",
///     Condition::above("temperature", 30.0),
///     Action::alert("Temperature too high!"),
/// );
/// agent.add_rule(rule);
///
/// // Process observations
/// agent.observe(Observation::sensor("temperature", 35.0));
/// let action = agent.decide();
/// let result = agent.execute(action);
/// ```
///
/// ## With Learning
///
/// ```
/// # use kaneru::{Agent, SimpleAgent, Observation, Action};
/// # use kaneru::learning::{LearningConfig, LearningAlgorithm};
/// let mut agent = SimpleAgent::new("learning_agent");
///
/// // Enable learning
/// let config = LearningConfig {
///     learning_rate: 0.1,
///     discount_factor: 0.9,
///     algorithm: LearningAlgorithm::QLearning,
///     ..Default::default()
/// };
/// agent.enable_learning(config);
///
/// // Agent will now learn from its actions
/// for _ in 0..100 {
///     agent.observe(Observation::sensor("state", 1.0));
///     let action = agent.decide();
///     let result = agent.execute(action.clone());
///     let obs = Observation::sensor("state", 2.0);
///     agent.learn(&obs, &action, &result);
/// }
/// ```
///
/// # See Also
///
/// - [`Agent`] trait for the core interface
/// - `KaneruAgent` for advanced hierarchical goal-based agents
/// - [`AgentConfig`] for configuration options
pub struct SimpleAgent {
    /// The unique identifier for the agent.
    id: AgentId,
    /// The agent's configuration settings.
    config: AgentConfig,
    /// The current operational state of the agent.
    state: AgentState,
    /// The engine that evaluates policies and rules to make decisions.
    policy_engine: PolicyEngine,
    /// Manages the agent's goals and their statuses.
    goals: GoalManager,
    /// A buffer of recent observations.
    observations: ObservationBuffer,
    /// The most recent observation received by the agent.
    last_observation: Option<Observation>,
    /// A history of actions taken and their results.
    action_history: Vec<(Action, ActionResult)>,
    /// Performance and operational statistics for the agent.
    stats: AgentStats,
    /// An optional learning engine for reinforcement learning.
    learning_engine: Option<LearningEngine>,
    /// The last state-action pair, used for Q-learning updates.
    last_state_action: Option<(StateId, ActionId)>,
}

impl SimpleAgent {
    /// Creates a new `SimpleAgent` with a given name and default configuration.
    ///
    /// The default configuration includes:
    /// - Learning enabled with Q-learning
    /// - Maximum of 10 concurrent goals
    /// - Observation buffer of 100 items
    /// - Normal memory limits (not IoT mode)
    ///
    /// # Arguments
    ///
    /// * `name` - A unique name for the agent
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent};
    /// let agent = SimpleAgent::new("my_agent");
    /// assert_eq!(agent.name(), "my_agent");
    /// ```
    ///
    /// # See Also
    ///
    /// - [`SimpleAgent::with_config`] for custom configuration
    pub fn new(name: &str) -> Self {
        let config = AgentConfig::new(name);
        Self::with_config(name, config)
    }

    /// Creates a new `SimpleAgent` with a custom configuration.
    ///
    /// This allows full control over the agent's configuration, including memory limits,
    /// learning settings, and goal capacity.
    ///
    /// # Arguments
    ///
    /// * `name` - A unique name for the agent (overrides config.name if different)
    /// * `config` - The configuration to use
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, AgentConfig};
    /// let config = AgentConfig::iot_mode();
    /// let agent = SimpleAgent::with_config("iot_agent", config);
    /// assert!(agent.config().max_memory_bytes <= 128 * 1024);
    /// ```
    ///
    /// # See Also
    ///
    /// - [`AgentConfig::new`] for default configuration
    /// - [`AgentConfig::iot_mode`] for IoT-optimized settings
    pub fn with_config(name: &str, config: AgentConfig) -> Self {
        let max_goals = config.max_goals;
        let learning_enabled = config.learning_enabled;
        // Override config name with provided name
        let mut config = config;
        config.name = name.to_string();

        // Initialize learning engine if enabled
        let learning_engine = if learning_enabled {
            Some(LearningEngine::default_config())
        } else {
            None
        };

        Self {
            id: AgentId::new(name),
            config,
            state: AgentState::Idle,
            policy_engine: PolicyEngine::new(),
            goals: GoalManager::new(max_goals),
            observations: ObservationBuffer::new(100),
            last_observation: None,
            action_history: Vec::new(),
            stats: AgentStats::default(),
            learning_engine,
            last_state_action: None,
        }
    }

    /// Adds a [`Policy`] to the agent's policy engine.
    ///
    /// Policies contain collections of rules that define the agent's reactive behavior.
    /// Multiple policies can be added, and they will be evaluated in order.
    ///
    /// # Arguments
    ///
    /// * `policy` - The policy to add
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{SimpleAgent, Policy, Rule, Condition, Action};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// let mut policy = Policy::new("safety");
    /// policy.add_rule(Rule::new(
    ///     "emergency",
    ///     Condition::above("pressure", 100.0),
    ///     Action::alert("Pressure too high!"),
    /// ));
    /// agent.add_policy(policy);
    /// ```
    pub fn add_policy(&mut self, policy: Policy) {
        self.policy_engine.add_policy(policy);
    }

    /// Adds a [`Rule`] to the agent's default policy.
    ///
    /// This is a convenience method for simple rule-based behavior. Each rule consists
    /// of a condition and an action to take when the condition is met.
    ///
    /// # Arguments
    ///
    /// * `rule` - The rule to add
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{SimpleAgent, Rule, Condition, Action};
    /// let mut agent = SimpleAgent::new("temperature_monitor");
    /// agent.add_rule(Rule::new(
    ///     "high_temp",
    ///     Condition::above("temperature", 30.0),
    ///     Action::alert("Temperature too high!"),
    /// ));
    /// ```
    pub fn add_rule(&mut self, rule: Rule) {
        let mut policy = Policy::new("default");
        policy.add_rule(rule);
        self.policy_engine.add_policy(policy);
    }

    /// Sets the exploration rate (epsilon) for the epsilon-greedy policy.
    ///
    /// This controls the trade-off between exploration (trying new actions) and
    /// exploitation (using known good actions). A value of 0.0 makes the agent
    /// fully greedy (exploitative), while 1.0 makes it fully random (exploratory).
    ///
    /// # Arguments
    ///
    /// * `rate` - The exploration rate, should be between 0.0 and 1.0
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::SimpleAgent;
    /// let mut agent = SimpleAgent::new("explorer");
    /// agent.set_exploration_rate(0.1); // 10% exploration, 90% exploitation
    /// ```
    pub fn set_exploration_rate(&mut self, rate: f32) {
        self.policy_engine.set_exploration_rate(rate);
    }

    /// Adds a [`Goal`] to the agent's goal manager.
    ///
    /// Goals provide high-level objectives for the agent to pursue. The agent can
    /// manage multiple goals simultaneously, prioritized by their priority level.
    ///
    /// # Arguments
    ///
    /// * `goal` - The goal to add
    ///
    /// # Returns
    ///
    /// `Some(goal_id)` if the goal was added successfully, `None` if the goal manager
    /// is at capacity and couldn't remove completed goals to make space.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{SimpleAgent, Goal};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// let goal = Goal::maintain("temperature", 20.0..25.0);
    /// if let Some(goal_id) = agent.add_goal(goal) {
    ///     println!("Goal added with ID: {}", goal_id);
    /// }
    /// ```
    pub fn add_goal(&mut self, goal: Goal) -> Option<String> {
        self.goals.add(goal)
    }

    /// Sets a goal for the agent.
    ///
    /// This is a convenience method equivalent to [`add_goal`](Self::add_goal) but
    /// without returning the goal ID.
    ///
    /// # Arguments
    ///
    /// * `goal` - The goal to set
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{SimpleAgent, Goal};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// agent.set_goal(Goal::maximize("efficiency"));
    /// ```
    pub fn set_goal(&mut self, goal: Goal) {
        self.goals.add(goal);
    }

    /// Returns a list of the agent's currently active goals.
    ///
    /// Active goals are those with status `GoalStatus::Active`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{SimpleAgent, Goal};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// let mut goal = Goal::maximize("score");
    /// goal.activate();
    /// agent.add_goal(goal);
    /// assert_eq!(agent.active_goals().len(), 1);
    /// ```
    pub fn active_goals(&self) -> Vec<&Goal> {
        self.goals.active_goals()
    }

    /// Returns the `count` most recent observations.
    ///
    /// This is useful for examining recent environmental state or debugging
    /// the agent's perception.
    ///
    /// # Arguments
    ///
    /// * `count` - The number of recent observations to retrieve
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, Observation};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// agent.observe(Observation::sensor("temp", 20.0));
    /// agent.observe(Observation::sensor("temp", 21.0));
    /// agent.observe(Observation::sensor("temp", 22.0));
    ///
    /// let recent = agent.recent_observations(2);
    /// assert_eq!(recent.len(), 2);
    /// ```
    pub fn recent_observations(&self, count: usize) -> Vec<&Observation> {
        self.observations.get_recent(count)
    }

    /// Returns a reference to the agent's statistics.
    ///
    /// Statistics include observation counts, action counts, success rates, and
    /// learning updates.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, Observation};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// agent.observe(Observation::sensor("temp", 20.0));
    /// assert_eq!(agent.stats().observations_received, 1);
    /// println!("Success rate: {:.2}", agent.stats().success_rate());
    /// ```
    pub fn stats(&self) -> &AgentStats {
        &self.stats
    }

    /// Pauses the agent, preventing it from executing further steps.
    ///
    /// A paused agent can be resumed with [`resume`](Self::resume). While paused,
    /// the agent can still receive observations but won't make decisions or execute actions.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, AgentState};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// agent.pause();
    /// assert_eq!(agent.state(), AgentState::Paused);
    /// ```
    pub fn pause(&mut self) {
        self.state = AgentState::Paused;
    }

    /// Resumes a paused agent.
    ///
    /// If the agent is not paused, this method has no effect.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, AgentState};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// agent.pause();
    /// agent.resume();
    /// assert_eq!(agent.state(), AgentState::Idle);
    /// ```
    pub fn resume(&mut self) {
        if self.state == AgentState::Paused {
            self.state = AgentState::Idle;
        }
    }

    /// Stops the agent permanently.
    ///
    /// Once stopped, the agent cannot be restarted. This is a terminal state.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// agent.stop();
    /// assert!(!agent.is_running());
    /// ```
    pub fn stop(&mut self) {
        self.state = AgentState::Stopped;
    }

    /// Returns `true` if the agent is in a running state.
    ///
    /// An agent is considered running if it's not in [`AgentState::Stopped`] or
    /// [`AgentState::Error`] state.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent};
    /// let mut agent = SimpleAgent::new("my_agent");
    /// assert!(agent.is_running());
    ///
    /// agent.stop();
    /// assert!(!agent.is_running());
    /// ```
    pub fn is_running(&self) -> bool {
        !matches!(self.state, AgentState::Stopped | AgentState::Error)
    }

    /// Enables the learning engine with a custom configuration.
    ///
    /// This allows the agent to learn from its experiences using reinforcement learning.
    /// The learning engine uses Q-learning by default to improve action selection over time.
    ///
    /// # Arguments
    ///
    /// * `config` - The learning configuration to use
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{SimpleAgent, learning::{LearningConfig, LearningAlgorithm}};
    /// let mut agent = SimpleAgent::new("learner");
    /// let config = LearningConfig {
    ///     learning_rate: 0.1,
    ///     discount_factor: 0.9,
    ///     algorithm: LearningAlgorithm::QLearning,
    ///     ..Default::default()
    /// };
    /// agent.enable_learning(config);
    /// assert!(agent.learning_engine().is_some());
    /// ```
    ///
    /// # See Also
    ///
    /// - [`LearningConfig`] for configuration options
    /// - [`enable_learning_default`](Self::enable_learning_default) for default settings
    pub fn enable_learning(&mut self, config: LearningConfig) {
        self.config.learning_enabled = true;
        self.learning_engine = Some(LearningEngine::new(config));
    }

    /// Enables the learning engine with default configuration.
    ///
    /// This is a convenience method that enables Q-learning with standard parameters.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::SimpleAgent;
    /// let mut agent = SimpleAgent::new("learner");
    /// agent.enable_learning_default();
    /// assert!(agent.learning_engine().is_some());
    /// ```
    pub fn enable_learning_default(&mut self) {
        self.config.learning_enabled = true;
        self.learning_engine = Some(LearningEngine::default_config());
    }

    /// Disables the learning engine.
    ///
    /// This stops the agent from learning and clears any stored Q-values or learning state.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::SimpleAgent;
    /// let mut agent = SimpleAgent::new("learner");
    /// agent.enable_learning_default();
    /// agent.disable_learning();
    /// assert!(agent.learning_engine().is_none());
    /// ```
    pub fn disable_learning(&mut self) {
        self.config.learning_enabled = false;
        self.learning_engine = None;
    }

    /// Returns a reference to the learning engine, if enabled.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::SimpleAgent;
    /// let agent = SimpleAgent::new("learner");
    /// if let Some(engine) = agent.learning_engine() {
    ///     println!("Total updates: {}", engine.total_updates());
    /// }
    /// ```
    pub fn learning_engine(&self) -> Option<&LearningEngine> {
        self.learning_engine.as_ref()
    }

    /// Returns a mutable reference to the learning engine, if enabled.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::SimpleAgent;
    /// let mut agent = SimpleAgent::new("learner");
    /// if let Some(engine) = agent.learning_engine_mut() {
    ///     // Modify engine settings
    /// }
    /// ```
    pub fn learning_engine_mut(&mut self) -> Option<&mut LearningEngine> {
        self.learning_engine.as_mut()
    }

    /// Gets a list of available actions from the policy engine for a given observation.
    fn get_available_actions(&self, obs: &Observation) -> Vec<ActionId> {
        // Get action from policy engine
        let policy_action = self.policy_engine.decide(obs);

        // For now, return the policy action plus some common actions
        // In a more sophisticated implementation, this would be context-dependent
        vec![
            ActionId::from_action(&policy_action),
            ActionId::from_action(&Action::noop()),
            ActionId::from_action(&Action::wait()),
        ]
    }
}

impl Agent for SimpleAgent {
    fn id(&self) -> &AgentId {
        &self.id
    }

    fn name(&self) -> &str {
        &self.config.name
    }

    fn state(&self) -> AgentState {
        self.state
    }

    fn observe(&mut self, observation: Observation) {
        self.state = AgentState::Processing;
        self.observations.push(observation.clone());
        self.last_observation = Some(observation);
        self.stats.observations_received += 1;
    }

    fn decide(&self) -> Action {
        if let Some(ref obs) = self.last_observation {
            self.policy_engine.decide(obs)
        } else {
            Action::noop()
        }
    }

    fn execute(&mut self, action: Action) -> ActionResult {
        self.state = AgentState::Executing;
        self.stats.actions_executed += 1;

        // Store state-action pair for learning
        if self.config.learning_enabled && self.last_observation.is_some() {
            let state_id = StateId::from_observation(self.last_observation.as_ref().unwrap());
            let action_id = ActionId::from_action(&action);
            self.last_state_action = Some((state_id, action_id));
        }

        // Simple execution - just log and return success
        log::debug!("Executing action: {:?}", action.action_type);

        let result = ActionResult::success(&action.id);

        // Store in history
        if self.action_history.len() >= 100 {
            self.action_history.remove(0);
        }
        self.action_history.push((action, result.clone()));

        self.state = AgentState::Idle;
        result
    }

    fn learn(&mut self, observation: &Observation, action: &Action, result: &ActionResult) {
        if !self.config.learning_enabled {
            return;
        }

        self.state = AgentState::Learning;

        // Track success rate (existing behavior)
        if result.success {
            self.stats.successful_actions += 1;
        }

        // Prepare learning data before borrowing engine
        let next_state = StateId::from_observation(observation);
        let available_actions = self.get_available_actions(observation);
        let reward = if result.success { 1.0 } else { -1.0 };
        let next_action = ActionId::from_action(action);

        // Use learning engine if available
        if let Some(ref mut engine) = self.learning_engine {
            // Get previous state-action pair
            if let Some((prev_state, prev_action)) = &self.last_state_action {
                // Update Q-values
                engine.update(
                    prev_state,
                    prev_action,
                    reward,
                    &next_state,
                    Some(&next_action),
                    &available_actions,
                );

                self.stats.learning_updates += 1;
            }
        }

        self.state = AgentState::Idle;
    }

    fn config(&self) -> &AgentConfig {
        &self.config
    }
}

/// Collects statistics about an agent's performance and operation.
///
/// `AgentStats` tracks key performance indicators for an agent, including observation
/// and action counts, success rates, and learning progress. These metrics are useful
/// for monitoring agent behavior, debugging, and evaluating performance.
///
/// # Examples
///
/// ```
/// # use kaneru::{Agent, SimpleAgent, Observation, Action};
/// let mut agent = SimpleAgent::new("my_agent");
///
/// // Process some observations and actions
/// agent.observe(Observation::sensor("temp", 20.0));
/// let action = agent.decide();
/// agent.execute(action);
///
/// // Check statistics
/// let stats = agent.stats();
/// println!("Observations: {}", stats.observations_received);
/// println!("Actions: {}", stats.actions_executed);
/// println!("Success rate: {:.2}%", stats.success_rate() * 100.0);
/// ```
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AgentStats {
    /// The total number of observations received by the agent.
    pub observations_received: u64,
    /// The total number of actions executed by the agent.
    pub actions_executed: u64,
    /// The number of actions that completed successfully.
    pub successful_actions: u64,
    /// The number of goals the agent has successfully achieved.
    pub goals_achieved: u64,
    /// The number of goals the agent has failed to achieve.
    pub goals_failed: u64,
    /// The total number of times the learning model has been updated.
    pub learning_updates: u64,
}

impl AgentStats {
    /// Calculates the agent's action success rate.
    ///
    /// The success rate is the ratio of successful actions to total actions executed,
    /// returned as a value between 0.0 (no successes) and 1.0 (all successes).
    ///
    /// # Returns
    ///
    /// The success rate as a floating point number from 0.0 to 1.0. Returns 0.0 if
    /// no actions have been executed.
    ///
    /// # Examples
    ///
    /// ```
    /// # use kaneru::{Agent, SimpleAgent, Action};
    /// let mut agent = SimpleAgent::new("my_agent");
    ///
    /// // Execute some actions
    /// for _ in 0..10 {
    ///     agent.execute(Action::noop());
    /// }
    ///
    /// let rate = agent.stats().success_rate();
    /// assert!(rate >= 0.0 && rate <= 1.0);
    /// ```
    pub fn success_rate(&self) -> f64 {
        if self.actions_executed == 0 {
            0.0
        } else {
            self.successful_actions as f64 / self.actions_executed as f64
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::policy::Condition;

    #[test]
    fn test_agent_creation() {
        let agent = SimpleAgent::new("test_agent");
        assert_eq!(agent.name(), "test_agent");
        assert_eq!(agent.state(), AgentState::Idle);
    }

    #[test]
    fn test_agent_observe() {
        let mut agent = SimpleAgent::new("test");
        let obs = Observation::sensor("temp", 25.0);

        agent.observe(obs);
        assert_eq!(agent.stats().observations_received, 1);
    }

    #[test]
    fn test_agent_with_rule() {
        let mut agent = SimpleAgent::new("temp_monitor");

        // Disable exploration for deterministic test
        agent.set_exploration_rate(0.0);

        // Add a rule: if temp > 30, alert
        let rule = Rule::new(
            "high_temp",
            Condition::above("temperature", 30.0),
            Action::alert("Temperature too high!"),
        );
        agent.add_rule(rule);

        // Observe high temperature
        let obs = Observation::sensor("temperature", 35.0);
        agent.observe(obs);

        // Decide should return the alert action
        let action = agent.decide();
        assert!(matches!(
            action.action_type,
            crate::action::ActionType::Alert(_)
        ));
    }

    #[test]
    fn test_agent_execute() {
        let mut agent = SimpleAgent::new("test");

        let action = Action::store("key", "value");
        let result = agent.execute(action);

        assert!(result.success);
        assert_eq!(agent.stats().actions_executed, 1);
    }

    #[test]
    fn test_agent_lifecycle() {
        let mut agent = SimpleAgent::new("test");

        assert!(agent.is_running());

        agent.pause();
        assert_eq!(agent.state(), AgentState::Paused);

        agent.resume();
        assert_eq!(agent.state(), AgentState::Idle);

        agent.stop();
        assert!(!agent.is_running());
    }

    #[test]
    fn test_agent_learning() {
        use crate::learning::{LearningAlgorithm, LearningConfig};

        let mut agent = SimpleAgent::new("learning_agent");

        // Enable learning with custom config
        let config = LearningConfig {
            learning_rate: 0.1,
            discount_factor: 0.9,
            algorithm: LearningAlgorithm::QLearning,
            ..Default::default()
        };
        agent.enable_learning(config);

        // Verify learning is enabled
        assert!(agent.learning_engine().is_some());

        // Simulate a learning episode
        let obs1 = Observation::sensor("temperature", 25.0);
        agent.observe(obs1.clone());

        let action1 = agent.decide();
        let result1 = agent.execute(action1.clone());

        // Learn from the result
        agent.learn(&obs1, &action1, &result1);

        // Check that learning updates occurred
        assert_eq!(agent.stats().learning_updates, 1);

        // Get learning engine stats
        let engine = agent.learning_engine().unwrap();
        assert!(engine.total_updates() > 0);
    }

    #[test]
    fn test_agent_learning_toggle() {
        // Create agent with IoT config (learning disabled)
        let mut agent = SimpleAgent::with_config("test", crate::config::AgentConfig::iot_mode());

        // Learning should be disabled in IoT mode
        assert!(agent.learning_engine().is_none());

        // Enable learning
        agent.enable_learning_default();
        assert!(agent.learning_engine().is_some());

        // Disable learning
        agent.disable_learning();
        assert!(agent.learning_engine().is_none());
    }

    #[test]
    fn test_agent_multi_step_learning() {
        let mut agent = SimpleAgent::new("multi_step_agent");
        // Default config has learning enabled, so no need to enable explicitly

        // Run multiple learning steps
        for i in 0..10 {
            let obs = Observation::sensor("step", i as f64);
            agent.observe(obs.clone());

            let action = agent.decide();
            let result = agent.execute(action.clone());

            agent.learn(&obs, &action, &result);
        }

        // Verify learning occurred
        // Learning updates equals actions executed because learning is enabled from start
        assert_eq!(agent.stats().learning_updates, 10);
        assert_eq!(agent.stats().actions_executed, 10);

        let engine = agent.learning_engine().unwrap();
        assert!(engine.total_updates() >= 10);
        assert!(engine.state_action_count() > 0);
    }
}
