//! Git worktree management — creates, lists, and removes worktrees for agent isolation.
use std::path::{Path, PathBuf};

mod cleanup;
mod create;
mod git;
mod remove;
mod sweep;

pub use cleanup::{format_report, run_cleanup};
pub use create::create_worktree;
pub use create::install_pre_commit_hook;
pub use git::migrate_slug_paths;
pub(crate) use git::resolve_base_branch;
pub use remove::remove_worktree_by_story_id;
pub(crate) use sweep::worktree_should_be_swept;

#[derive(Debug, Clone)]
/// Details about a newly created worktree: path, branch, and base branch.
pub struct WorktreeInfo {
    pub path: PathBuf,
    pub branch: String,
    pub base_branch: String,
}

#[derive(Debug, Clone)]
/// A discovered worktree on disk: its story ID and filesystem path.
pub struct WorktreeListEntry {
    pub story_id: String,
    pub path: PathBuf,
}

/// Worktree path inside the project: `{project_root}/.huskies/worktrees/{story_id}`.
pub fn worktree_path(project_root: &Path, story_id: &str) -> PathBuf {
    project_root
        .join(".huskies")
        .join("worktrees")
        .join(story_id)
}

/// Write a `.mcp.json` file in the given directory pointing to the huskies
/// HTTP MCP endpoint at the given port.
pub fn write_mcp_json(dir: &Path, port: u16) -> Result<(), String> {
    let content = format!(
        "{{\n  \"mcpServers\": {{\n    \"huskies\": {{\n      \"type\": \"http\",\n      \"url\": \"http://localhost:{port}/mcp\"\n    }}\n  }}\n}}\n"
    );
    std::fs::write(dir.join(".mcp.json"), content).map_err(|e| format!("Write .mcp.json: {e}"))
}

/// Find the worktree path for a given story ID, if it exists.
pub fn find_worktree_path(project_root: &Path, story_id: &str) -> Option<PathBuf> {
    let wt_path = project_root
        .join(".huskies")
        .join("worktrees")
        .join(story_id);
    if wt_path.is_dir() {
        Some(wt_path)
    } else {
        None
    }
}

/// List all worktrees under `{project_root}/.huskies/worktrees/`.
pub fn list_worktrees(project_root: &Path) -> Result<Vec<WorktreeListEntry>, String> {
    let worktrees_dir = project_root.join(".huskies").join("worktrees");
    if !worktrees_dir.exists() {
        return Ok(Vec::new());
    }
    let mut entries = Vec::new();
    for entry in std::fs::read_dir(&worktrees_dir).map_err(|e| format!("list worktrees: {e}"))? {
        let entry = entry.map_err(|e| format!("list worktrees entry: {e}"))?;
        let path = entry.path();
        if path.is_dir() {
            let story_id = path
                .file_name()
                .map(|n| n.to_string_lossy().to_string())
                .unwrap_or_default();
            entries.push(WorktreeListEntry { story_id, path });
        }
    }
    entries.sort_by(|a, b| a.story_id.cmp(&b.story_id));
    Ok(entries)
}

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

    #[test]
    fn write_mcp_json_uses_given_port() {
        let tmp = TempDir::new().unwrap();
        write_mcp_json(tmp.path(), 4242).unwrap();
        let content = std::fs::read_to_string(tmp.path().join(".mcp.json")).unwrap();
        assert!(content.contains("http://localhost:4242/mcp"));
    }

    #[test]
    fn write_mcp_json_default_port() {
        let tmp = TempDir::new().unwrap();
        write_mcp_json(tmp.path(), 3001).unwrap();
        let content = std::fs::read_to_string(tmp.path().join(".mcp.json")).unwrap();
        assert!(content.contains("http://localhost:3001/mcp"));
    }

    #[test]
    fn worktree_path_is_inside_project() {
        let project_root = Path::new("/home/user/my-project");
        let path = worktree_path(project_root, "42_my_story");
        assert_eq!(
            path,
            Path::new("/home/user/my-project/.huskies/worktrees/42_my_story")
        );
    }

    #[test]
    fn list_worktrees_empty_when_no_dir() {
        let tmp = TempDir::new().unwrap();
        let entries = list_worktrees(tmp.path()).unwrap();
        assert!(entries.is_empty());
    }

    #[test]
    fn list_worktrees_returns_subdirs() {
        let tmp = TempDir::new().unwrap();
        let worktrees_dir = tmp.path().join(".huskies").join("worktrees");
        fs::create_dir_all(worktrees_dir.join("42_story_a")).unwrap();
        fs::create_dir_all(worktrees_dir.join("43_story_b")).unwrap();
        // A file (not dir) — should be ignored
        fs::write(worktrees_dir.join("readme.txt"), "").unwrap();

        let entries = list_worktrees(tmp.path()).unwrap();
        assert_eq!(entries.len(), 2);
        assert_eq!(entries[0].story_id, "42_story_a");
        assert_eq!(entries[1].story_id, "43_story_b");
    }
}
