//! Python bindings for built-in tools (Calculator, Search, WebRequest, Files)

use pyo3::prelude::*;
use std::sync::Arc;

use flowgentra_ai::core::tools::builtin::{CalculatorTool, FilesTool, SearchTool, WebRequestTool};
use flowgentra_ai::core::tools::Tool;

use crate::error::to_py_err;
use crate::get_runtime;
use crate::{json_to_py, py_to_json};

// ─── PyCalculatorTool ────────────────────────────────────────────────────────

/// Built-in calculator tool for math operations.
///
/// Example:
///     calc = CalculatorTool()
///     result = calc.call({"operation": "add", "a": 2, "b": 3})
///     print(result["result"])  # 5.0
#[pyclass(name = "CalculatorTool")]
pub struct PyCalculatorTool {
    inner: Arc<CalculatorTool>,
}

#[pymethods]
impl PyCalculatorTool {
    #[new]
    fn new() -> Self {
        PyCalculatorTool {
            inner: Arc::new(CalculatorTool::new()),
        }
    }

    /// Call the calculator with a JSON dict input.
    fn call(&self, py: Python<'_>, input: &Bound<'_, PyAny>) -> PyResult<PyObject> {
        let json_input = py_to_json(input)?;
        let rt = get_runtime();
        let result = rt
            .block_on(self.inner.call(json_input))
            .map_err(to_py_err)?;
        json_to_py(py, &result)
    }

    /// Get the tool definition as a dict.
    fn definition(&self, py: Python<'_>) -> PyResult<PyObject> {
        let def = self.inner.definition();
        let val = serde_json::to_value(&def)
            .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{}", e)))?;
        json_to_py(py, &val)
    }

    fn __repr__(&self) -> String {
        "CalculatorTool()".to_string()
    }
}

// ─── PySearchTool ─────────────────────────────────────────────────────────────

/// Built-in search tool (mock implementation).
///
/// Example:
///     search = SearchTool()
///     result = search.call({"query": "rust programming"})
#[pyclass(name = "SearchTool")]
pub struct PySearchTool {
    inner: Arc<SearchTool>,
}

#[pymethods]
impl PySearchTool {
    #[new]
    fn new() -> Self {
        PySearchTool {
            inner: Arc::new(SearchTool::new()),
        }
    }

    fn call(&self, py: Python<'_>, input: &Bound<'_, PyAny>) -> PyResult<PyObject> {
        let json_input = py_to_json(input)?;
        let rt = get_runtime();
        let result = rt
            .block_on(self.inner.call(json_input))
            .map_err(to_py_err)?;
        json_to_py(py, &result)
    }

    fn definition(&self, py: Python<'_>) -> PyResult<PyObject> {
        let def = self.inner.definition();
        let val = serde_json::to_value(&def)
            .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{}", e)))?;
        json_to_py(py, &val)
    }

    fn __repr__(&self) -> String {
        "SearchTool()".to_string()
    }
}

// ─── PyWebRequestTool ────────────────────────────────────────────────────────

/// Built-in HTTP request tool (mock implementation).
///
/// Example:
///     web = WebRequestTool()
///     result = web.call({"url": "https://example.com"})
#[pyclass(name = "WebRequestTool")]
pub struct PyWebRequestTool {
    inner: Arc<WebRequestTool>,
}

#[pymethods]
impl PyWebRequestTool {
    #[new]
    fn new() -> Self {
        PyWebRequestTool {
            inner: Arc::new(WebRequestTool::new()),
        }
    }

    fn call(&self, py: Python<'_>, input: &Bound<'_, PyAny>) -> PyResult<PyObject> {
        let json_input = py_to_json(input)?;
        let rt = get_runtime();
        let result = rt
            .block_on(self.inner.call(json_input))
            .map_err(to_py_err)?;
        json_to_py(py, &result)
    }

    fn definition(&self, py: Python<'_>) -> PyResult<PyObject> {
        let def = self.inner.definition();
        let val = serde_json::to_value(&def)
            .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{}", e)))?;
        json_to_py(py, &val)
    }

    fn __repr__(&self) -> String {
        "WebRequestTool()".to_string()
    }
}

// ─── PyFilesTool ──────────────────────────────────────────────────────────────

/// Built-in file operations tool (read, write, list).
///
/// Example:
///     files = FilesTool()
///     result = files.call({"operation": "read", "path": "example.txt"})
#[pyclass(name = "FilesTool")]
pub struct PyFilesTool {
    inner: Arc<FilesTool>,
}

#[pymethods]
impl PyFilesTool {
    #[new]
    fn new() -> Self {
        PyFilesTool {
            inner: Arc::new(FilesTool::new()),
        }
    }

    fn call(&self, py: Python<'_>, input: &Bound<'_, PyAny>) -> PyResult<PyObject> {
        let json_input = py_to_json(input)?;
        let rt = get_runtime();
        let result = rt
            .block_on(self.inner.call(json_input))
            .map_err(to_py_err)?;
        json_to_py(py, &result)
    }

    fn definition(&self, py: Python<'_>) -> PyResult<PyObject> {
        let def = self.inner.definition();
        let val = serde_json::to_value(&def)
            .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{}", e)))?;
        json_to_py(py, &val)
    }

    fn __repr__(&self) -> String {
        "FilesTool()".to_string()
    }
}
