use std::sync::Arc;

use crate::{
    terms::Term,
    triples::{Triple, TripleRef},
};

pub mod filter;

pub struct Quad(Arc<Triple>, Option<Arc<Term>>);

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

#[derive(thiserror::Error, Debug)]
pub enum QuadError {
    #[error("quad term must be either none, a namednode or a blank node")]
    QuadTermInvalid,
}

pub fn validate(_triple: TripleRef<'_>, graph_name: Option<&Term>) -> Result<(), QuadError> {
    if !matches!(
        graph_name,
        Some(Term::NamedNode(_)) | Some(Term::BlankNode(_)) | None
    ) {
        return Err(QuadError::QuadTermInvalid);
    }

    Ok(())
}

impl<'a> QuadRef<'a> {
    pub fn new(triple: TripleRef<'a>, graph_name: Option<&'a Term>) -> Result<Self, QuadError> {
        validate(triple, graph_name)?;
        Ok(Self(triple, graph_name))
    }

    pub fn subject(&self) -> &Term {
        self.0.subject()
    }

    pub fn predicate(&self) -> &Term {
        self.0.predicate()
    }

    pub fn object(&self) -> &Term {
        self.0.object()
    }

    pub fn graph_name(&self) -> Option<&Term> {
        self.1
    }

    pub fn triple(&self) -> TripleRef<'a> {
        self.0
    }

    pub fn to_quad(self) -> Quad {
        Quad(self.0.to_triple(), self.1.map(|term| term.clone().into()))
    }
}

impl Quad {
    pub fn new(triple: Arc<Triple>, graph_name: Option<Arc<Term>>) -> Result<Self, QuadError> {
        validate(triple.as_triple_ref(), graph_name.as_deref())?;
        Ok(Self(triple, graph_name))
    }

    pub fn as_quad_ref(&self) -> QuadRef {
        QuadRef(self.triple().as_triple_ref(), self.1.as_deref())
    }

    pub fn subject(&self) -> &Term {
        self.0.subject()
    }

    pub fn subject_arc(&self) -> Arc<Term> {
        self.0.subject_arc()
    }

    pub fn predicate(&self) -> &Term {
        self.0.predicate()
    }

    pub fn predicate_arc(&self) -> Arc<Term> {
        self.0.predicate_arc()
    }

    pub fn object(&self) -> &Term {
        self.0.object()
    }

    pub fn object_arc(&self) -> Arc<Term> {
        self.0.object_arc()
    }

    pub fn graph_name(&self) -> Option<&Term> {
        self.1.as_deref()
    }
    pub fn graph_name_arc(&self) -> Option<Arc<Term>> {
        self.1.clone()
    }

    pub fn triple(&self) -> &Triple {
        &self.0
    }

    pub fn triple_arc(&self) -> Arc<Triple> {
        self.0.clone()
    }

    pub fn spo(&self) -> (&Term, &Term, &Term) {
        self.0.spo()
    }

    pub fn spo_arc(&self) -> (Arc<Term>, Arc<Term>, Arc<Term>) {
        self.0.spo_arc()
    }

    pub fn spog(&self) -> (&Term, &Term, &Term, Option<&Term>) {
        let (s, p, o) = self.spo();
        (s, p, o, self.graph_name())
    }

    pub fn spog_arc(&self) -> (Arc<Term>, Arc<Term>, Arc<Term>, Option<Arc<Term>>) {
        let (s, p, o) = self.spo_arc();
        (s, p, o, self.graph_name_arc())
    }

    pub fn extended_triple(&self) -> (&Triple, Option<&Term>) {
        (&self.0, self.1.as_deref())
    }

    pub fn extended_triple_ref(&self) -> (TripleRef<'_>, Option<&Term>) {
        (self.0.as_triple_ref(), self.1.as_deref())
    }

    pub fn extended_triple_arc(&self) -> (Arc<Triple>, Option<Arc<Term>>) {
        (self.0.clone(), self.1.clone())
    }
}
