use globset::GlobSetBuilder;
use relative_path::RelativePathBuf;
use walkdir::WalkDir;

pub struct Module {
    name: String,
    paths: Vec<(RelativePathBuf, Vec<crate::config::Tag>)>,
    target_directory: camino::Utf8PathBuf,
}

impl TryFrom<(crate::config::Config, String)> for Module {
    type Error = crate::Error;

    fn try_from(
        (config, module_name): (crate::config::Config, String),
    ) -> Result<Self, Self::Error> {
        let module = config
            .modules
            .get(&module_name)
            .ok_or_else(|| crate::error::ReadConfig::InvalidModuleName(module_name.clone()))?;
        let include = {
            let mut gsb = GlobSetBuilder::new();
            for entry in &module.include {
                gsb.add(entry.glob.clone());
            }
            gsb.build()?
        };
        let exclude = {
            let mut gsb = GlobSetBuilder::new();
            for entry in &module.exclude {
                gsb.add(entry.clone());
            }
            gsb.build()?
        };
        let paths: Result<Vec<_>, _> = WalkDir::new(".")
            .into_iter()
            .flatten()
            .filter(|entry| include.is_match(entry.path()) && !exclude.is_match(entry.path()))
            .map(|entry| {
                RelativePathBuf::from_path(entry.path()).map(|relative_path| {
                    (
                        relative_path,
                        include
                            .matches(entry.path())
                            .iter()
                            .enumerate()
                            .filter_map(|(idx, num_matches)| {
                                if *num_matches != 0 {
                                    module.include[idx].tag.clone()
                                } else {
                                    None
                                }
                            })
                            .collect(),
                    )
                })
            })
            .collect();

        Ok(Self {
            name: module_name,
            paths: paths?,
        })
    }
}