//use flexstr::SharedStr as SharedString;
#[cfg(feature = "python")]
use pyo3::prelude::*;
#[cfg(feature = "utoipa")]
use utoipa::ToSchema;

use smartstring::alias::String as SString;

use crate::DataType;

/// [`Key`] holds information about key. It's used to identify the feature
/// Mostly is represented as a string but inside is stored as a hash and the [`DataType`]
/// of the feature/value which is stored in [`crate::DataFrame`]
#[derive(Clone, Eq, serde::Deserialize, serde::Serialize, Default)]
#[cfg_attr(feature = "python", pyo3::pyclass)]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
pub struct Key {
    pub key: u32,
    #[cfg_attr(feature = "utoipa", schema(schema_with = smart_string_schema))]
    pub name: SString,
    pub ctype: DataType,
}

#[cfg(feature = "utoipa")]
fn smart_string_schema() -> utoipa::openapi::Object {
    utoipa::openapi::ObjectBuilder::new()
        .schema_type(utoipa::openapi::schema::Type::String)
        .build()
}

impl Key {
    pub fn name(&self) -> &str {
        self.name.as_str()
    }

    pub fn id(&self) -> u32 {
        self.key
    }
    pub fn key(&self) -> crate::Key {
        self.clone()
    }
}
impl PartialEq for Key {
    fn eq(&self, other: &Self) -> bool {
        self.key == other.key //&& self.name() == other.name()
    }
}
impl PartialOrd for Key {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for Key {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.key.cmp(&other.key)
    }
}

#[cfg(feature = "python")]
#[pymethods]
impl Key {
    #[new]
    #[pyo3(signature = (name, ctype=None))]
    pub fn init(name: String, ctype: Option<DataType>) -> Self {
        Self::new(name.as_str(), ctype.unwrap_or(DataType::Unknown))
    }

    #[pyo3(name = "name")]
    pub fn py_name(&self) -> &str {
        self.name()
    }

    #[pyo3(name = "id")]
    pub fn py_id(&self) -> u32 {
        self.key
    }
    #[pyo3(name = "dtype")]
    pub fn py_type(&self) -> DataType {
        self.ctype
    }
}

impl std::fmt::Display for Key {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.name)
    }
}
impl std::fmt::Debug for Key {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{self}")
    }
}

impl From<&str> for Key {
    fn from(name: &str) -> Self {
        Self {
            key: xxhash_rust::const_xxh32::xxh32(name.as_bytes(), 0),
            name: name.into(),
            ctype: DataType::Unknown,
        }
    }
}
impl From<SString> for Key {
    fn from(name: SString) -> Self {
        Self::from(name.as_str())
    }
}
impl From<String> for Key {
    fn from(name: String) -> Self {
        Self::from(name.as_str())
    }
}

impl From<&String> for Key {
    fn from(name: &String) -> Self {
        Self::from(name.as_str())
    }
}

impl Key {
    pub fn new(name: &str, ctype: DataType) -> Self {
        Self {
            key: xxhash_rust::const_xxh32::xxh32(name.as_bytes(), 0),
            name: name.into(),
            ctype,
        }
    }
}

impl std::hash::Hash for Key {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        state.write_u32(self.key);
    }
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn dummy_test() {
        let key = Key::from("test");
        assert_eq!(key.key, 1042293711);
        assert_eq!(key.id(), 1042293711);
        assert_eq!(key.key(), key);
        assert_eq!(key.name, "test");
        let s = String::from("test");
        let key1 = Key::from(s);
        assert_eq!(key1.name, "test");
        assert_eq!(key1, key);
        assert!(key1.cmp(&key) == std::cmp::Ordering::Equal);
        let s = SString::from("test");
        let key = Key::from(s);
        assert_eq!(key.key, 1042293711);
        assert_eq!(key.name, "test");
        let key = Key::new("test", DataType::Unknown);
        assert_eq!(key.key, 1042293711);
        assert_eq!(key.name, "test");
        assert_eq!(key.ctype, DataType::Unknown);
        assert_eq!(format!("{}", key), "test");
        let s = format!("{:?}", key);
        println!("{}", s);
        let key = Key::from(&("test".to_string()));
        assert_eq!(key.key, 1042293711);
        assert_eq!(key.name, "test");
        let key_serialized = serde_json::to_string(&key).expect("BUG: Cannot serialize");
        let key_deserialized: Key =
            serde_json::from_str(&key_serialized).expect("BUG: Cannot deserialize");
        assert_eq!(key, key_deserialized);
    }

    #[cfg(feature = "python")]
    #[test]
    fn py_test() {
        let key = Key::init("test".into(), Some(DataType::String));
        assert_eq!(key.key, 1042293711);
        assert_eq!(key.name, "test");
        assert_eq!(key.ctype, DataType::String);
        assert_eq!(key.py_name(), "test");
        assert_eq!(key.py_id(), 1042293711);
    }
}
