use crate::prelude::*;

use core::future::ready;
use core::iter::{Cloned, Map, Take};
use std::collections::hash_set::{HashSet, Iter};
use std::collections::HashMap;
use std::sync::LazyLock;

use futures::future::OkInto;
use futures::stream;
use taganak_core::graphs::{
    AndFilter, FilterIter, GraphView, JoinedMatches, Matches, MatchesFilter, TrueFilter, YesMatch,
};
use taganak_core::triples::{self, TripleRef};
use taganak_core::{
    default_graph_view, impl_default_cbd, impl_default_filter, impl_default_matches,
    impl_default_streams, impl_default_streams_dedup, impl_object_stream_dedup,
    impl_subject_stream,
};

#[derive(Debug, Default, Clone)]
pub struct SimpleGraph {
    all: HashSet<Arc<Triple>>,
    index_s: HashMap<Arc<Term>, HashSet<Arc<Triple>>>,
    index_sp: HashMap<(Arc<Term>, Arc<Term>), HashSet<Arc<Triple>>>,
    index_p: HashMap<Arc<Term>, HashSet<Arc<Triple>>>,
}

impl Graph for SimpleGraph {
    type View<'g> = SimpleGraphView<'g>;

    fn view(&self) -> impl core::future::Future<Output = Self::View<'_>> + Send {
        core::future::ready(SimpleGraphView {
            graph: self,
            matches: YesMatch,
            filter: TrueFilter,
            limit: None,
        })
    }

    fn add(
        &mut self,
        triple: Arc<Triple>,
    ) -> impl core::future::Future<Output = Result<(), GraphError>> + Send {
        self.all.insert(triple.clone());
        self.index_s
            .entry(triple.subject_arc())
            .or_insert(HashSet::new())
            .insert(triple.clone());
        self.index_sp
            .entry((triple.subject_arc(), triple.predicate_arc()))
            .or_insert(HashSet::new())
            .insert(triple.clone());
        self.index_p
            .entry(triple.predicate_arc())
            .or_insert(HashSet::new())
            .insert(triple.clone());
        core::future::ready(Ok(()))
    }

    fn remove(
        &mut self,
        triple: TripleRef<'_>,
    ) -> impl core::future::Future<Output = Result<(), GraphError>> + Send {
        self.all.remove(&triple.to_triple());
        self.index_s.get_mut(triple.subject()).and_then(|s| {
            s.remove(triple.to_triple().as_ref());
            Some(s)
        });
        self.index_sp
            .get_mut(&(
                triple.to_triple().subject_arc(),
                triple.to_triple().predicate_arc(),
            ))
            .and_then(|s| {
                s.remove(triple.to_triple().as_ref());
                Some(s)
            });
        self.index_p.get_mut(triple.predicate()).and_then(|s| {
            s.remove(triple.to_triple().as_ref());
            Some(s)
        });
        core::future::ready(Ok(()))
    }

    fn clear(&mut self) -> impl core::future::Future<Output = Result<(), GraphError>> + Send {
        self.all.clear();
        self.index_s.clear();
        self.index_sp.clear();
        self.index_p.clear();
        core::future::ready(Ok(()))
    }
}

fn ok_enum(t: Arc<Triple>) -> Result<Arc<Triple>, GraphError> {
    Ok(t)
}

static EMPTY_SET: LazyLock<HashSet<Arc<Triple>>> = LazyLock::new(|| HashSet::new());

#[derive(Debug, Clone, Copy)]
pub struct SimpleGraphView<'g, M = YesMatch, F = TrueFilter> {
    graph: &'g SimpleGraph,
    matches: M,
    filter: F,
    limit: Option<usize>,
}

fn print(triple: &Arc<Triple>) {
    dbg!(triple);
}

impl<'g, M, F> GraphView for SimpleGraphView<'g, M, F>
where
    M: Matches + Clone,
    F: TripleFilter + Clone,
{
    type Stream = stream::Iter<
        Map<
            Cloned<Take<FilterIter<FilterIter<Iter<'g, Arc<Triple>>, F>, MatchesFilter<M>>>>,
            fn(Arc<Triple>) -> Result<Arc<Triple>, GraphError>,
        >,
    >;

    fn stream(self) -> impl core::future::Future<Output = Result<Self::Stream, GraphError>> + Send {
        let matches = self.matches.get();
        let map = match (matches.subject, matches.predicate, matches.object) {
            (Some(subject), None, None) => self.graph.index_s.get(subject).unwrap_or(&*EMPTY_SET),
            (None, Some(predicate), None) => {
                self.graph.index_p.get(predicate).unwrap_or(&*EMPTY_SET)
            }
            (Some(subject), Some(predicate), None) => self
                .graph
                .index_sp
                .get(&(
                    Arc::new(subject.as_ref().clone()),
                    Arc::new(predicate.as_ref().clone()),
                ))
                .unwrap_or(&*EMPTY_SET),
            _ => &self.graph.all,
        };
        let iter = FilterIter::new(map.iter(), self.filter);
        let iter = FilterIter::new(iter, MatchesFilter(self.matches))
            .take(self.limit.unwrap_or(usize::MAX));
        core::future::ready(Ok(stream::iter(iter.cloned().map(ok_enum as _))))
    }

    type Filter<F2>
        = SimpleGraphView<'g, M, AndFilter<F, F2>>
    where
        F2: TripleFilter + Unpin;

    fn filter<F2>(
        self,
        filter: F2,
    ) -> impl core::future::Future<Output = Result<Self::Filter<F2>, GraphError>> + Send
    where
        F2: TripleFilter + Unpin,
    {
        core::future::ready(Ok(SimpleGraphView {
            graph: self.graph,
            matches: self.matches,
            filter: AndFilter::new(self.filter, filter),
            limit: self.limit,
        }))
    }

    type Matches<M2>
        = SimpleGraphView<'g, JoinedMatches<M, M2>, F>
    where
        M2: Matches + Unpin;

    fn matches<M2>(
        self,
        matches: M2,
        limit: Option<usize>,
    ) -> impl core::future::Future<Output = Result<Self::Matches<M2>, GraphError>> + Send
    where
        M2: Matches + Unpin,
    {
        let new_limit = match (limit, self.limit) {
            (None, None) => None,
            (None, Some(l)) | (Some(l), None) => Some(l),
            (Some(l1), Some(l2)) => Some(core::cmp::min(l1, l2)),
        };
        futures::future::ready(Ok(SimpleGraphView {
            matches: JoinedMatches::new(self.matches, matches),
            filter: self.filter,
            graph: self.graph,
            limit: new_limit,
        }))
    }
    impl_default_cbd!();
    impl_default_streams!();
    impl_default_streams_dedup!();
}

fn to_object_arc(t: &Arc<Triple>) -> Result<Arc<Term>, GraphError> {
    Ok(t.object_arc())
}

impl SimpleGraph {
    pub fn add_sync(&mut self, triple: Arc<Triple>) {
        self.all.insert(triple.clone());
        self.index_s
            .entry(triple.subject_arc())
            .or_insert(HashSet::new())
            .insert(triple.clone());
        self.index_sp
            .entry((triple.subject_arc(), triple.predicate_arc()))
            .or_insert(HashSet::new())
            .insert(triple.clone());
        self.index_p
            .entry(triple.predicate_arc())
            .or_insert(HashSet::new())
            .insert(triple.clone());
    }
}
