//! Python bindings for Agent

use pyo3::prelude::*;

use flowgentra_ai::core::agent::Agent;

use crate::error::to_py_err;
use crate::state::PySharedState;
use crate::config::PyAgentConfig;
use crate::{get_runtime, py_to_json};

// ─── PyAgent ────────────────────────────────────────────────────────────────

/// The Agent - main interface to FlowgentraAI.
///
/// Create an agent from a YAML config and run it with state.
///
/// Example:
///     agent = Agent.from_config_path("config.yaml")
///     agent.state["input"] = "hello"
///     result = agent.run()
///     print(result.to_dict())
#[pyclass(name = "Agent")]
pub struct PyAgent {
    inner: Agent,
}

#[pymethods]
impl PyAgent {
    /// Create an agent from a YAML config file path.
    ///
    /// This auto-discovers all registered handlers and builds the agent.
    #[staticmethod]
    fn from_config_path(config_path: &str) -> PyResult<Self> {
        let agent = flowgentra_ai::from_config_path(config_path).map_err(to_py_err)?;
        Ok(PyAgent { inner: agent })
    }

    /// Create an agent from an AgentConfig object.
    #[staticmethod]
    fn from_config(config: &PyAgentConfig) -> PyResult<Self> {
        let handlers = std::collections::HashMap::new();
        let conditions = std::collections::HashMap::new();
        let agent =
            Agent::from_config_inner(config.inner.clone(), handlers, conditions).map_err(to_py_err)?;
        Ok(PyAgent { inner: agent })
    }

    /// Get the agent's current state
    #[getter]
    fn state(&self) -> PySharedState {
        PySharedState {
            inner: self.inner.state.clone(),
        }
    }

    /// Set a value in the agent's state
    fn set_state(&mut self, key: &str, value: &Bound<'_, PyAny>) -> PyResult<()> {
        let val = py_to_json(value)?;
        self.inner.state.set(key, val);
        Ok(())
    }

    /// Run the agent and return the result state.
    fn run(&mut self) -> PyResult<PySharedState> {
        let rt = get_runtime();
        let result = rt
            .block_on(self.inner.run())
            .map_err(to_py_err)?;
        Ok(PySharedState { inner: result })
    }

    /// Run the agent with a thread ID for checkpointing and conversation memory.
    fn run_with_thread(&mut self, thread_id: &str) -> PyResult<PySharedState> {
        let rt = get_runtime();
        let result = rt
            .block_on(self.inner.run_with_thread(thread_id))
            .map_err(to_py_err)?;
        Ok(PySharedState { inner: result })
    }

    /// Get the agent's configuration
    #[getter]
    fn config(&self) -> PyAgentConfig {
        PyAgentConfig {
            inner: self.inner.config().clone(),
        }
    }

    /// Get the agent name from config
    #[getter]
    fn name(&self) -> String {
        self.inner.config().name.clone()
    }

    fn __repr__(&self) -> String {
        format!("Agent(name='{}')", self.inner.config().name)
    }
}

// ─── Free function ──────────────────────────────────────────────────────────

/// Create an agent from a YAML config file with auto-discovered handlers.
#[pyfunction]
pub fn py_from_config_path(config_path: &str) -> PyResult<PyAgent> {
    PyAgent::from_config_path(config_path)
}
