#!/bin/sh
set -eu
repo_url="ssh://git@ssh.github.com:443/pikasTech/HWLAB.git"
repo_path="/cache/pikasTech/HWLAB.git"
source_snapshot_ref_prefix=__HWLAB_SOURCE_SNAPSHOT_REF_PREFIX_SHELL__
lock_dir="/cache/.hwlab-sync.lock"
sync_started_ms=$(node -e 'console.log(Date.now())')
now_ms() { node -e 'console.log(Date.now())'; }
emit_timing() {
  phase="$1"
  status="$2"
  started_ms="$3"
  finished_ms=$(now_ms)
  duration_ms=$((finished_ms - started_ms))
  printf '{"event":"git-mirror-sync-timing","phase":"%s","status":"%s","durationMs":%s,"repository":"pikasTech/HWLAB"}
' "$phase" "$status" "$duration_ms"
}
mkdir -p /cache/pikasTech /root/.ssh
if ! mkdir "$lock_dir" 2>/dev/null; then
  echo "git mirror sync already running"
  exit 0
fi
trap 'rmdir "$lock_dir" 2>/dev/null || true' EXIT INT TERM
cp /git-ssh/ssh-privatekey /root/.ssh/id_rsa
chmod 0400 /root/.ssh/id_rsa
# __HWLAB_GIT_MIRROR_SSH_PRELUDE__
if [ -d "$repo_path/objects" ] && [ -f "$repo_path/HEAD" ]; then
  git -C "$repo_path" remote set-url origin "$repo_url" || git -C "$repo_path" remote add origin "$repo_url"
else
  rm -rf "$repo_path"
  git init --bare "$repo_path"
  git -C "$repo_path" remote add origin "$repo_url"
fi
/script/install-hooks.sh
git -C "$repo_path" config uploadpack.hideRefs refs/mirror-stage
git -C "$repo_path" config uploadpack.allowReachableSHA1InWant true
git -C "$repo_path" config uploadpack.allowAnySHA1InWant true
git -C "$repo_path" config --unset-all remote.origin.fetch 2>/dev/null || true
# __HWLAB_FETCH_CONFIG_COMMANDS__
fetch_started_ms=$(now_ms)
timeout 180 git -C "$repo_path" fetch --quiet --prune origin
emit_timing fetch succeeded "$fetch_started_ms"
git -C "$repo_path" for-each-ref --format='%(objectname) %(refname)' refs/mirror-stage/heads > /tmp/hwlab-stage-heads
git -C "$repo_path" for-each-ref --format='%(refname)' refs/mirror-stage/heads > /tmp/hwlab-stage-head-refs
git -C "$repo_path" for-each-ref --format='%(objectname) %(refname)' refs/mirror-stage/tags > /tmp/hwlab-stage-tags
git -C "$repo_path" for-each-ref --format='%(refname)' refs/mirror-stage/tags > /tmp/hwlab-stage-tag-refs
if [ ! -s /tmp/hwlab-stage-heads ]; then
  echo "git mirror sync fetched no branch refs" >&2
  exit 41
fi
for required_ref in __HWLAB_REQUIRED_REF_ARGS__; do
  git -C "$repo_path" show-ref --verify --quiet "$required_ref" || { echo "git mirror sync missing required ref $required_ref" >&2; exit 43; }
done
validate_started_ms=$(now_ms)
while read -r sha ref; do
  [ -n "$sha" ] || continue
  git -C "$repo_path" cat-file -e "$sha^{commit}"
  git -C "$repo_path" cat-file -e "$sha^{tree}"
  if git -C "$repo_path" rev-list --objects --missing=print "$sha" | grep -q '^?'; then
    echo "git mirror sync found missing objects for $ref $sha" >&2
    exit 42
  fi
