#!/usr/bin/env bash
# sync_repos.sh — mirror branches + tags between the two Gitea instances that
# host the Timesheet repo, in BOTH directions.
#
#   dadeschools : gitea.dadeschools.net / Contractor/Timesheet           (HTTPS — SSH:2222 is flaky)
#   prgs        : gitea-ssh.prgs.cc:2222 / Scaled-Tech-Consulting/Timesheet (SSH — HTTPS host 404s)
#
# Safety model:
#   * ADDITIVE by default. A branch on only one side is pushed to the other.
#   * A shared branch where one side is strictly ahead is fast-forwarded.
#   * A shared branch that has DIVERGED is skipped with a loud warning
#     (never auto-overwritten). Resolve those by hand.
#   * Dry-run by default; pass --apply to actually push. --force lets the
#     fast-forward pushes use --force (still skips diverged branches).
#
# Auth is automatic via the macOS keychain (`git credential fill`), same as the
# other Gitea-Tools scripts. Run it from inside any clone of the repo, or set
# REPO=/path/to/clone.
#
# Usage:
#   ./sync_repos.sh            # dry run — show what WOULD sync
#   ./sync_repos.sh --apply    # perform the sync
#   ./sync_repos.sh --apply --force
set -euo pipefail

# --- config ------------------------------------------------------------------
DADE_URL="https://gitea.dadeschools.net/Contractor/Timesheet.git"
PRGS_URL="ssh://git@gitea-ssh.prgs.cc:2222/Scaled-Tech-Consulting/Timesheet.git"
REPO="${REPO:-$(pwd)}"

APPLY=0
FORCE=0
for arg in "$@"; do
    case "$arg" in
        --apply) APPLY=1 ;;
        --force) FORCE=1 ;;
        -h|--help) sed -n '2,30p' "$0"; exit 0 ;;
        *) echo "Unknown flag: $arg" >&2; exit 2 ;;
    esac
done

cd "$REPO"
git rev-parse --git-dir >/dev/null 2>&1 || { echo "Not a git repo: $REPO" >&2; exit 1; }

note()  { printf '%s\n' "$*"; }
action() { if [ "$APPLY" -eq 1 ]; then printf '  ✓ %s\n' "$*"; else printf '  [dry] %s\n' "$*"; fi; }

# --- fetch both sides into private namespaces --------------------------------
note "==> Fetching both remotes..."
git fetch --prune "$DADE_URL" '+refs/heads/*:refs/sync/dade/*' 'refs/tags/*:refs/tags/*' >/dev/null 2>&1 \
    || { echo "ERROR: cannot fetch dadeschools (check VPN/keychain)" >&2; exit 1; }
git fetch --prune "$PRGS_URL" '+refs/heads/*:refs/sync/prgs/*' >/dev/null 2>&1 \
    || { echo "ERROR: cannot fetch prgs (check SSH access)" >&2; exit 1; }

# --- reconcile branches ------------------------------------------------------
# push <dest_url> <sha> <branch> <label>
push_branch() {
    local url="$1" sha="$2" b="$3" label="$4" ff_flag=""
    [ "$FORCE" -eq 1 ] && ff_flag="--force"
    action "push '${b}' -> ${label}"
    if [ "$APPLY" -eq 1 ]; then
        git push $ff_flag "$url" "${sha}:refs/heads/${b}" >/dev/null 2>&1 \
            && note "        done." \
            || note "        FAILED (see: git push $ff_flag $url ${sha}:refs/heads/${b})"
    fi
}

branches=$(git for-each-ref --format='%(refname:lstrip=3)' refs/sync/dade refs/sync/prgs | sort -u)

note "==> Reconciling branches..."
synced=0; pushed=0; diverged=0
for b in $branches; do
    d=$(git rev-parse -q --verify "refs/sync/dade/${b}" || true)
    p=$(git rev-parse -q --verify "refs/sync/prgs/${b}" || true)

    if [ -n "$d" ] && [ -z "$p" ]; then
        note "branch '${b}': only on dadeschools"; push_branch "$PRGS_URL" "$d" "$b" "prgs"; pushed=$((pushed+1)); continue
    fi
    if [ -z "$d" ] && [ -n "$p" ]; then
        note "branch '${b}': only on prgs"; push_branch "$DADE_URL" "$p" "$b" "dadeschools"; pushed=$((pushed+1)); continue
    fi
    # on both
    if [ "$d" = "$p" ]; then synced=$((synced+1)); continue; fi
    if git merge-base --is-ancestor "$p" "$d"; then
        note "branch '${b}': dadeschools is ahead (fast-forward)"; push_branch "$PRGS_URL" "$d" "$b" "prgs"; pushed=$((pushed+1))
    elif git merge-base --is-ancestor "$d" "$p"; then
        note "branch '${b}': prgs is ahead (fast-forward)"; push_branch "$DADE_URL" "$p" "$b" "dadeschools"; pushed=$((pushed+1))
    else
        note "branch '${b}': ⚠ DIVERGED — skipped (resolve manually; --force will not auto-pick a winner)"; diverged=$((diverged+1))
    fi
done

# --- tags (additive both ways) ----------------------------------------------
note "==> Syncing tags..."
sync_tags() {
    local url="$1" label="$2"
    local remote_tags local_tags missing
    remote_tags=$(git ls-remote --tags "$url" 2>/dev/null | awk '{print $2}' | grep -v '\^{}' | sed 's#refs/tags/##' | sort || true)
    local_tags=$(git tag -l | sort)
    missing=$(comm -23 <(printf '%s\n' "$local_tags") <(printf '%s\n' "$remote_tags"))
    if [ -z "$missing" ]; then note "  ${label}: tags up to date"; return; fi
    for t in $missing; do
        action "push tag '${t}' -> ${label}"
        [ "$APPLY" -eq 1 ] && git push "$url" "refs/tags/${t}" >/dev/null 2>&1 && note "        done."
    done
}
sync_tags "$DADE_URL" "dadeschools"
sync_tags "$PRGS_URL" "prgs"

# --- cleanup private namespace ----------------------------------------------
git for-each-ref --format='%(refname)' refs/sync | while read -r r; do git update-ref -d "$r"; done

note ""
note "==> Summary: ${synced} already in sync, ${pushed} branch push(es), ${diverged} diverged/skipped."
[ "$APPLY" -eq 0 ] && note "    (dry run — re-run with --apply to perform the sync)"
[ "$diverged" -gt 0 ] && note "    ⚠ ${diverged} diverged branch(es) need manual reconciliation."
exit 0
