use crate::prelude::*;

use super::*;

use const_format::concatcp;

use uuid::Uuid;

use std::fmt;

const SKOLEM_GENID_ROOT: &iref::Iri =
    unsafe { iref::Iri::new_unchecked(concatcp!(SYSTEM_AUTHORITY, "/.well-known/genid/")) };

/// https://www.w3.org/TR/rdf11-concepts/#section-blank-nodes
#[derive(PartialEq, Eq, Hash, Clone, Debug, PartialOrd, Ord)]
pub struct BlankNode(String);

pub trait BlankNodeInterface: fmt::Display {
    fn new() -> Self
    where
        Self: Sized,
    {
        Self::from_string(Self::gen_id())
    }

    fn from_string(id: String) -> Self;

    fn gen_id() -> String;
    fn id(&self) -> &String;

    fn to_skolem_iri(&self) -> Iri;
    fn from_skolem_iri(iri: &Iri) -> Result<Self, TermError>
    where
        Self: Sized;
}

impl BlankNodeInterface for BlankNode {
    fn from_string(id: String) -> Self {
        Self(id)
    }

    fn gen_id() -> String {
        Uuid::new_v4().to_string()
    }

    fn id(&self) -> &String {
        &self.0
    }

    fn to_skolem_iri(&self) -> Iri {
        iref::IriRef::new(self.id())
            .unwrap()
            .resolved(SKOLEM_GENID_ROOT)
    }

    fn from_skolem_iri(iri: &Iri) -> Result<Self, TermError> {
        Ok(Self::from_string(
            iri.strip_prefix(SKOLEM_GENID_ROOT.as_str())
                .and_then(|s| Uuid::try_parse(s).ok().and(Some(s)))
                .ok_or(TermError::UnknownSkolemIri)?
                .to_string(),
        ))
    }
}

// FIXME can we implement Display for all implementors auf BlankNodeInterface?
impl fmt::Display for BlankNode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "_:{}", self.id())
    }
}
