pub mod filters;

use crate::default_graph_view;
use crate::prelude::*;

use crate::terms::*;

use std::cmp::Ordering;
use std::fmt;

use thiserror::Error;

#[cfg(feature = "source_n_triples")]
use crate::sources::parsers::n_triples;

#[derive(Error, Debug)]
pub enum TripleError {
    #[error("Term variant not allowed for subject")]
    InvalidSubject,
    #[error("Term variant not allowed for subject")]
    InvalidPredicate,
    #[error("Term variant not allowed for subject")]
    InvalidObject,
    #[error("IncalidTriple")]
    // FIXME workaround for N-Triples parser not returning specific error
    InvalidTriple,
}

/// https://www.w3.org/TR/rdf11-concepts/#section-triples
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Triple(Arc<Term>, Arc<Term>, Arc<Term>);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TripleRef<'a>(&'a Term, &'a Term, &'a Term);

pub fn validate(subject: &Term, predicate: &Term, object: &Term) -> Result<(), TripleError> {
    if !matches!(subject, Term::NamedNode(_) | Term::BlankNode(_)) {
        return Err(TripleError::InvalidSubject);
    }

    if !matches!(predicate, Term::NamedNode(_)) {
        return Err(TripleError::InvalidPredicate);
    }
    #[allow(clippy::needless_if)]
    if !matches!(object, _) {
        return Err(TripleError::InvalidObject);
    }
    Ok(())
}

impl<'a> TripleRef<'a> {
    pub fn new(
        subject: &'a Term,
        predicate: &'a Term,
        object: &'a Term,
    ) -> Result<Self, TripleError> {
        validate(subject, predicate, object)?;
        tracing::trace!(
            "Instantiating triple (ref): {} {} {} .",
            subject,
            predicate,
            object,
        );
        Ok(Self(subject, predicate, object))
    }
    /// # Safety
    ///
    /// The passed in values must yield a correct RDF triple
    pub unsafe fn new_unchecked(subject: &'a Term, predicate: &'a Term, object: &'a Term) -> Self {
        Self(subject, predicate, object)
    }
    pub fn subject(&self) -> &Term {
        self.0
    }
    pub fn predicate(&self) -> &Term {
        self.1
    }
    pub fn object(&self) -> &Term {
        self.2
    }
    pub fn spo(&self) -> (&Term, &Term, &Term) {
        (self.subject(), self.predicate(), self.object())
    }

    pub fn ground(&self) -> bool {
        self.subject().ground() && self.predicate().ground() && self.object().ground()
    }

    pub fn to_triple(self) -> Arc<Triple> {
        unsafe {
            Triple::new_unchecked(
                self.subject().clone().into(),
                self.predicate().clone().into(),
                self.object().clone().into(),
            )
        }
    }
}

impl Triple {
    pub fn new(
        subject: Arc<Term>,
        predicate: Arc<Term>,
        object: Arc<Term>,
    ) -> Result<Arc<Self>, TripleError> {
        validate(&subject, &predicate, &object)?;
        tracing::trace!(
            "Instantiating triple (owned): {} {} {} .",
            subject,
            predicate,
            object,
        );
        Ok(Arc::new(Self(
            subject.clone(),
            predicate.clone(),
            object.clone(),
        )))
    }

    /// # Safety
    ///
    /// The passed in values must yield a correct RDF tripple
    pub unsafe fn new_unchecked(
        subject: Arc<Term>,
        predicate: Arc<Term>,
        object: Arc<Term>,
    ) -> Arc<Self> {
        Arc::new(Self(subject.clone(), predicate.clone(), object.clone()))
    }

    pub fn subject(&self) -> &Term {
        &self.0
    }
    pub fn subject_arc(&self) -> Arc<Term> {
        self.0.clone()
    }
    pub fn predicate(&self) -> &Term {
        &self.1
    }
    pub fn predicate_arc(&self) -> Arc<Term> {
        self.1.clone()
    }
    pub fn object_arc(&self) -> Arc<Term> {
        self.2.clone()
    }
    pub fn as_triple_ref(&self) -> TripleRef {
        unsafe { TripleRef::new_unchecked(self.subject(), self.predicate(), self.object()) }
    }

    pub fn object(&self) -> &Term {
        &self.2
    }
    pub fn spo(&self) -> (&Term, &Term, &Term) {
        (self.subject(), self.predicate(), self.object())
    }
    pub fn spo_arc(&self) -> (Arc<Term>, Arc<Term>, Arc<Term>) {
        (self.subject_arc(), self.predicate_arc(), self.object_arc())
    }

