use core::hash::Hash;
use std::{collections::HashSet, sync::Arc};

use futures::{Stream, StreamExt};

use crate::{prelude::DatasetError, quads::Quad, terms::Term};

pub fn map_quad_to_subject(quad: Result<Quad, DatasetError>) -> Result<Arc<Term>, DatasetError> {
    quad.map(|q| q.subject_arc())
}

pub fn map_quad_to_predicate(quad: Result<Quad, DatasetError>) -> Result<Arc<Term>, DatasetError> {
    quad.map(|q| q.predicate_arc())
}

pub fn map_quad_to_object(quad: Result<Quad, DatasetError>) -> Result<Arc<Term>, DatasetError> {
    quad.map(|q| q.object_arc())
}

pub fn map_quad_to_graph_name(
    quad: Result<Quad, DatasetError>,
) -> Result<Option<Arc<Term>>, DatasetError> {
    quad.map(|q| q.graph_name_arc())
}

#[macro_export]
macro_rules! impl_dataset_subject_stream {
    () => {
        type SubjectStream<'stream> = ::futures::stream::Map<<Self::Matches<$crate::datasets::MatchesTuple<'stream, 'stream, 'stream, 'stream>> as $crate::datasets::DatasetView>::Stream, fn(::std::result::Result<$crate::quads::Quad, $crate::datasets::DatasetError>) -> ::std::result::Result<::std::sync::Arc<$crate::terms::Term>, $crate::datasets::DatasetError>>;

        async fn subjects<'stream>(self, predicate: Option<&'stream $crate::terms::Term>, object: Option<&'stream $crate::terms::Term>, graph_name: Option<Option<&'stream $crate::terms::Term>>, limit: Option<usize>) -> Result<Self::SubjectStream<'stream>, $crate::datasets::DatasetError> {
            use ::futures::stream::StreamExt;
            Ok(self.matches((None, predicate, object, graph_name), limit).await?.stream().await?.map($crate::datasets::default::streams::map_quad_to_subject))
        }
    };
}

#[macro_export]
macro_rules! impl_dataset_subject_dedup_stream {
    () => {
        type SubjectStreamDedup<'stream> = $crate::graphs::default::DedupStream<
            Self::SubjectStream<'stream>,
            ::std::sync::Arc<$crate::terms::Term>,
        >;

        async fn subjects_dedup<'stream>(
            self,
            predicate: Option<&'stream $crate::terms::Term>,
            object: Option<&'stream $crate::terms::Term>,
            graph_name: Option<Option<&'stream $crate::terms::Term>>,
            limit: Option<usize>,
        ) -> Result<Self::SubjectStreamDedup<'stream>, $crate::datasets::DatasetError> {
            Ok($crate::graphs::default::DedupStream::new(
                self.subjects(predicate, object, graph_name, limit).await?,
            ))
        }
    };
}

#[macro_export]
macro_rules! impl_dataset_predicate_stream {
    () => {
        type PredicateStream<'stream> = ::futures::stream::Map<<Self::Matches<$crate::datasets::MatchesTuple<'stream, 'stream, 'stream, 'stream>> as $crate::datasets::DatasetView>::Stream, fn(::std::result::Result<$crate::quads::Quad, $crate::datasets::DatasetError>) -> ::std::result::Result<::std::sync::Arc<$crate::terms::Term>, $crate::datasets::DatasetError>>;

        async fn predicates<'stream>(self, subject: Option<&'stream $crate::terms::Term>, object: Option<&'stream $crate::terms::Term>, graph_name: Option<Option<&'stream $crate::terms::Term>>, limit: Option<usize>) -> Result<Self::PredicateStream<'stream>, $crate::datasets::DatasetError> {
            use ::futures::stream::StreamExt;
            Ok(self.matches((subject, None, object, graph_name), limit).await?.stream().await?.map($crate::datasets::default::streams::map_quad_to_predicate))
        }
    };
}

#[macro_export]
macro_rules! impl_dataset_predicate_dedup_stream {
    () => {
        type PredicateStreamDedup<'stream> = $crate::graphs::default::DedupStream<
            Self::PredicateStream<'stream>,
            ::std::sync::Arc<$crate::terms::Term>,
        >;

        async fn predicates_dedup<'stream>(
            self,
            subject: Option<&'stream $crate::terms::Term>,
            object: Option<&'stream $crate::terms::Term>,
            graph_name: Option<Option<&'stream $crate::terms::Term>>,
            limit: Option<usize>,
        ) -> Result<Self::PredicateStreamDedup<'stream>, $crate::datasets::DatasetError> {
            Ok($crate::graphs::default::DedupStream::new(
                self.predicates(subject, object, graph_name, limit).await?,
            ))
        }
    };
}

