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

usage() {
  cat <<'USAGE'
Usage:
  agentrail internal review-pr --pr NUMBER [--base main] [--engine codex] [--output FILE] [--machine-readable]

Internal AgentRail helper for running a pull request review in a fresh agent context window.
Use agentrail prompt review for public prompt generation and agentrail afk for automated review loops.

Options:
  --pr NUMBER       Pull request number to review. Required.
  --base BRANCH     Base branch to review against. Default: PR base branch.
  --engine ENGINE   Review engine. Supported: codex. Default: codex.
  --output FILE     Write the review agent's final message to FILE.
  --machine-readable
                    Include the GitHub PR reviewer JSON contract for AFK follow-up issue creation.

Environment:
  REVIEW_CODEX_ARGS Extra arguments passed to `codex exec review`.

Examples:
  agentrail internal review-pr --pr 15
USAGE
}

die() {
  echo "agentrail review: $*" >&2
  exit 1
}

need() {
  command -v "$1" >/dev/null 2>&1 || die "missing required command: $1"
}

repo_root() {
  git rev-parse --show-toplevel
}

ensure_clean_tree() {
  local status
  status="$(git status --porcelain)"
  [[ -z "$status" ]] || die "working tree is dirty; commit, stash, or clean changes before reviewing"
}

extract_machine_readable_review_json() {
  local review_file="$1"

  awk '
    /BEGIN_REVIEW_FIX_ISSUES_JSON/ { in_json = 1; next }
    /END_REVIEW_FIX_ISSUES_JSON/ { in_json = 0; exit }
    in_json { print }
  ' "$review_file"
}

validate_machine_readable_review_output() {
  local review_file="$1"
  local json

  [[ -s "$review_file" ]] || die "machine-readable review output is missing or empty: ${review_file}"

  json="$(extract_machine_readable_review_json "$review_file")"
  [[ -n "$json" ]] || die "machine-readable review output missing BEGIN_REVIEW_FIX_ISSUES_JSON block: ${review_file}"

  if ! jq -e '
    type == "object"
    and (.fix_issues | type == "array")
    and (.memory_suggestions | type == "array")
  ' >/dev/null <<< "$json"; then
    die "machine-readable review output must include fix_issues and memory_suggestions arrays: ${review_file}"
  fi
}

