#!/usr/bin/env bash
set -euo pipefail

ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || {
  echo "error: TiDB migration history guard must run inside a Git worktree" >&2
  exit 1
}
cd "${ROOT}"

readonly migration_dir="internal/tidb/migrations"
readonly repair_manifest="guardrails/storage/tidb-migration-repairs.tsv"
readonly target_ref_input="${AGENTTEAM_TIDB_MIGRATION_BASE_REF:-origin/main}"
tmp_dir="$(mktemp -d)"
trap 'rm -rf "${tmp_dir}"' EXIT

fail() {
  printf 'error: %s\n' "$1" >&2
  exit 1
}

format_version() {
  printf '%05d' "$((10#$1))"
}

migration_version() {
  local path="$1"
  local filename="${path##*/}"

  if [[ "${filename}" =~ ^([0-9]{5})_.+\.sql$ ]]; then
    printf '%s\n' "${BASH_REMATCH[1]}"
    return 0
  fi
  return 1
}

approved_repair() {
  local path="$1"
  local target_blob candidate_blob

  [[ -f "${repair_manifest}" ]] || return 1
  target_blob="$(git rev-parse "${target_commit}:${path}" 2>/dev/null)" || return 1
  candidate_blob="$(git rev-parse "HEAD:${path}" 2>/dev/null)" || return 1
  awk -F $'\t' \
    -v path="${path}" \
    -v target_blob="${target_blob}" \
    -v candidate_blob="${candidate_blob}" \
    'NF >= 4 && $1 == path && $2 == target_blob && $3 == candidate_blob { found = 1 } END { exit !found }' \
    "${repair_manifest}"
}

target_commit="$(git rev-parse --verify "${target_ref_input}^{commit}" 2>/dev/null)" ||
  fail "cannot resolve TiDB migration target ref '${target_ref_input}' to a commit"
merge_base="$(git merge-base "${target_commit}" HEAD 2>/dev/null)" ||
  fail "cannot compute merge-base between TiDB migration target '${target_ref_input}' (${target_commit}) and HEAD"

printf 'TiDB migration target ref input: %s\n' "${target_ref_input}"
printf 'TiDB migration target resolved commit: %s\n' "${target_commit}"
printf 'TiDB migration merge-base: %s\n' "${merge_base}"

untracked_paths="$(
  git ls-files --others --exclude-standard -- "${migration_dir}"
)"
if [[ -n "${untracked_paths}" ]]; then
  printf 'error: untracked migration path is not validated against committed HEAD:\n' >&2
  printf '%s\n' "${untracked_paths}" | sed 's/^/  /' >&2
  exit 1
fi

staged_paths="$(
  git diff --cached --name-only -- "${migration_dir}"
)"
if [[ -n "${staged_paths}" ]]; then
  printf 'error: staged migration changes are not validated against committed HEAD:\n' >&2
  printf '%s\n' "${staged_paths}" | sed 's/^/  /' >&2
  exit 1
fi

unstaged_paths="$(
  git diff --name-only -- "${migration_dir}"
)"
if [[ -n "${unstaged_paths}" ]]; then
  printf 'error: unstaged migration changes are not validated against committed HEAD:\n' >&2
  printf '%s\n' "${unstaged_paths}" | sed 's/^/  /' >&2
  exit 1
fi

target_max=0
target_tree_file="${tmp_dir}/target-tree"
if ! git ls-tree -r --name-only "${target_commit}" -- "${migration_dir}" >"${target_tree_file}"; then
  fail "cannot read TiDB migration tree from target commit ${target_commit}"
fi
while IFS= read -r path; do
  [[ -n "${path}" ]] || continue
  version="$(migration_version "${path}")" ||
    fail "target contains an invalid migration filename: ${path}"
  if (( 10#${version} > target_max )); then
    target_max=$((10#${version}))
  fi
done <"${target_tree_file}"
printf 'TiDB migration target maximum: %05d\n' "${target_max}"

candidate_versions_file="${tmp_dir}/candidate-versions"
candidate_diff_file="${tmp_dir}/candidate-diff"
: >"${candidate_versions_file}"
if ! git diff --name-status --no-renames \
  "${merge_base}..HEAD" -- "${migration_dir}" >"${candidate_diff_file}"; then
  fail "cannot read candidate TiDB migration changes from ${merge_base}..HEAD"
fi
while IFS=$'\t' read -r status path extra; do
  [[ -n "${status}" ]] || continue

  case "${status}" in
    A)
      version="$(migration_version "${path}")" ||
        fail "added file in ${migration_dir} is not a valid migration: ${path}"
      if (( 10#${version} <= target_max )); then
        fail "candidate migration version $(format_version "${version}") must be greater than target maximum $(format_version "${target_max}"); next available migration version is $(format_version "$((target_max + 1))")"
      fi
      if grep -q "^${version}"$'\t' "${candidate_versions_file}"; then
        previous_path="$(grep "^${version}"$'\t' "${candidate_versions_file}" | head -1 | cut -f2-)"
        fail "candidate migration version $(format_version "${version}") is not unique: ${previous_path} and ${path}"
      fi
      printf '%s\t%s\n' "${version}" "${path}" >>"${candidate_versions_file}"
      ;;
    M)
      if approved_repair "${path}"; then
        printf 'Approved TiDB migration repair: %s\n' "${path}"
      else
        fail "forbidden migration change status ${status}: ${path}"
      fi
      ;;
    D|T|U)
      fail "forbidden migration change status ${status}: ${path}"
      ;;
    *)
      fail "unknown migration change status ${status}: ${path}${extra:+ ${extra}}"
      ;;
  esac
done <"${candidate_diff_file}"

printf 'TiDB migration history guard passed.\n'