#[macro_export]
macro_rules! impl_dataset_object_stream {
    () => {
        type ObjectStream<'stream> = ::futures::stream::Map<<Self::Matches<$crate::datasets::MatchesTuple<'stream, 'stream, 'stream, 'stream>> as $crate::datasets::DatasetView>::Stream, fn(::std::result::Result<$crate::quads::Quad, $crate::datasets::DatasetError>) -> ::std::result::Result<::std::sync::Arc<$crate::terms::Term>, $crate::datasets::DatasetError>>;

        async fn objects<'stream>(self, subject: Option<&'stream $crate::terms::Term>, predicate: Option<&'stream $crate::terms::Term>, graph_name: Option<Option<&'stream $crate::terms::Term>>, limit: Option<usize>) -> Result<Self::ObjectStream<'stream>,
        $crate::datasets::DatasetError> {
            use ::futures::stream::StreamExt;
            Ok(self.matches((subject, predicate, None, graph_name), limit).await?.stream().await?.map($crate::datasets::default::streams::map_quad_to_object))
        }
    };
}

#[macro_export]
macro_rules! impl_dataset_object_dedup_stream {
    () => {
        type ObjectStreamDedup<'stream> = $crate::graphs::default::DedupStream<
            Self::ObjectStream<'stream>,
            ::std::sync::Arc<$crate::terms::Term>,
        >;

        async fn objects_dedup<'stream>(
            self,
            subject: Option<&'stream $crate::terms::Term>,
            predicate: Option<&'stream $crate::terms::Term>,
            graph_name: Option<Option<&'stream $crate::terms::Term>>,
            limit: Option<usize>,
        ) -> Result<Self::ObjectStreamDedup<'stream>, $crate::datasets::DatasetError> {
            Ok($crate::graphs::default::DedupStream::new(
                self.objects(subject, predicate, graph_name, limit).await?,
            ))
        }
    };
}

#[macro_export]
macro_rules! impl_dataset_graph_name_stream {
    () => {
        type GraphNameStream<'stream> = ::futures::stream::Map<<Self::Matches<$crate::datasets::MatchesTuple<'stream, 'stream, 'stream, 'stream>> as $crate::datasets::DatasetView>::Stream, fn(::std::result::Result<$crate::quads::Quad, $crate::datasets::DatasetError>) -> ::std::result::Result<Option<::std::sync::Arc<$crate::terms::Term>>, $crate::datasets::DatasetError>>;

        async fn graph_names<'stream>(self, subject: Option<&'stream $crate::terms::Term>, predicate: Option<&'stream $crate::terms::Term>, object: Option<&'stream $crate::terms::Term>, limit: Option<usize>) -> Result<Self::GraphNameStream<'stream>, $crate::datasets::DatasetError> {
            use ::futures::stream::StreamExt;
            Ok(self.matches((subject, predicate, object, None), limit).await?.stream().await?.map($crate::datasets::default::streams::map_quad_to_graph_name))
        }
    };
}

#[macro_export]
macro_rules! impl_dataset_graph_name_dedup_stream {
    () => {
        type GraphNameStreamDedup<'stream> = $crate::graphs::default::DedupStream<
            Self::GraphNameStream<'stream>,
            Option<::std::sync::Arc<$crate::terms::Term>>,
        >;

        async fn graph_names_dedup<'stream>(
            self,
            subject: Option<&'stream $crate::terms::Term>,
            predicate: Option<&'stream $crate::terms::Term>,
            object: Option<&'stream $crate::terms::Term>,
            limit: Option<usize>,
        ) -> Result<Self::GraphNameStreamDedup<'stream>, $crate::datasets::DatasetError> {
            Ok($crate::graphs::default::DedupStream::new(
                self.graph_names(subject, predicate, object, limit).await?,
            ))
        }
    };
}

#[macro_export]
macro_rules! impl_dataset_streams {
    () => {
        $crate::impl_dataset_subject_stream!();
        $crate::impl_dataset_predicate_stream!();
        $crate::impl_dataset_object_stream!();
        $crate::impl_dataset_graph_name_stream!();
    };
}

#[macro_export]
macro_rules! impl_dataset_dedup_streams {
    () => {
        $crate::impl_dataset_subject_dedup_stream!();
        $crate::impl_dataset_object_dedup_stream!();
        $crate::impl_dataset_predicate_dedup_stream!();
        $crate::impl_dataset_graph_name_dedup_stream!();
    };
}