review_prompt() {
  local pr="$1"
  local title="$2"
  local url="$3"
  local machine_readable="$4"
  local prompt_file="docs/agents/pr-review.md"
  local machine_prompt_file="docs/agents/github-pr-reviewer.md"
  local memory_command="scripts/memory"

  if [[ ! -f "$prompt_file" && -f "templates/docs/agents/pr-review.md" ]]; then
    prompt_file="templates/docs/agents/pr-review.md"
  fi

  if [[ ! -f "$machine_prompt_file" && -f "templates/docs/agents/github-pr-reviewer.md" ]]; then
    machine_prompt_file="templates/docs/agents/github-pr-reviewer.md"
  fi

  if [[ ! -x "$memory_command" && -x "templates/scripts/memory" ]]; then
    memory_command="templates/scripts/memory"
  fi

  [[ -f "$prompt_file" ]] || die "missing PR review instructions: docs/agents/pr-review.md or templates/docs/agents/pr-review.md"
  if [[ "$machine_readable" == "1" ]]; then
    [[ -f "$machine_prompt_file" ]] || die "missing GitHub PR reviewer contract: docs/agents/github-pr-reviewer.md or templates/docs/agents/github-pr-reviewer.md"
  fi

  cat <<PROMPT
Review exactly one pull request: #${pr}.

Pull request title: ${title}
Pull request URL: ${url}

Use the review instructions below:

$(cat "$prompt_file")
PROMPT

  if [[ "$machine_readable" == "1" ]]; then
    cat <<PROMPT

Use the machine-readable GitHub PR reviewer contract below. You must include the marked JSON block with both \`fix_issues\` and \`memory_suggestions\` arrays in the final output, even when both arrays are empty:

Machine-readable contract source: ${machine_prompt_file}

$(cat "$machine_prompt_file")
PROMPT
  fi

  cat <<PROMPT

Repo-specific instructions:

- Start from a clean checkout of the pull request head branch.
- Compare against the pull request base branch.
- Read the PR body, linked issue, milestone, PRD, architecture baseline, and agent docs.
- Run ${memory_command} recall for the PR title, linked issue, and key terms when that command is available.
- Treat project memory as advisory; verify it against current code, docs, issue, PRD, and ADRs.
- Do not edit files.
- Do not commit, push, close, or merge anything.
- Return findings first, ordered by severity.
PROMPT
}

sanitized_agent_exec() {
  env \
    -u CLAUDECODE -u CLAUDE_CODE_SESSION_ID -u CLAUDE_CODE_ENTRYPOINT \
    -u CLAUDE_AGENT_SDK_VERSION -u CLAUDE_CODE_EXECPATH -u CLAUDE_EFFORT \
    -u AI_AGENT \
    -u CODEX_SESSION -u CODEX_SANDBOX \
    -u CURSOR_SESSION -u CURSOR_AGENT \
    "$@"
}

run_codex_review() {
  local base="$1"
  local pr="$2"
  local title="$3"
  local url="$4"
  local output_file="$5"
  local machine_readable="$6"
  local prompt
  prompt="$(review_prompt "$pr" "$title" "$url" "$machine_readable")"

  local extra_args=()
  if [[ -n "${REVIEW_CODEX_ARGS:-}" ]]; then
    # shellcheck disable=SC2206
    extra_args=(${REVIEW_CODEX_ARGS})
  fi

  local output_args=()
  if [[ -n "$output_file" ]]; then
    mkdir -p "$(dirname "$output_file")"
    output_args=(-o "$output_file")
  fi

  if [[ ${#extra_args[@]} -gt 0 ]]; then
    printf '%s\n' "$prompt" | sanitized_agent_exec codex exec review --base "$base" "${output_args[@]}" "${extra_args[@]}"
  else
    printf '%s\n' "$prompt" | sanitized_agent_exec codex exec review --base "$base" "${output_args[@]}"
  fi
}

run_claude_review() {
  local base="$1"
  local pr="$2"
  local title="$3"
  local url="$4"
  local output_file="$5"
  local machine_readable="$6"
  local prompt
  prompt="$(review_prompt "$pr" "$title" "$url" "$machine_readable")"

  if [[ -n "$output_file" ]]; then
    mkdir -p "$(dirname "$output_file")"
    printf '%s\n' "$prompt" | sanitized_agent_exec bash -lc "claude -p --allowedTools Bash,Read" 2>&1 | tee "$output_file"
  else
    printf '%s\n' "$prompt" | sanitized_agent_exec bash -lc "claude -p --allowedTools Bash,Read" 2>&1
  fi
}

main() {
  local pr=""
  local base=""
  local engine="codex"
  local output_file=""
  local machine_readable="0"

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --pr)
        pr="${2:-}"
        [[ -n "$pr" ]] || die "--pr requires a number"
        shift 2
        ;;
      --base)
        base="${2:-}"
        [[ -n "$base" ]] || die "--base requires a branch"
        shift 2
        ;;
      --engine)
        engine="${2:-}"
        [[ -n "$engine" ]] || die "--engine requires a value"
        shift 2
        ;;
      --output)
        output_file="${2:-}"
        [[ -n "$output_file" ]] || die "--output requires a file path"
        shift 2
        ;;
      --machine-readable)
        machine_readable="1"
        shift
        ;;
      -h|--help)
        usage
        exit 0
        ;;
      *)
        die "unknown option: $1"
        ;;
    esac
  done

  [[ -n "$pr" ]] || { usage; exit 1; }
  [[ "$engine" == "codex" || "$engine" == "claude" ]] || die "unsupported review engine: $engine"
  if [[ "$machine_readable" == "1" && -z "$output_file" ]]; then
    die "--machine-readable requires --output so AgentRail can validate the review contract"
  fi

  need git
  need gh
  need jq
  if [[ "$engine" == "codex" ]]; then
    need codex
  else
    need claude
  fi

  local root
  root="$(repo_root)"
  cd "$root"

  ensure_clean_tree

  local pr_json title url head_ref base_ref
  pr_json="$(gh pr view "$pr" --json number,title,url,headRefName,baseRefName,state)"
  title="$(jq -r '.title' <<< "$pr_json")"
  url="$(jq -r '.url' <<< "$pr_json")"
  head_ref="$(jq -r '.headRefName' <<< "$pr_json")"
  base_ref="$(jq -r '.baseRefName' <<< "$pr_json")"
  base="${base:-$base_ref}"

  git fetch origin "$base"
  git fetch origin "$head_ref"
  git switch "$head_ref"
  git pull --ff-only origin "$head_ref"
  ensure_clean_tree

  echo "==> Review PR #${pr}: ${title}"
  echo "    ${url}"
  echo "    base: ${base}"
  echo "    head: ${head_ref}"

  if [[ "$engine" == "claude" ]]; then
    run_claude_review "$base" "$pr" "$title" "$url" "$output_file" "$machine_readable"
  else
    run_codex_review "$base" "$pr" "$title" "$url" "$output_file" "$machine_readable"
  fi
  if [[ "$machine_readable" == "1" ]]; then
    validate_machine_readable_review_output "$output_file"
  fi
}

main "$@"