done < /tmp/hwlab-stage-heads
emit_timing validate succeeded "$validate_started_ms"
publish_started_ms=$(now_ms)
while read -r sha ref; do
  [ -n "$sha" ] || continue
  name=$(printf '%s
' "$ref" | sed 's#^refs/mirror-stage/heads/##')
  is_gitops_branch=false
  for gitops_branch in __HWLAB_GITOPS_BRANCH_ARGS__; do
    if [ "$name" = "$gitops_branch" ]; then is_gitops_branch=true; break; fi
  done
  if [ "$is_gitops_branch" = true ]; then
    local_sha=$(git -C "$repo_path" show-ref --verify --hash "refs/heads/$name" 2>/dev/null || true)
    if [ -n "$local_sha" ] && [ "$local_sha" != "$sha" ]; then
      if git -C "$repo_path" merge-base --is-ancestor "$sha" "$local_sha"; then
        echo "git mirror sync keeps local $name ahead of GitHub"
        continue
      fi
      if ! git -C "$repo_path" merge-base --is-ancestor "$local_sha" "$sha"; then
        echo "git mirror sync found divergent $name local=$local_sha github=$sha" >&2
        exit 44
      fi
    fi
  fi
  git -C "$repo_path" update-ref "refs/heads/$name" "$sha"
done < /tmp/hwlab-stage-heads
for source_branch in __HWLAB_SOURCE_BRANCH_ARGS__; do
  source_sha=$(git -C "$repo_path" show-ref --verify --hash "refs/heads/$source_branch" 2>/dev/null || true)
  if [ -n "$source_sha" ]; then
    source_stage_ref="${source_snapshot_ref_prefix%/}/$source_branch/$source_sha"
    git -C "$repo_path" update-ref "$source_stage_ref" "$source_sha"
  fi
done
while read -r sha ref; do
  [ -n "$sha" ] || continue
  name=$(printf '%s
' "$ref" | sed 's#^refs/mirror-stage/tags/##')
  git -C "$repo_path" update-ref "refs/tags/$name" "$sha"
done < /tmp/hwlab-stage-tags
git -C "$repo_path" for-each-ref --format='%(refname)' refs/heads > /tmp/hwlab-public-heads
while read -r ref; do
  [ -n "$ref" ] || continue
  name=$(printf '%s
' "$ref" | sed 's#^refs/heads/##')
  if ! grep -Fxq "refs/mirror-stage/heads/$name" /tmp/hwlab-stage-head-refs; then
    git -C "$repo_path" update-ref -d "$ref"
  fi
done < /tmp/hwlab-public-heads
git -C "$repo_path" for-each-ref --format='%(refname)' refs/tags > /tmp/hwlab-public-tags
while read -r ref; do
  [ -n "$ref" ] || continue
  name=$(printf '%s
' "$ref" | sed 's#^refs/tags/##')
  if ! grep -Fxq "refs/mirror-stage/tags/$name" /tmp/hwlab-stage-tag-refs; then
    git -C "$repo_path" update-ref -d "$ref"
  fi
done < /tmp/hwlab-public-tags
emit_timing publish succeeded "$publish_started_ms"
for head_branch in __HWLAB_SOURCE_BRANCH_ARGS__; do
  if git -C "$repo_path" show-ref --verify --quiet "refs/heads/$head_branch"; then
    git -C "$repo_path" symbolic-ref HEAD "refs/heads/$head_branch"
    break
  fi
done
fsck_started_ms=$(now_ms)
git -C "$repo_path" fsck --connectivity-only --no-dangling >/tmp/hwlab-git-fsck.out
emit_timing fsck succeeded "$fsck_started_ms"
git -C "$repo_path" update-server-info
published_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
for source_branch in __HWLAB_SOURCE_BRANCH_ARGS__; do
  git -C "$repo_path" rev-parse "refs/heads/$source_branch" 2>/dev/null | tee "/cache/HWLAB-$source_branch.head" >/dev/null || true
done
printf '%s
' "$published_at" > /cache/HWLAB.last-sync
export published_at
export source_snapshot_ref_prefix
export mirror_branches_json=__HWLAB_MIRROR_BRANCHES_JSON_SHELL__
export source_branches_json=__HWLAB_SOURCE_BRANCHES_JSON_SHELL__
export gitops_branches_json=__HWLAB_GITOPS_BRANCHES_JSON_SHELL__
node - <<'NODE' | tee /cache/HWLAB.last-sync.json
const { execFileSync } = require("node:child_process");
const repo = "/cache/pikasTech/HWLAB.git";
const mirrorBranches = JSON.parse(process.env.mirror_branches_json || "[]");
const sourceBranches = JSON.parse(process.env.source_branches_json || "[]");
const gitopsBranches = JSON.parse(process.env.gitops_branches_json || "[]");
const sourceSnapshotRefPrefix = (process.env.source_snapshot_ref_prefix || "refs/unidesk/snapshots/hwlab-node-runtime").replace(//+$/u, "");
function rev(ref) {
  try { return execFileSync("git", ["-C", repo, "rev-parse", ref], { encoding: "utf8" }).trim() || null; } catch { return null; }
}
const refs = {};
for (const branch of mirrorBranches) {
  refs["refs/heads/" + branch] = rev("refs/heads/" + branch);
  refs["refs/mirror-stage/heads/" + branch] = rev("refs/mirror-stage/heads/" + branch);
}
const sourceSnapshots = {};
for (const branch of sourceBranches) {
  const source = refs["refs/heads/" + branch] || null;
  const stageRef = source ? sourceSnapshotRefPrefix + "/" + branch + "/" + source : null;
  const sourceSnapshot = stageRef ? rev(stageRef) : null;
  if (stageRef) refs[stageRef] = sourceSnapshot;
  sourceSnapshots[branch] = { stageRef, sourceSnapshot };
}
const sourceInSync = sourceBranches.every((branch) => refs["refs/heads/" + branch] && refs["refs/heads/" + branch] === refs["refs/mirror-stage/heads/" + branch] && sourceSnapshots[branch]?.sourceSnapshot === refs["refs/mirror-stage/heads/" + branch]);
const gitopsInSync = gitopsBranches.every((branch) => refs["refs/heads/" + branch] && refs["refs/heads/" + branch] === refs["refs/mirror-stage/heads/" + branch]);
const pendingFlush = gitopsBranches.some((branch) => refs["refs/heads/" + branch] && refs["refs/mirror-stage/heads/" + branch] && refs["refs/heads/" + branch] !== refs["refs/mirror-stage/heads/" + branch]);
const payload = {
  event: "git-mirror-sync",
  status: "published",
  repository: "pikasTech/HWLAB",
  publishedAt: process.env.published_at,
  pendingFlush,
  sourceInSync,
  gitopsInSync,
  sourceSnapshots,
  refs
};
console.log(JSON.stringify(payload));
NODE
emit_timing total succeeded "$sync_started_ms"
