//! User entity type.

use std::collections::HashMap;

use crate::schema::EntityId;

/// A user entity in tidalDB.
///
/// Users are the "who" of personalized ranking. Each user has:
/// - An ID (globally unique across all entity kinds)
/// - Optional metadata (display name, preferences, etc.)
/// - Optional embedding (preference vector, managed by the database)
#[derive(Debug, Clone)]
pub struct UserEntity {
    pub user_id: EntityId,
    pub metadata: HashMap<String, String>,
    pub embedding: Option<Vec<f32>>,
}

impl UserEntity {
    #[must_use]
    pub fn new(user_id: EntityId) -> Self {
        Self {
            user_id,
            metadata: HashMap::new(),
            embedding: None,
        }
    }

    /// Serialize to bytes using the shared entity codec.
    #[must_use]
    pub fn serialize(&self) -> Vec<u8> {
        super::serialize_entity(self.embedding.as_deref(), &self.metadata)
    }

    /// Deserialize from bytes.
    #[must_use]
    pub fn deserialize(user_id: EntityId, bytes: &[u8]) -> Self {
        let (embedding, metadata) = super::deserialize_entity(bytes);
        Self {
            user_id,
            metadata,
            embedding,
        }
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
    use super::*;

    #[test]
    fn user_roundtrip() {
        let mut user = UserEntity::new(EntityId::new(42));
        user.metadata
            .insert("name".to_string(), "alice".to_string());
        user.embedding = Some(vec![1.0, 2.0, 3.0]);

        let bytes = user.serialize();
        let restored = UserEntity::deserialize(EntityId::new(42), &bytes);

        assert_eq!(restored.user_id, EntityId::new(42));
        assert_eq!(restored.metadata.get("name").unwrap(), "alice");
        assert_eq!(restored.embedding.unwrap(), vec![1.0, 2.0, 3.0]);
    }

    #[test]
    fn user_no_embedding() {
        let user = UserEntity::new(EntityId::new(1));
        let bytes = user.serialize();
        let restored = UserEntity::deserialize(EntityId::new(1), &bytes);
        assert!(restored.embedding.is_none());
        assert!(restored.metadata.is_empty());
    }
}
