use cocoindex_core::engine::profile::Persist;

use crate::{prelude::*, runtime::python_objects};

pub struct PyValue {
    data: Py<PyAny>,
}

impl PyValue {
    pub fn new(data: Py<PyAny>) -> Self {
        Self { data }
    }

    pub fn value(&self) -> &Py<PyAny> {
        &self.data
    }

    pub fn into_inner(self) -> Py<PyAny> {
        self.data
    }
}

impl std::fmt::Debug for PyValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        debug_py_any(f, &self.data)
    }
}

impl Persist for PyValue {
    fn to_bytes(&self) -> Result<bytes::Bytes> {
        let serialized = Python::attach(|py| python_objects().serialize(py, &self.data.bind(py)))?;
        Ok(serialized)
    }

    fn from_bytes(data: &[u8]) -> Result<Self> {
        let value = Python::attach(|py| python_objects().deserialize(py, data))?;
        Ok(Self { data: value })
    }
}

impl Clone for PyValue {
    fn clone(&self) -> Self {
        Python::attach(|py| Self {
            data: self.data.clone_ref(py),
        })
    }
}

fn debug_py_any(f: &mut std::fmt::Formatter<'_>, value: &Py<PyAny>) -> std::fmt::Result {
    Python::attach(|py| {
        let value = value.bind(py);
        match value.repr() {
            Ok(repr) => match repr.to_str() {
                Ok(repr_str) => f.write_str(repr_str),
                Err(err) => {
                    error!("Error getting repr: {:?}", err);
                    f.write_str("<error getting repr>")
                }
            },
            Err(err) => {
                error!("Error getting repr: {:?}", err);
                f.write_str("<error getting repr>")
            }
        }
    })
}
