#!/usr/bin/env bash

release_error() {
  printf 'Error: %s\n' "$*" >&2
  return 1
}

require_clean_release_source() {
  local source_dir="$1"
  local status

  if ! git -C "$source_dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    release_error "release source is not a Git worktree: $source_dir"
    return 1
  fi
  if ! status="$(git -C "$source_dir" status --porcelain --untracked-files=all)"; then
    release_error "cannot inspect release source: $source_dir"
    return 1
  fi
  if [[ -n "$status" ]]; then
    release_error "release source is dirty: $source_dir"
    return 1
  fi
}

prepare_clean_release_checkout() {
  local source_dir="$1"
  local expected_commit="$2"
  local destination="$3"
  local actual_commit

  if ! git clone --quiet --no-local --no-checkout "$source_dir" "$destination"; then
    release_error "cannot clone release source: $source_dir"
    return 1
  fi
  if ! git -C "$destination" checkout --quiet --detach "$expected_commit"; then
    release_error "cannot check out release commit: $expected_commit"
    return 1
  fi
  actual_commit="$(git -C "$destination" rev-parse HEAD)" || return 1
  if [[ "$actual_commit" != "$expected_commit" ]]; then
    release_error "clean checkout commit mismatch: got $actual_commit, want $expected_commit"
    return 1
  fi
  require_clean_release_source "$destination"
}

go_build_value() {
  local binary="$1"
  local key="$2"

  go version -m "$binary" |
    awk -v key="$key" '$1 == "build" && index($2, key "=") == 1 { sub("^" key "=", "", $2); print $2; exit }'
}

verify_go_binary_provenance() {
  local binary="$1"
  local expected_commit="$2"
  local expected_goos="$3"
  local expected_goarch="$4"
  local vcs revision modified goos goarch

  vcs="$(go_build_value "$binary" vcs)"
  revision="$(go_build_value "$binary" vcs.revision)"
  modified="$(go_build_value "$binary" vcs.modified)"
  goos="$(go_build_value "$binary" GOOS)"
  goarch="$(go_build_value "$binary" GOARCH)"

  if [[ "$vcs" != "git" ]]; then
    release_error "missing Git VCS metadata: $binary"
    return 1
  fi
  if [[ "$revision" != "$expected_commit" ]]; then
    release_error "VCS revision mismatch: got $revision, want $expected_commit: $binary"
    return 1
  fi
  if [[ "$modified" != "false" ]]; then
    release_error "binary reports vcs.modified=$modified: $binary"
    return 1
  fi
  if [[ "$goos" != "$expected_goos" ]]; then
    release_error "GOOS mismatch: got $goos, want $expected_goos: $binary"
    return 1
  fi
  if [[ "$goarch" != "$expected_goarch" ]]; then
    release_error "GOARCH mismatch: got $goarch, want $expected_goarch: $binary"
    return 1
  fi
}

verify_daemon_self_check() {
  local binary="$1"
  local expected_version="$2"
  local expected_commit="$3"
  local expected_goos="$4"
  local expected_goarch="$5"
  local identity

  if ! identity="$("$binary" --self-check --expected-version "$expected_version")"; then
    release_error "daemon self-check failed: $binary"
    return 1
  fi

  if ! printf '%s' "$identity" | node -e '
    const fs = require("node:fs");
    const actual = JSON.parse(fs.readFileSync(0, "utf8"));
    const expected = {
      package: "autowonder",
      version: process.argv[1],
      commit: process.argv[2],
      goos: process.argv[3],
      goarch: process.argv[4],
    };
    for (const [key, value] of Object.entries(expected)) {
      if (actual[key] !== value) {
        throw new Error(`${key} mismatch: got ${actual[key]}, want ${value}`);
      }
    }
  ' "$expected_version" "$expected_commit" "$expected_goos" "$expected_goarch"; then
    release_error "daemon self-check identity mismatch: $binary"
    return 1
  fi
}
