use serde::{Deserialize, Serialize};
use std::fmt;
use uuid::Uuid;

macro_rules! define_id {
    ($name:ident) => {
        #[derive(
            Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
        )]
        pub struct $name(pub Uuid);

        impl $name {
            pub fn new() -> Self {
                Self(Uuid::now_v7())
            }
        }

        impl Default for $name {
            fn default() -> Self {
                Self::new()
            }
        }

        impl From<Uuid> for $name {
            fn from(uuid: Uuid) -> Self {
                Self(uuid)
            }
        }

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

define_id!(ReferenceId);
define_id!(LibraryId);
define_id!(AnnotationId);

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

    #[test]
    fn new_generates_unique_ids() {
        let a = ReferenceId::new();
        let b = ReferenceId::new();
        assert_ne!(a, b);
    }

    #[test]
    fn display_is_uuid_format() {
        let id = ReferenceId::new();
        let s = id.to_string();
        assert_eq!(s.len(), 36); // UUID hyphenated format
    }

    #[test]
    fn serde_round_trip() {
        let id = LibraryId::new();
        let json = serde_json::to_string(&id).unwrap();
        let id2: LibraryId = serde_json::from_str(&json).unwrap();
        assert_eq!(id, id2);
    }
}
