use std::sync::Arc;

#[cfg(feature = "dataset_glob")]
use glob::{GlobError, Pattern};
use taganak_orm::re::{DatasetError, Graph, Iri, SourceError, Term};

use crate::graphs::{ConfigurableGraphs, FromIriError, GraphConfig, GraphConfigs};

use super::simple::SimpleDataset;

use taganak_core::{datasets::Dataset, terms::path_to_file_iri};

#[derive(Default)]
pub struct IriDataset {
    dataset: SimpleDataset<ConfigurableGraphs>,
}

#[derive(Debug, thiserror::Error)]
#[error("failed to open graph with iri `{iri}`")]
pub struct DatasetFromIriError {
    pub iri: taganak_core::terms::Iri,
    #[source]
    pub e: FromIriErrorSource,
}

#[derive(Debug, thiserror::Error)]
pub enum FromIriErrorSource {
    #[error(transparent)]
    FromIri(#[from] FromIriError),
    #[error(transparent)]
    IntoGraph(#[from] SourceError),
    #[error(transparent)]
    DatasetError(#[from] DatasetError),
}

#[cfg(feature = "dataset_glob")]
#[derive(Debug, thiserror::Error)]
pub enum GlobReadError {
    #[error("failed to read glob")]
    GlobError(
        #[from]
        #[source]
        GlobError,
    ),
    #[error(transparent)]
    FromIriError(#[from] DatasetFromIriError),
}

impl IriDataset {
    pub fn from_simple_dataset(dataset: SimpleDataset<ConfigurableGraphs>) -> Self {
        Self { dataset }
    }

    pub async fn add_iri(&mut self, iri: &Iri) -> Result<(), FromIriErrorSource> {
        let config = GraphConfigs::from_iri(iri.clone())?;
        let name = Arc::new(Term::NamedNode(iri.clone()));
        self.dataset
            .add_named_graph(name, config.into_graph()?)
            .await?;
        Ok(())
    }

    pub async fn add_iris<'a, I>(&mut self, iris: I) -> Result<(), DatasetFromIriError>
    where
        I: IntoIterator<Item = &'a taganak_core::terms::Iri>,
    {
        for iri in iris {
            self.add_iri(iri).await.map_err(|e| DatasetFromIriError {
                iri: iri.clone(),
                e,
            })?;
        }
        Ok(())
    }

    pub async fn from_iris<'a, I>(iris: I) -> Result<Self, DatasetFromIriError>
    where
        I: IntoIterator<Item = &'a taganak_core::terms::Iri>,
    {
        let mut dataset = Self {
            dataset: SimpleDataset::default(),
        };
        dataset.add_iris(iris).await?;
        Ok(dataset)
    }

    #[cfg(feature = "dataset_glob")]
    pub async fn add_glob(&mut self, pattern: &Pattern) -> Result<(), GlobReadError> {
        for path in glob::glob(pattern.as_str()).unwrap() {
            let Some(iri) = path_to_file_iri(path?) else {
                continue;
            };
            self.add_iri(&iri)
                .await
                .map_err(|e| DatasetFromIriError { iri, e })?;
        }
        Ok(())
    }

    #[cfg(feature = "dataset_glob")]
    pub async fn from_glob(pattern: &Pattern) -> Result<Self, GlobReadError> {
        let mut dataset = Self {
            dataset: SimpleDataset::default(),
        };
        dataset.add_glob(pattern).await?;
        Ok(dataset)
    }
}

impl From<SimpleDataset<ConfigurableGraphs>> for IriDataset {
    fn from(value: SimpleDataset<ConfigurableGraphs>) -> Self {
        Self::from_simple_dataset(value)
    }
}

impl Dataset for IriDataset {
    type View<'a>
        = <SimpleDataset<ConfigurableGraphs> as Dataset>::View<'a>
    where
        Self: 'a;

    type Graph = <SimpleDataset<ConfigurableGraphs> as Dataset>::Graph;

    type DefaultGraph = <SimpleDataset<ConfigurableGraphs> as Dataset>::DefaultGraph;

    type DefaultView<'a>
        = <SimpleDataset<ConfigurableGraphs> as Dataset>::DefaultView<'a>
    where
        Self: 'a;

    fn view(&self) -> impl core::future::Future<Output = <Self as Dataset>::View<'_>> + Send {
        <SimpleDataset<ConfigurableGraphs> as Dataset>::view(&self.dataset)
    }

    fn default_view(&self) -> impl core::future::Future<Output = Self::DefaultView<'_>> + Send {
        self.dataset.default_view()
    }

    fn named_view<'a>(
        &'a self,
        name: &Term,
    ) -> impl core::future::Future<
        Output = Option<<Self::Graph as taganak_core::prelude::Graph>::View<'a>>,
    > + Send {
        self.dataset.named_view(name)
    }

    fn default_graph(&self) -> impl core::future::Future<Output = &Self::DefaultGraph> + Send {
        self.dataset.default_graph()
    }

    fn default_graph_mut(
        &mut self,
    ) -> impl core::future::Future<Output = Option<&mut Self::DefaultGraph>> + Send {
        self.dataset.default_graph_mut()
    }

    fn named_graph(
        &self,
        name: &Term,
    ) -> impl core::future::Future<Output = Option<&Self::Graph>> + Send {
        self.dataset.named_graph(name)
    }

    fn named_graph_mut(
        &mut self,
        name: &Term,
    ) -> impl core::future::Future<Output = Option<&mut Self::Graph>> + Send {
        self.dataset.named_graph_mut(name)
    }

    fn add_named_graph(
        &mut self,
        name: Arc<Term>,
        graph: Self::Graph,
    ) -> impl core::future::Future<Output = Result<(), DatasetError>> + Send {
        self.dataset.add_named_graph(name, graph)
    }

    fn remove_named_graph(
        &mut self,
        name: &Term,
    ) -> impl core::future::Future<Output = Result<Option<Self::Graph>, DatasetError>> {
        self.dataset.remove_named_graph(name)
    }
}

impl Graph for IriDataset {
    type View<'g>
        = <SimpleDataset<ConfigurableGraphs> as Graph>::View<'g>
    where
        Self: 'g;

    fn view(&self) -> impl core::future::Future<Output = Self::View<'_>> + Send {
        <SimpleDataset<ConfigurableGraphs> as Graph>::view(&self.dataset)
    }

    fn clear(
        &mut self,
    ) -> impl core::future::Future<Output = Result<(), taganak_core::prelude::GraphError>> + Send
    {
        self.dataset.clear()
    }
}
