#!/usr/bin/env bash
# 校验：改动 src/ 或 e2e/ 时必须绑定活跃 OpenSpec change（见 AGENTS.md）
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"

WATCH_PATHS=('src/' 'e2e/')
CHANGE_DIR_PREFIX="openspec/changes"
ACTIVE_CHANGE_FILES=('.opsx-active-change' '.poquan-active-change')

log() { printf '[opsx:gate] %s\n' "$*"; }
die() { log "ERROR: $*"; exit 1; }

path_is_watched() {
  local path="$1"
  local prefix
  for prefix in "${WATCH_PATHS[@]}"; do
    if [[ "$path" == "$prefix"* ]] || [[ "$path" == */"$prefix"* ]]; then
      return 0
    fi
  done
  return 1
}

collect_candidate_files() {
  CANDIDATE_FILES=()
  if [[ "${1:-}" == "--ci" ]]; then
    local base="${GITHUB_BASE_REF:-main}"
    base="${base#origin/}"
    git fetch origin "$base" --depth=1 2>/dev/null || true
    local merge_base
    merge_base="$(git merge-base "origin/${base}" HEAD 2>/dev/null || git merge-base "${base}" HEAD 2>/dev/null || echo "")"
    [[ -n "$merge_base" ]] || merge_base="HEAD~1"
    while IFS= read -r line; do
      [[ -n "$line" ]] && CANDIDATE_FILES+=("$line")
    done < <(git diff --name-only "$merge_base"...HEAD 2>/dev/null || git diff --name-only HEAD~1 HEAD)
  else
    while IFS= read -r line; do
      [[ -n "$line" ]] && CANDIDATE_FILES+=("$line")
    done < <(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null || true)
    if [[ ${#CANDIDATE_FILES[@]} -eq 0 ]]; then
      while IFS= read -r line; do
        [[ -n "$line" ]] && CANDIDATE_FILES+=("$line")
      done < <(git diff --name-only --diff-filter=ACMR HEAD 2>/dev/null || true)
    fi
  fi
}

has_watched_changes() {
  local file
  for file in "${CANDIDATE_FILES[@]}"; do
    if path_is_watched "$file"; then
      return 0
    fi
  done
  return 1
}

resolve_change_name() {
  if [[ -n "${OPSX_ACTIVE_CHANGE:-}" ]]; then
    echo "$OPSX_ACTIVE_CHANGE"
    return 0
  fi
  if [[ -n "${POQUAN_ACTIVE_CHANGE:-}" ]]; then
    echo "$POQUAN_ACTIVE_CHANGE"
    return 0
  fi
  local file name
  for file in "${ACTIVE_CHANGE_FILES[@]}"; do
    if [[ -f "$file" ]]; then
      name="$(tr -d '[:space:]' <"$file")"
      [[ -n "$name" ]] && { echo "$name"; return 0; }
    fi
  done
  local branch
  branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")"
  if [[ "$branch" =~ ^change/([^/]+) ]]; then
    echo "${BASH_REMATCH[1]}"
    return 0
  fi
  return 1
}

validate_change_dir() {
  local name="$1"
  local dir="${CHANGE_DIR_PREFIX}/${name}"
  [[ -d "$dir" ]] || die "change 目录不存在: ${dir}（请先 Propose 或设置 OPSX_ACTIVE_CHANGE）"
  [[ "$dir" != *"/archive/"* ]] || die "change 已归档，请新建 change: ${name}"
  if [[ -f "${dir}/tasks.md" ]] && grep -q '^- \[ \]' "${dir}/tasks.md" 2>/dev/null; then
    log "WARN: tasks.md 仍有未完成项；合码前建议全部勾选并跑 delivery-pipeline"
  fi
}

main() {
  if [[ "${OPSX_SKIP_OPSX:-}" == "1" ]] || [[ "${POQUAN_SKIP_OPSX:-}" == "1" ]]; then
    log "已跳过（OPSX_SKIP_OPSX 或 POQUAN_SKIP_OPSX=1）"
    exit 0
  fi

  collect_candidate_files "${1:-}"
  if ! has_watched_changes; then
    log "无 src/ 或 e2e/ 改动，跳过"
    exit 0
  fi

  local change_name
  change_name="$(resolve_change_name)" || die "改动 src/e2e 但未绑定 OpenSpec change。请先 Propose，或设置 OPSX_ACTIVE_CHANGE / .opsx-active-change / 分支 change/<name>。详见 AGENTS.md"

  validate_change_dir "$change_name"
  log "OK — 活跃 change: ${change_name}"
}

main "$@"
