use crate::config::Config;
use crate::gh::{checkout_pr, get_pr_branch, is_gh_authenticated, is_gh_installed};
use crate::git::GitRepo;
use crate::ui::{print_info, print_success};
use crate::worktree::{list_worktrees, slug_from_branch};
use anyhow::{bail, Context, Result};
use std::process::Command;
pub struct PrOptions {
pub number: u32,
pub print_path: bool,
pub open: bool,
}
pub fn execute(repo: &GitRepo, opts: PrOptions) -> Result<()> {
if !is_gh_installed() {
bail!("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");
}
if !is_gh_authenticated() {
bail!("GitHub CLI is not authenticated. Run `gh auth login` to authenticate.");
}
let config = Config::load(repo)?;
let pr_name = format!("pr-{}", opts.number);
let worktrees = list_worktrees(repo)?;
if let Some(existing) = worktrees.iter().find(|wt| {
wt.branch_short.as_deref() == Some(&pr_name)
|| wt.path.file_name().and_then(|s| s.to_str()) == Some(&pr_name)
}) {
print_info(&format!(
"PR #{} already has a worktree at {}",
opts.number,
existing.path.display()
));
println!("{}", existing.path.display());
return Ok(());
}
let branch_name = get_pr_branch(opts.number)?;
print_info(&format!(
"PR #{} uses branch '{}'",
opts.number, branch_name
));
let slug = slug_from_branch(&pr_name);
let worktree_path = config.worktree_path(repo, &slug);
if worktree_path.exists() {
bail!(
"Directory already exists: {}\nUse a different path or remove the existing directory.",
worktree_path.display()
);
}
if let Some(parent) = worktree_path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("Failed to create directory: {}", parent.display()))?;
}
let path_str = worktree_path
.to_str()
.ok_or_else(|| anyhow::anyhow!("Path contains invalid UTF-8: {:?}", worktree_path))?;
let output = Command::new("git")
.current_dir(&repo.root)
.args(["worktree", "add", path_str, "--detach"])
.output()
.context("Failed to create worktree")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("Failed to create worktree: {}", stderr.trim());
}
checkout_pr(&worktree_path, opts.number)?;
if opts.print_path {
println!("{}", worktree_path.display());
} else {
print_success(&format!(
"Created PR worktree at {}",
worktree_path.display()
));
}
if opts.open {
if let Some(open_cmd) = &config.open_cmd {
let _ = Command::new(open_cmd).arg(&worktree_path).spawn();
}
}
Ok(())
}