supgit 0.2.0

A simple Git CLI wrapper for common Git operations
use anyhow::Result;

use crate::git::run_git_quiet;
use crate::status::get_current_branch;

pub fn run_push(remote: Option<String>, branch: Option<String>) -> Result<()> {
    if remote.is_none() && branch.is_some() {
        anyhow::bail!("cannot specify --branch without --remote");
    }

    print!("→ Pushing");
    if let Some(ref r) = remote {
        print!(" to {}", r);
    }
    if let Some(ref b) = branch {
        print!("/{}", b);
    }
    println!("...");

    let mut args_owned = vec!["push".to_string()];
    if let Some(remote) = remote {
        args_owned.push(remote);
        if let Some(branch) = branch {
            args_owned.push(branch);
        }
    }

    let args_refs: Vec<&str> = args_owned.iter().map(String::as_str).collect();
    run_git_quiet(&args_refs)?;
    println!("✓ Pushed successfully");
    Ok(())
}

pub fn run_pull(remote: Option<String>, branch: Option<String>) -> Result<()> {
    print!("→ Pulling");
    if let Some(ref r) = remote {
        print!(" from {}", r);
    }
    if let Some(ref b) = branch {
        print!("/{}", b);
    }
    println!("...");

    let mut args_owned = vec!["pull".to_string()];
    if let Some(remote) = remote {
        args_owned.push(remote);
        if let Some(branch) = branch {
            args_owned.push(branch);
        }
    }

    let args_refs: Vec<&str> = args_owned.iter().map(String::as_str).collect();
    run_git_quiet(&args_refs)?;
    println!("✓ Pulled successfully");
    Ok(())
}

pub fn run_sync(remote: Option<&str>, branch: Option<&str>) -> Result<()> {
    let remote_name = remote.unwrap_or("origin");

    println!("→ Fetching from {}...", remote_name);
    let fetch_result = run_git_quiet(&["fetch", remote_name]);
    if let Err(e) = fetch_result {
        let err_str = e.to_string();
        if err_str.contains("could not resolve host") || err_str.contains("network") {
            eprintln!("✗ Network error: cannot reach '{}'", remote_name);
            return Err(e);
        }
        eprintln!("⚠ Fetch failed: {}", e);
        eprintln!("  Continuing with local state...");
    } else {
        println!("✓ Fetch complete");
    }

    println!("→ Pulling changes...");
    let mut pull_args = vec!["pull"];
    let mut pull_owned: Vec<String> = Vec::new();
    if let Some(r) = remote {
        pull_owned.push(r.to_string());
        if let Some(b) = branch {
            pull_owned.push(b.to_string());
        }
    }
    let pull_refs: Vec<&str> = if pull_owned.is_empty() {
        pull_args
    } else {
        pull_args.extend(pull_owned.iter().map(String::as_str));
        pull_args
    };

    let pull_result = run_git_quiet(&pull_refs);
    if let Err(e) = pull_result {
        let err_str = e.to_string();
        if err_str.contains("CONFLICT") || err_str.contains("merge conflict") {
            eprintln!("✗ Pull failed due to merge conflicts");
            eprintln!("  Resolve conflicts manually:");
            eprintln!("    1. Edit conflicting files (marked with <<<<<<<)");
            eprintln!("    2. Run 'supgit stage .' to stage resolved files");
            eprintln!("    3. Run 'supgit commit' to complete the merge");
            return Err(e);
        }
        if err_str.contains("no tracking information") {
            eprintln!("✗ Branch has no upstream configured");
            eprintln!(
                "  Try: git branch --set-upstream-to={}/{}",
                remote_name,
                get_current_branch().unwrap_or_default()
            );
            return Err(e);
        }
        eprintln!("⚠ Pull failed: {}", e);
        eprintln!("  Attempting to push local changes anyway...");
    } else {
        println!("✓ Pull complete");
    }

    println!("→ Pushing changes...");
    let mut push_args = vec!["push"];
    let mut push_owned: Vec<String> = Vec::new();
    if let Some(r) = remote {
        push_owned.push(r.to_string());
        if let Some(b) = branch {
            push_owned.push(b.to_string());
        }
    }
    let push_refs: Vec<&str> = if push_owned.is_empty() {
        push_args
    } else {
        push_args.extend(push_owned.iter().map(String::as_str));
        push_args
    };

    let push_result = run_git_quiet(&push_refs);
    if let Err(e) = push_result {
        let err_str = e.to_string();
        if err_str.contains("rejected") {
            eprintln!("✗ Push rejected: remote has new commits");
            eprintln!("  Run 'supgit pull' first to integrate remote changes.");
        } else if err_str.contains("no upstream branch") {
            eprintln!("✗ No upstream branch configured");
            eprintln!(
                "  Try: git push -u {} {}",
                remote_name,
                get_current_branch().unwrap_or_default()
            );
        } else {
            eprintln!("✗ Push failed: {}", e);
        }
        return Err(e);
    }

    println!("✓ Sync complete: fetched, pulled, and pushed successfully.");
    Ok(())
}