//! Shared type conversions between Rust core types and Python objects.
//!
//! ## Conversion Strategy
//!
//! | Rust Type                  | Python Type          | Approach               |
//! |----------------------------|----------------------|------------------------|
//! | `String`                   | `str`                | PyO3 automatic         |
//! | `Vec<f32>`                 | `list[float]`        | PyO3 automatic         |
//! | `HashMap<String, Value>`   | `dict`               | serde_json → PyDict    |
//! | `Option<T>`                | `T | None`           | PyO3 automatic         |
//! | `Result<T, E>`             | `T` / raise          | PyO3 automatic         |
//! | `ContentType`              | `str` enum           | #[pyclass] enum        |
//! | `StoreType`                | `str` enum           | #[pyclass] enum        |
//! | `Record`                   | `dict`               | serde_json conversion  |

use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList};
use serde_json::Value;

// ---- Enums ----

/// Content type for stored records.
///
/// Maps to `aifastdb_core::types::memory::ContentType`.
#[pyclass(eq, eq_int)]
#[derive(Clone, Debug, PartialEq)]
pub enum ContentType {
    /// Plain text content
    Text = 0,
    /// JSON structured data
    Json = 1,
    /// Binary data
    Binary = 2,
    /// Embedding vector
    Embedding = 3,
    /// Code snippet
    Code = 4,
}

/// Store type in the federation.
///
/// Maps to `aifastdb_core::federation::StoreType`.
#[pyclass(eq, eq_int)]
#[derive(Clone, Debug, PartialEq)]
pub enum StoreType {
    /// Social graph storage (SocialGraphV2)
    Graph = 0,
    /// Document storage (EnhancedDocumentStore)
    Document = 1,
    /// Vector storage (ConcurrentHNSWIndex)
    Vector = 2,
}

// ---- Conversion Helpers ----

/// Convert a `serde_json::Value` to a Python object.
pub fn json_value_to_py(py: Python<'_>, value: &Value) -> PyResult<PyObject> {
    match value {
        Value::Null => Ok(py.None()),
        Value::Bool(b) => Ok(b.into_py(py)),
        Value::Number(n) => {
            if let Some(i) = n.as_i64() {
                Ok(i.into_py(py))
            } else if let Some(f) = n.as_f64() {
                Ok(f.into_py(py))
            } else {
                Ok(py.None())
            }
        }
        Value::String(s) => Ok(s.into_py(py)),
        Value::Array(arr) => {
            let items: Vec<PyObject> = arr
                .iter()
                .map(|item| json_value_to_py(py, item))
                .collect::<PyResult<_>>()?;
            Ok(PyList::new_bound(py, &items).into_py(py))
        }
        Value::Object(obj) => {
            let dict = PyDict::new_bound(py);
            for (key, val) in obj {
                dict.set_item(key, json_value_to_py(py, val)?)?;
            }
            Ok(dict.into_py(py))
        }
    }
}

/// Convert a Python object to `serde_json::Value`.
pub fn py_to_json_value(obj: &Bound<'_, PyAny>) -> PyResult<Value> {
    if obj.is_none() {
        Ok(Value::Null)
    } else if let Ok(b) = obj.extract::<bool>() {
        Ok(Value::Bool(b))
    } else if let Ok(i) = obj.extract::<i64>() {
        Ok(Value::Number(serde_json::Number::from(i)))
    } else if let Ok(f) = obj.extract::<f64>() {
        Ok(serde_json::Number::from_f64(f)
            .map(Value::Number)
            .unwrap_or(Value::Null))
    } else if let Ok(s) = obj.extract::<String>() {
        Ok(Value::String(s))
    } else if let Ok(list) = obj.downcast::<PyList>() {
        let mut arr = Vec::with_capacity(list.len());
        for item in list.iter() {
            arr.push(py_to_json_value(&item)?);
        }
        Ok(Value::Array(arr))
    } else if let Ok(dict) = obj.downcast::<PyDict>() {
        let mut map = serde_json::Map::new();
        for (key, val) in dict.iter() {
            let key_str: String = key.extract()?;
            map.insert(key_str, py_to_json_value(&val)?);
        }
        Ok(Value::Object(map))
    } else {
        // Fallback: convert to string representation
        let repr = obj.str()?.to_string();
        Ok(Value::String(repr))
    }
}

/// Convert a `serde_json::Value` (expected to be an Object) to a Python dict.
pub fn json_to_pydict<'py>(py: Python<'py>, value: &Value) -> PyResult<Bound<'py, PyDict>> {
    let dict = PyDict::new_bound(py);
    if let Value::Object(obj) = value {
        for (key, val) in obj {
            dict.set_item(key, json_value_to_py(py, val)?)?;
        }
    }
    Ok(dict)
}

/// Register type classes into the Python module.
pub fn register_types(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_class::<ContentType>()?;
    m.add_class::<StoreType>()?;
    Ok(())
}