    /// Checks whether trple is *ground* as defined by
    /// [RDF Concepts and Abstract Syntax](https://www.w3.org/TR/rdf12-concepts/#section-terms)
    ///
    /// A triple is *ground* if all terms appearing in it are *ground* (cf. [super::terms::Term::ground]).
    ///
    /// # Example
    ///
    /// ```
    /// use taganak_core::terms::Term;
    /// use taganak_core::triples::Triple;
    /// use std::sync::Arc;
    ///
    /// let nn: Arc<Term> = Arc::new("<https://example.com/foo>".try_into().unwrap());
    /// let bn: Arc<Term> = Arc::new("_:b1".try_into().unwrap());
    /// let li: Arc<Term> = Arc::new(r#""Something""#.try_into().unwrap());
    ///
    /// assert_eq!(Triple::new(nn.clone(), nn.clone(), li.clone()).unwrap().ground(), true);
    /// assert_eq!(Triple::new(bn.clone(), nn.clone(), li.clone()).unwrap().ground(), false);
    /// ```
    pub fn ground(&self) -> bool {
        self.subject().ground() && self.predicate().ground() && self.object().ground()
    }
}

impl<'a> From<&'a Triple> for TripleRef<'a> {
    fn from(value: &'a Triple) -> Self {
        value.as_triple_ref()
    }
}

#[cfg(feature = "source_n_triples")]
impl TryFrom<String> for Triple {
    type Error = TripleError;

    fn try_from(mut value: String) -> Result<Self, Self::Error> {
        let mut input = n_triples::TokenStream {
            input: value.as_mut_str(),
            state: &mut n_triples::State::new(),
        };
        Ok(Arc::into_inner(
            n_triples::triple_parser(&mut input).map_err(|e| TripleError::InvalidTriple)?,
        )
        .unwrap())
    }
}

#[cfg(feature = "source_n_triples")]
impl TryFrom<&str> for Triple {
    type Error = TripleError;

    /// # Examples
    ///
    /// ```
    /// use taganak_core::triples::Triple;
    /// use taganak_core::terms::Term;
    ///
    /// let s: Term = "<https://example.com/foo#Emil>".try_into().unwrap();
    /// let p: Term = "<https://example.com/foo#animal>".try_into().unwrap();
    /// let o: Term = r#""Schildkröte"@de"#.try_into().unwrap();
    ///
    /// let t1: Triple = r#"<https://example.com/foo#Emil> <https://example.com/foo#animal> "Schildkröte"@de ."#.try_into().unwrap();
    ///
    /// assert_eq!(t1, *Triple::new(s.into(), p.into(), o.into()).unwrap());
    /// ```
    fn try_from(value: &str) -> Result<Self, Self::Error> {
        value.to_string().try_into()
    }
}

impl PartialEq<TripleRef<'_>> for Triple {
    fn eq(&self, other: &TripleRef) -> bool {
        self.as_triple_ref() == *other
    }
}
impl PartialEq<Triple> for TripleRef<'_> {
    fn eq(&self, other: &Triple) -> bool {
        *self == other.as_triple_ref()
    }
}
impl PartialOrd for Triple {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Triple {
    fn cmp(&self, other: &Self) -> Ordering {
        self.to_string().cmp(&other.to_string())
    }
}

impl PartialOrd for TripleRef<'_> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for TripleRef<'_> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.to_string().cmp(&other.to_string())
    }
}

impl PartialOrd<TripleRef<'_>> for Triple {
    fn partial_cmp(&self, other: &TripleRef) -> Option<Ordering> {
        Some(self.to_string().cmp(&other.to_string()))
    }
}
impl PartialOrd<Triple> for TripleRef<'_> {
    fn partial_cmp(&self, other: &Triple) -> Option<Ordering> {
        Some(self.to_string().cmp(&other.to_string()))
    }
}

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

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

impl GraphView for &Triple {
    type Stream = futures::stream::Once<futures::future::Ready<Result<Arc<Triple>, GraphError>>>;

    async fn stream(self) -> Result<Self::Stream, GraphError> {
        Ok(futures::stream::once(futures::future::ready(
            Ok(unsafe { Triple::new_unchecked(self.subject_arc(), self.predicate_arc(), self.object_arc()) }),
        )))
    }

    default_graph_view!();
}

impl GraphView for &TripleRef<'_> {
    type Stream = futures::stream::Once<futures::future::Ready<Result<Arc<Triple>, GraphError>>>;

    async fn stream(self) -> Result<Self::Stream, GraphError> {
        Ok(futures::stream::once(futures::future::ready(Ok(self.to_triple()))))
    }

    default_graph_view!();
}
