#!/bin/sh
set -eu

script_dir=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
root_dir=$(CDPATH= cd -- "$script_dir/.." && pwd)
base_output="${NBDE_BASE_CHANNELS_OUTPUT:-$root_dir/pins/base-channels.sexp}"
legion_output="${NBDE_LEGION_CHANNELS_OUTPUT:-$root_dir/pins/legion-channels.sexp}"

fork_url="https://git.teralink.net/tribes/guix-fork.git"
fork_branch="refactor/substituter-trace-framing"
fork_introduction_commit="093f27dde01cdbda68f2ec4b81e5a34ae180aab9"
fork_introduction_signer="6688 9153 C51C 4613 A493  A525 2F0D FD14 EF99 DAC3"
fork_checkout="$root_dir/../guix-fork"
fork_comment="guix-fork refactor/substituter-trace-framing"

official_url="https://git.teralink.net/tribes/guix.git"
official_branch="master"
official_introduction_commit="9edb3f66fd807b096b48283debdcddccfea34bad"
official_introduction_signer="BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA"
official_checkout="$root_dir/../guix"
official_comment="guix master"

usage() {
  cat <<EOF
Usage: $(basename "$0") [--fork|--official] [--commit COMMIT]

Refreshes $base_output and syncs the Guix entry in $legion_output.
Defaults to the current channel kind from pins/base-channels.sexp and uses the
matching sibling checkout HEAD (../guix-fork or ../guix) when available.
EOF
}

is_full_commit() {
  printf '%s\n' "$1" | grep -Eq '^[0-9a-f]{40}$'
}

current_channel_kind() {
  current_url=$(perl -0ne 'if (/\(name \x27guix\)[\s\S]*?\(url "([^"]+)"\)/s) { print $1; exit 0 } exit 1' "$base_output")
  case "$current_url" in
    "$fork_url")
      printf '%s\n' fork
      ;;
    "$official_url")
      printf '%s\n' official
      ;;
    *)
      echo "Unsupported Guix channel URL in $base_output: $current_url" >&2
      exit 1
      ;;
  esac
}

checkout_head() {
  checkout=$1
  if [ -d "$checkout/.git" ]; then
    git -C "$checkout" rev-parse HEAD
    return 0
  fi
  return 1
}

remote_branch_head() {
  git ls-remote "$1" "refs/heads/$2" | awk 'NR == 1 { print $1 }'
}

channel_kind=
commit_override=

while [ "$#" -gt 0 ]; do
  case "$1" in
    --fork)
      channel_kind=fork
      ;;
    --official)
      channel_kind=official
      ;;
    --commit)
      shift
      [ "$#" -gt 0 ] || {
        echo "Missing value for --commit" >&2
        usage >&2
        exit 2
      }
      commit_override=$1
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      if [ -z "$commit_override" ]; then
        commit_override=$1
      else
        echo "Unexpected argument: $1" >&2
        usage >&2
        exit 2
      fi
      ;;
  esac
  shift
done

[ -n "$channel_kind" ] || channel_kind=$(current_channel_kind)

case "$channel_kind" in
  fork)
    channel_url=$fork_url
    channel_branch=$fork_branch
    channel_introduction_commit=$fork_introduction_commit
    channel_introduction_signer=$fork_introduction_signer
    channel_checkout=$fork_checkout
    channel_comment=$fork_comment
    ;;
  official)
    channel_url=$official_url
    channel_branch=$official_branch
    channel_introduction_commit=$official_introduction_commit
    channel_introduction_signer=$official_introduction_signer
    channel_checkout=$official_checkout
    channel_comment=$official_comment
    ;;
  *)
    echo "Unsupported channel kind: $channel_kind" >&2
    exit 1
    ;;
esac

commit_source=
if [ -n "$commit_override" ]; then
  if [ -d "$channel_checkout/.git" ]; then
    commit=$(git -C "$channel_checkout" rev-parse "$commit_override^{commit}" 2>/dev/null || true)
  else
    commit=
  fi
  [ -n "$commit" ] || commit=$commit_override
  commit_source="override"
else
  commit=$(checkout_head "$channel_checkout" || true)
  if [ -n "$commit" ]; then
    commit_source=$channel_checkout
  else
    commit=$(remote_branch_head "$channel_url" "$channel_branch")
    commit_source="$channel_url#$channel_branch"
  fi
fi

is_full_commit "$commit" || {
  echo "Expected a full 40-character commit, got: $commit" >&2
  exit 1
}

mkdir -p "$(dirname "$base_output")" "$(dirname "$legion_output")"

cat >"$base_output" <<EOF
(list (channel
        (name 'guix)
        (url "$channel_url")
        (branch "$channel_branch")
        ;; $channel_comment
        (commit
          "$commit")
        (introduction
          (make-channel-introduction
            "$channel_introduction_commit"
            (openpgp-fingerprint
              "$channel_introduction_signer")))))
EOF

new_guix_block=$(cat <<EOF
  (channel
    (name 'guix)
    (url "$channel_url")
    (branch "$channel_branch")
    ;; $channel_comment
    (commit
      "$commit")
    (introduction
      (make-channel-introduction
        "$channel_introduction_commit"
        (openpgp-fingerprint
          "$channel_introduction_signer"))))
EOF
)

tmp_output=$(mktemp "${TMPDIR:-/tmp}/update-base-channels-pin.XXXXXX")
trap 'rm -f "$tmp_output"' EXIT HUP INT TERM

NEW_GUIX_BLOCK=$new_guix_block perl -0pe '
  BEGIN { $changed = 0 }
  $changed = s{\A\(list\s*\n  \(channel\b[\s\S]*?\)\)\n(?=  \(channel\b)}{"(list\n$ENV{NEW_GUIX_BLOCK}\n"}se;
  END { exit($changed ? 0 : 1) }
' "$legion_output" >"$tmp_output" || {
  echo "Failed to sync the Guix entry in $legion_output" >&2
  exit 1
}

mv "$tmp_output" "$legion_output"
rm -f "$tmp_output"
trap - EXIT HUP INT TERM

printf 'Updated %s\n' "$base_output"
printf 'Synced %s\n' "$legion_output"
printf 'Channel: %s\n' "$channel_kind"
printf 'Commit:  %s\n' "$commit"
printf 'Source:  %s\n' "$commit_source"
printf '\nRun Legion pin refresh next:\n'
printf '  cd %s && npm run generate:guix-base-channel\n' "$root_dir/../legion_kk"
