#!/usr/bin/env bash
# sync-repos — Pull latest for all git repos under ~/dev/claude
# Usage:
#   sync-repos              # pull all repos
#   sync-repos --dryrun     # show what would be pulled
#   sync-repos -n           # same as --dryrun

set -euo pipefail

BASE_DIR="${HOME}/dev/claude"
DRYRUN=false

if [[ "${1:-}" == "--dryrun" || "${1:-}" == "-n" ]]; then
    DRYRUN=true
fi

GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
DIM='\033[2m'
RESET='\033[0m'

# Collect all repos upfront (sorted for deterministic output order)
repo_dirs=()
while IFS= read -r gitdir; do
    repo_dirs+=("$(dirname "$gitdir")")
done < <(find "$BASE_DIR" -maxdepth 4 -name .git -type d 2>/dev/null | sort)

# Temp dir for capturing per-repo output in parallel
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

# Spawn all git operations in parallel
for i in "${!repo_dirs[@]}"; do
    repo_dir="${repo_dirs[$i]}"
    repo_name="${repo_dir#"${BASE_DIR}/"}"
    outfile="$tmpdir/$i"

    (
        # Skip if no remote configured
        if ! git -C "$repo_dir" remote get-url origin &>/dev/null; then
            printf "${DIM}  skip  %-40s no remote${RESET}\n" "$repo_name" >"$outfile.out"
            echo "skip" >"$outfile.status"
            exit 0
        fi

        # Check for uncommitted changes
        if ! git -C "$repo_dir" diff --quiet 2>/dev/null || ! git -C "$repo_dir" diff --cached --quiet 2>/dev/null; then
            has_changes=true
        else
            has_changes=false
        fi

        branch="$(git -C "$repo_dir" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")"

        if $DRYRUN; then
            if $has_changes; then
                printf "${YELLOW}  dirty %-40s %s (has uncommitted changes)${RESET}\n" "$repo_name" "$branch" >"$outfile.out"
                echo "dirty" >"$outfile.status"
            else
                printf "${GREEN}  pull  %-40s %s${RESET}\n" "$repo_name" "$branch" >"$outfile.out"
                echo "pull" >"$outfile.status"
            fi
            exit 0
        fi

        # Pull with --ff-only to avoid creating merge commits
        output="$(git -C "$repo_dir" pull --ff-only 2>&1)" && rc=0 || rc=$?

        if [[ $rc -eq 0 ]]; then
            if echo "$output" | grep -q "Already up to date"; then
                printf "${DIM}  ok    %-40s %s (up to date)${RESET}\n" "$repo_name" "$branch" >"$outfile.out"
            else
                printf "${GREEN}  pull  %-40s %s${RESET}\n" "$repo_name" "$branch" >"$outfile.out"
            fi
            echo "pull" >"$outfile.status"
        else
            {
                printf "${RED}  FAIL  %-40s %s${RESET}\n" "$repo_name" "$branch"
                printf "${DIM}         %s${RESET}\n" "$output"
            } >"$outfile.out"
            echo "fail" >"$outfile.status"
        fi
    ) &
done

# Wait for all parallel jobs to finish
wait

# Print results in original sorted order and tally counts
pulled=0
skipped=0
failed=0
dirty=0

for i in "${!repo_dirs[@]}"; do
    outfile="$tmpdir/$i"
    [[ -f "$outfile.out" ]] && cat "$outfile.out"
    if [[ -f "$outfile.status" ]]; then
        case "$(cat "$outfile.status")" in
            pull)  pulled=$((pulled + 1)) ;;
            skip)  skipped=$((skipped + 1)) ;;
            fail)  failed=$((failed + 1)) ;;
            dirty) dirty=$((dirty + 1)) ;;
        esac
    fi
done

echo ""
if $DRYRUN; then
    echo "Dryrun: ${pulled} would pull, ${dirty} dirty, ${skipped} skipped"
else
    echo "Done: ${pulled} pulled, ${failed} failed, ${skipped} skipped"
fi

[[ $failed -eq 0 ]]
