//! Git utilities for staged file discovery.

use std::path::{Path, PathBuf};
use std::process::Command;

use crate::AphoriaError;

/// Find the root of the git repository containing `from`.
///
/// Walks up the directory tree looking for a `.git` directory.
pub fn find_repo_root(from: &Path) -> Result<PathBuf, AphoriaError> {
    let mut current = from.to_path_buf();

    loop {
        if current.join(".git").exists() {
            return Ok(current);
        }

        if !current.pop() {
            return Err(AphoriaError::NotGitRepo);
        }
    }
}

/// Get the list of staged files from git.
///
/// Uses `git diff --cached --name-only --diff-filter=ACMR` to get:
/// - A: Added files
/// - C: Copied files
/// - M: Modified files
/// - R: Renamed files
///
/// Excludes deleted files (D) since we can't scan them.
pub fn get_staged_files(repo_root: &Path) -> Result<Vec<PathBuf>, AphoriaError> {
    let output = Command::new("git")
        .args(["diff", "--cached", "--name-only", "--diff-filter=ACMR"])
        .current_dir(repo_root)
        .output()
        .map_err(|e| {
            AphoriaError::GitCommand(format!("Failed to execute git diff command: {e}"))
        })?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        return Err(AphoriaError::GitCommand(stderr.to_string()));
    }

    let stdout = String::from_utf8_lossy(&output.stdout);
    let files: Vec<PathBuf> =
        stdout.lines().filter(|line| !line.is_empty()).map(|line| repo_root.join(line)).collect();

    Ok(files)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use tempfile::TempDir;

    #[test]
    fn test_find_repo_root_not_a_repo() {
        let temp_dir = TempDir::new().expect("create temp dir");
        let result = find_repo_root(temp_dir.path());
        assert!(matches!(result, Err(AphoriaError::NotGitRepo)));
    }

    #[test]
    fn test_find_repo_root_from_subdir() {
        let temp_dir = TempDir::new().expect("create temp dir");
        let root = temp_dir.path();

        // Create a fake .git directory
        fs::create_dir(root.join(".git")).expect("create .git");

        // Create a subdirectory
        let subdir = root.join("src").join("lib");
        fs::create_dir_all(&subdir).expect("create subdir");

        // Should find the repo root from the subdirectory
        let found = find_repo_root(&subdir).expect("find repo root");
        assert_eq!(found, root);
    }
}
