use crate::ids::{LibraryId, ReferenceId};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;

/// A named collection of references. Forms a tree via `parent_id`.
///
/// References are not "owned" by a library — they exist in a flat pool.
/// A reference can appear in multiple libraries (multi-membership).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Library {
    pub id: LibraryId,
    pub name: String,
    /// `None` means this is a root library (no parent).
    pub parent_id: Option<LibraryId>,
    /// The set of references that are members of this library.
    /// BTreeSet for deterministic serialization order.
    pub members: BTreeSet<ReferenceId>,
    pub created_at: DateTime<Utc>,
    pub modified_at: DateTime<Utc>,
}

impl Library {
    pub fn new(name: impl Into<String>, parent_id: Option<LibraryId>) -> Self {
        let now = Utc::now();
        Self {
            id: LibraryId::new(),
            name: name.into(),
            parent_id,
            members: BTreeSet::new(),
            created_at: now,
            modified_at: now,
        }
    }
}

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

    #[test]
    fn library_serde_round_trip() {
        let mut lib = Library::new("Machine Learning", None);
        let ref_id = ReferenceId::new();
        lib.members.insert(ref_id);

        let json = serde_json::to_string(&lib).expect("serialize to JSON");
        let lib2: Library = serde_json::from_str(&json).expect("deserialize from JSON");

        assert_eq!(lib.id, lib2.id);
        assert_eq!(lib.name, lib2.name);
        assert_eq!(lib.members, lib2.members);
        assert_eq!(lib.parent_id, lib2.parent_id);
    }

    #[test]
    fn nested_library_serde_round_trip() {
        let parent = Library::new("Science", None);
        let child = Library::new("Physics", Some(parent.id));

        let json = serde_json::to_string(&child).expect("serialize to JSON");
        let child2: Library = serde_json::from_str(&json).expect("deserialize from JSON");

        assert_eq!(child2.parent_id, Some(parent.id));
    }
}
