use crate::dot::config::DotfileConfig;
use crate::dot::dotfilerepo::DotfileRepo;
use crate::dot::override_config::DotfileSource;
use anyhow::Result;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

pub use crate::common::home_dir;

pub fn default_source_for(sources: &[DotfileSource]) -> Option<DotfileSource> {
    sources.first().cloned()
}

pub fn list_sources_for_target(
    config: &DotfileConfig,
    target_path: &Path,
) -> Result<Vec<DotfileSource>> {
    let home = home_dir();
    let is_home = target_path.starts_with(&home);
    let relative_path = if is_home {
        target_path.strip_prefix(&home).unwrap_or(target_path)
    } else {
        target_path.strip_prefix("/").unwrap_or(target_path)
    };
    let mut sources = Vec::new();

    for repo_config in &config.repos {
        if !repo_config.enabled {
            continue;
        }

        let dotfile_repo = match DotfileRepo::new(config, repo_config.name.clone()) {
            Ok(repo) => repo,
            Err(_) => continue,
        };

        for dotfile_dir in dotfile_repo.active_dotfile_dirs() {
            if dotfile_dir.is_root == is_home {
                continue;
            }
            let subdir_name = dotfile_dir
                .path
                .file_name()
                .map(|s| s.to_string_lossy().to_string())
                .unwrap_or_default();

            // Try the plain source first, then the `.age`-suffixed variant.
            // A given target may resolve to either kind in different repos,
            // and we want both to be reported.
            let plain = dotfile_dir.path.join(relative_path);
            let encrypted = crate::dot::encryption::append_age_suffix(&plain);
            for candidate in [plain, encrypted] {
                if candidate.exists() {
                    sources.push(DotfileSource {
                        repo_name: repo_config.name.clone(),
                        subdir_name: subdir_name.clone(),
                        source_path: candidate,
                    });
                }
            }
        }
    }

    Ok(sources)
}

pub fn list_sources_by_target_in_dir(
    config: &DotfileConfig,
    dir_path: &Path,
) -> Result<HashMap<PathBuf, Vec<DotfileSource>>> {
    let home = home_dir();
    let mut sources_by_target: HashMap<PathBuf, Vec<DotfileSource>> = HashMap::new();

    for repo_config in &config.repos {
        if !repo_config.enabled {
            continue;
        }

        let dotfile_repo = match DotfileRepo::new(config, repo_config.name.clone()) {
            Ok(repo) => repo,
            Err(_) => continue,
        };

        for dotfile_dir in dotfile_repo.active_dotfile_dirs() {
            let subdir_name = dotfile_dir
                .path
                .file_name()
                .map(|s| s.to_string_lossy().to_string())
                .unwrap_or_default();

            for entry in WalkDir::new(&dotfile_dir.path)
                .into_iter()
                .filter_map(|e| e.ok())
                .filter(|e| {
                    !e.path().to_string_lossy().contains("/.git/") && e.file_type().is_file()
                })
            {
                let source_path = entry.path().to_path_buf();
                let relative_path = match source_path.strip_prefix(&dotfile_dir.path) {
                    Ok(rel) => rel.to_path_buf(),
                    Err(_) => continue,
                };
                // Strip `.age` from the target path so encrypted and plain
                // sources collide on the same target key in the returned map.
                let target_relative = crate::dot::encryption::strip_age_suffix(&relative_path)
                    .unwrap_or(relative_path);
                let target_path = if dotfile_dir.is_root {
                    std::path::Path::new("/").join(target_relative)
                } else {
                    home.join(target_relative)
                };

                if !target_path.starts_with(dir_path) {
                    continue;
                }

                sources_by_target
                    .entry(target_path)
                    .or_default()
                    .push(DotfileSource {
                        repo_name: repo_config.name.clone(),
                        subdir_name: subdir_name.clone(),
                        source_path,
                    });
            }
        }
    }

    Ok(sources_by_target)
}
