use serde::{Deserialize, Serialize};

use crate::{AuthorityId, LocalizedLabel};

/// The kind of authority record.
///
/// NOTE: kept in sync by hand with the
/// `CHECK (kind IN ('person', 'organisation', 'place'))` constraint in
/// `crates/db/migrations/0002_vocabularies_authorities.sql` — add a variant in both places.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum AuthorityKind {
    Person,
    Organisation,
    Place,
}

impl AuthorityKind {
    pub fn as_str(&self) -> &'static str {
        match self {
            AuthorityKind::Person => "person",
            AuthorityKind::Organisation => "organisation",
            AuthorityKind::Place => "place",
        }
    }

    pub fn from_db(s: &str) -> Option<Self> {
        match s {
            "person" => Some(AuthorityKind::Person),
            "organisation" => Some(AuthorityKind::Organisation),
            "place" => Some(AuthorityKind::Place),
            _ => None,
        }
    }
}

/// An authority record (person / organisation / place), with multilingual labels.
#[derive(Debug, Clone, PartialEq)]
pub struct Authority {
    pub id: AuthorityId,
    pub kind: AuthorityKind,
    pub external_uri: Option<String>,
    pub labels: Vec<LocalizedLabel>,
}

/// An authority to be created.
#[derive(Debug, Clone, PartialEq)]
pub struct NewAuthority {
    pub kind: AuthorityKind,
    pub external_uri: Option<String>,
    pub labels: Vec<LocalizedLabel>,
}

/// A reference to an authority confirmed to exist (carries its kind).
///
/// Obtain via `db::authority::resolve_authority`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuthorityRef {
    authority_id: AuthorityId,
    kind: AuthorityKind,
}

impl AuthorityRef {
    pub fn new(authority_id: AuthorityId, kind: AuthorityKind) -> Self {
        Self { authority_id, kind }
    }
    pub fn authority_id(&self) -> AuthorityId {
        self.authority_id
    }
    pub fn kind(&self) -> AuthorityKind {
        self.kind
    }
}

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

    #[test]
    fn kind_round_trips_via_db_string() {
        for k in [
            AuthorityKind::Person,
            AuthorityKind::Organisation,
            AuthorityKind::Place,
        ] {
            assert_eq!(AuthorityKind::from_db(k.as_str()), Some(k));
        }
        assert_eq!(AuthorityKind::from_db("ufo"), None);
    }
}
