use serde::{Deserialize, Serialize};

use crate::{LocalizedLabel, TermId, VocabularyId};

/// A controlled vocabulary (term source), e.g. "material" or "object_name".
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Vocabulary {
    pub id: VocabularyId,
    pub key: String,
}

/// A term within a vocabulary, with its multilingual labels.
#[derive(Debug, Clone, PartialEq)]
pub struct Term {
    pub id: TermId,
    pub vocabulary_id: VocabularyId,
    pub external_uri: Option<String>,
    pub labels: Vec<LocalizedLabel>,
}

/// A term to be created.
#[derive(Debug, Clone, PartialEq)]
pub struct NewTerm {
    pub vocabulary_id: VocabularyId,
    pub external_uri: Option<String>,
    pub labels: Vec<LocalizedLabel>,
}

/// A reference to a term confirmed to exist in a given vocabulary.
///
/// Obtain via `db::vocab::resolve_term`; do not construct ad hoc for
/// values that haven't been resolved.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct TermRef {
    term_id: TermId,
    vocabulary_id: VocabularyId,
}

impl TermRef {
    pub fn new(term_id: TermId, vocabulary_id: VocabularyId) -> Self {
        Self {
            term_id,
            vocabulary_id,
        }
    }
    pub fn term_id(&self) -> TermId {
        self.term_id
    }
    pub fn vocabulary_id(&self) -> VocabularyId {
        self.vocabulary_id
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{TermId, VocabularyId};

    #[test]
    fn term_ref_exposes_its_parts() {
        let term_id = TermId::new();
        let vocabulary_id = VocabularyId::new();
        let r = TermRef::new(term_id, vocabulary_id);
        assert_eq!(r.term_id(), term_id);
        assert_eq!(r.vocabulary_id(), vocabulary_id);
    }
}
