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

trim() {
  local s="${1:-}"
  s="${s#"${s%%[![:space:]]*}"}"
  s="${s%"${s##*[![:space:]]}"}"
  printf "%s" "$s"
}

to_lower() {
  printf "%s" "$1" | tr '[:upper:]' '[:lower:]'
}

bool_from_env() {
  local raw="${1:-}"
  local name="${2:-}"
  local default="${3:-false}"

  raw="$(trim "$raw")"
  if [[ -z "$raw" ]]; then
    [[ "$default" == "true" ]]
    return $?
  fi

  local lowered
  lowered="$(to_lower "$raw")"
  case "$lowered" in
    true) return 0 ;;
    false) return 1 ;;
    *)
      echo "gh-stub: warning: ${name} must be true|false (got: ${raw}); treating as false" >&2
      return 1
      ;;
  esac
}

if ! bool_from_env "${CODEX_GH_STUB_MODE_ENABLED:-}" "CODEX_GH_STUB_MODE_ENABLED" "false"; then
  echo "error: gh is blocked by script regression tests" >&2
  exit 90
fi

log_dir="${CODEX_STUB_LOG_DIR:-}"
if [[ -n "$log_dir" ]]; then
  mkdir -p "$log_dir"
  printf "%s\n" "gh $*" >>"${log_dir%/}/gh.calls.txt"
fi

pr_state_file_for() {
  local pr_number="${1:-}"
  if [[ -z "$log_dir" || -z "$pr_number" ]]; then
    return 1
  fi
  printf "%s" "${log_dir%/}/gh.pr.${pr_number}.state"
}

pr_post_merge_view_fail_once_marker_for() {
  local pr_number="${1:-}"
  if [[ -z "$log_dir" || -z "$pr_number" ]]; then
    return 1
  fi
  printf "%s" "${log_dir%/}/gh.pr.${pr_number}.fail-post-merge-pr-view-once.pending"
}

die_unhandled() {
  echo "error: gh stub unhandled args: $*" >&2
  exit 91
}

if [[ "${1-}" == "auth" && "${2-}" == "status" ]]; then
  exit 0
fi

render_pr_url() {
  local pr_number="$1"
  printf "%s" "${CODEX_GH_STUB_PR_URL:-https://github.com/example/repo/pull/${pr_number}}"
}

json_escape() {
  local s="${1:-}"
  s="${s//\\/\\\\}"
  s="${s//\"/\\\"}"
  s="${s//$'\n'/\\n}"
  s="${s//$'\r'/\\r}"
  s="${s//$'\t'/\\t}"
  printf "%s" "$s"
}

if [[ "${1-}" == "pr" && "${2-}" == "view" ]]; then
  shift 2

  pr_number=""
  if [[ $# -gt 0 && "${1-}" != -* ]]; then
    pr_number="$1"
    shift
  fi
  pr_number="${pr_number:-${CODEX_GH_STUB_PR_NUMBER:-123}}"

  if bool_from_env "${CODEX_GH_STUB_FAIL_FIRST_POST_MERGE_PR_VIEW:-}" "CODEX_GH_STUB_FAIL_FIRST_POST_MERGE_PR_VIEW" "false"; then
    state_file="$(pr_state_file_for "$pr_number" || true)"
    fail_marker="$(pr_post_merge_view_fail_once_marker_for "$pr_number" || true)"
    if [[ -n "$state_file" && -n "$fail_marker" && -f "$state_file" && -f "$fail_marker" ]]; then
      rm -f "$fail_marker"
      echo "gh-stub: simulated post-merge 'pr view' failure for PR ${pr_number}" >&2
      exit "${CODEX_GH_STUB_FAIL_POST_MERGE_PR_VIEW_EXIT_CODE:-1}"
    fi
  fi

  json=""
  while [[ $# -gt 0 ]]; do
    case "${1-}" in
      --json)
        json="${2:-}"
        shift 2
        ;;
      -q|--jq)
        shift 2
        ;;
      *)
        shift
        ;;
    esac
  done

  case "$json" in
    number)
      printf "%s\n" "${CODEX_GH_STUB_PR_NUMBER:-$pr_number}"
      exit 0
      ;;
    body)
      if [[ -n "${CODEX_GH_STUB_BODY_FILE:-}" ]]; then
        cat "${CODEX_GH_STUB_BODY_FILE}"
      else
        printf "%s" "${CODEX_GH_STUB_BODY:-}"
      fi
      exit 0
      ;;
    isDraft)
      printf "%s\n" "${CODEX_GH_STUB_IS_DRAFT:-false}"
      exit 0
      ;;
    url,baseRefName,headRefName,state)
      pr_url="$(render_pr_url "$pr_number")"
      base_ref="${CODEX_GH_STUB_BASE_REF:-main}"
      head_ref="${CODEX_GH_STUB_HEAD_REF:-feat/stub}"
      state="${CODEX_GH_STUB_STATE:-OPEN}"
      state_file="$(pr_state_file_for "$pr_number" || true)"
      if [[ -n "$state_file" && -f "$state_file" ]]; then
        state="$(cat "$state_file" 2>/dev/null || printf "%s" "$state")"
      fi
      printf "%s\t%s\t%s\t%s\n" "$pr_url" "$base_ref" "$head_ref" "$state"
      exit 0
      ;;
    url,baseRefName,headRefName,state,isDraft)
      pr_url="$(render_pr_url "$pr_number")"
      base_ref="${CODEX_GH_STUB_BASE_REF:-main}"
      head_ref="${CODEX_GH_STUB_HEAD_REF:-feat/stub}"
      state="${CODEX_GH_STUB_STATE:-OPEN}"
      state_file="$(pr_state_file_for "$pr_number" || true)"
      if [[ -n "$state_file" && -f "$state_file" ]]; then
        state="$(cat "$state_file" 2>/dev/null || printf "%s" "$state")"
      fi
      is_draft="${CODEX_GH_STUB_IS_DRAFT:-false}"
      printf "%s\t%s\t%s\t%s\t%s\n" "$pr_url" "$base_ref" "$head_ref" "$state" "$is_draft"
      exit 0
      ;;
    closingIssuesReferences)
      printf '{"closingIssuesReferences":%s}\n' "${CODEX_GH_STUB_CLOSING_ISSUES_JSON:-[]}"
      exit 0
      ;;
    url,title,baseRefName,headRefName,state)
      pr_url="$(render_pr_url "$pr_number")"
      title="${CODEX_GH_STUB_TITLE:-Fixture PR}"
      base_ref="${CODEX_GH_STUB_BASE_REF:-main}"
      head_ref="${CODEX_GH_STUB_HEAD_REF:-feat/stub}"
      state="${CODEX_GH_STUB_STATE:-OPEN}"
      printf "%s\t%s\t%s\t%s\t%s\n" "$pr_url" "$title" "$base_ref" "$head_ref" "$state"
      exit 0
      ;;
    url,title,baseRefName,headRefName,state,isDraft)
      pr_url="$(render_pr_url "$pr_number")"
      title="${CODEX_GH_STUB_TITLE:-Fixture PR}"
      base_ref="${CODEX_GH_STUB_BASE_REF:-main}"
      head_ref="${CODEX_GH_STUB_HEAD_REF:-feat/stub}"
      state="${CODEX_GH_STUB_STATE:-OPEN}"
      is_draft="${CODEX_GH_STUB_IS_DRAFT:-false}"
      printf "%s\t%s\t%s\t%s\t%s\t%s\n" "$pr_url" "$title" "$base_ref" "$head_ref" "$state" "$is_draft"
      exit 0
      ;;
    number,title,url,body,headRefName,baseRefName,labels,updatedAt)
      pr_url="$(render_pr_url "$pr_number")"
      title="${CODEX_GH_STUB_TITLE:-Fixture PR}"
      base_ref="${CODEX_GH_STUB_BASE_REF:-main}"
      head_ref="${CODEX_GH_STUB_HEAD_REF:-feat/stub}"
      updated_at="${CODEX_GH_STUB_UPDATED_AT:-2026-01-01T00:00:00Z}"
      body=""
      if [[ -n "${CODEX_GH_STUB_BODY_FILE:-}" ]]; then
        body="$(cat "${CODEX_GH_STUB_BODY_FILE}")"
      else
        body="${CODEX_GH_STUB_BODY:-}"
      fi
      labels_env="${CODEX_GH_STUB_LABELS:-}"
      labels_json="[]"
      if [[ -n "$labels_env" ]]; then
        IFS=',' read -r -a labels_arr <<<"$labels_env"
        labels_json="["
        for label in "${labels_arr[@]}"; do
          labels_json+="{\"name\":\"$(json_escape "$label")\"},"
        done
        labels_json="${labels_json%,}]"
      fi
      if [[ "$pr_number" =~ ^[0-9]+$ ]]; then
        number_json="$pr_number"
      else
        number_json="\"$(json_escape "$pr_number")\""
      fi
      printf '{'
      printf '"number":%s,' "$number_json"
      printf '"title":"%s",' "$(json_escape "$title")"
      printf '"url":"%s",' "$(json_escape "$pr_url")"
      printf '"body":"%s",' "$(json_escape "$body")"
      printf '"headRefName":"%s",' "$(json_escape "$head_ref")"
      printf '"baseRefName":"%s",' "$(json_escape "$base_ref")"
      printf '"labels":%s,' "$labels_json"
      printf '"updatedAt":"%s"' "$(json_escape "$updated_at")"
      printf '}\n'
      exit 0
      ;;
    *)
      exit 0
      ;;
  esac
fi

if [[ "${1-}" == "pr" && "${2-}" == "merge" ]]; then
  if [[ "${3-}" == "--help" || "${3-}" == "-h" ]]; then
    if bool_from_env "${CODEX_GH_STUB_MERGE_HELP_HAS_YES_ENABLED:-}" "CODEX_GH_STUB_MERGE_HELP_HAS_YES_ENABLED" "true"; then
      printf "%s\n" "      --yes"
    fi
    exit 0
  fi

  pr_number=""
  merge_args=( "$@" )
  if [[ "${#merge_args[@]}" -gt 2 ]]; then
    for arg in "${merge_args[@]:2}"; do
      if [[ "$arg" != -* ]]; then
        pr_number="$arg"
        break
      fi
    done
  fi
  pr_number="${pr_number:-${CODEX_GH_STUB_PR_NUMBER:-123}}"

  post_merge_state="${CODEX_GH_STUB_PR_MERGE_SET_STATE:-}"
  if [[ -n "$post_merge_state" ]]; then
    state_file="$(pr_state_file_for "$pr_number" || true)"
    if [[ -n "$state_file" ]]; then
      printf "%s\n" "$post_merge_state" >"$state_file"
    fi
    if bool_from_env "${CODEX_GH_STUB_FAIL_FIRST_POST_MERGE_PR_VIEW:-}" "CODEX_GH_STUB_FAIL_FIRST_POST_MERGE_PR_VIEW" "false"; then
      fail_marker="$(pr_post_merge_view_fail_once_marker_for "$pr_number" || true)"
      if [[ -n "$fail_marker" ]]; then
        : >"$fail_marker"
      fi
    fi
  fi

  merge_exit_code="${CODEX_GH_STUB_PR_MERGE_EXIT_CODE:-0}"
  if [[ ! "$merge_exit_code" =~ ^[0-9]+$ ]]; then
    merge_exit_code="1"
  fi
  if [[ "$merge_exit_code" -ne 0 ]]; then
    echo "gh-stub: simulated 'pr merge' non-zero exit ${merge_exit_code} for PR ${pr_number}" >&2
  fi
  exit "$merge_exit_code"
fi

if [[ "${1-}" == "issue" && "${2-}" == "view" ]]; then
  shift 2

  issue_number=""
  if [[ $# -gt 0 && "${1-}" != -* ]]; then
    issue_number="$1"
    shift
  fi
  issue_number="${issue_number:-${CODEX_GH_STUB_ISSUE_NUMBER:-273}}"

  if [[ -n "${CODEX_GH_STUB_ISSUE_JSON_FILE:-}" ]]; then
    cat "$CODEX_GH_STUB_ISSUE_JSON_FILE"
    exit 0
  fi
  if [[ -n "${CODEX_GH_STUB_ISSUE_JSON:-}" ]]; then
    printf '%s\n' "$CODEX_GH_STUB_ISSUE_JSON"
    exit 0
  fi

  if [[ "$issue_number" =~ ^[0-9]+$ ]]; then
    issue_number_json="$issue_number"
    issue_url="https://github.com/example/repo/issues/${issue_number}"
  else
    issue_number_json="null"
    issue_url="$issue_number"
  fi
  printf '{"number":%s,"title":"Fixture issue","state":"OPEN","url":"%s","body":"","comments":[]}\n' "$issue_number_json" "$(json_escape "$issue_url")"
  exit 0
fi

if [[ "${1-}" == "pr" && "${2-}" == "edit" ]]; then
  pr_number="${3:-${CODEX_GH_STUB_PR_NUMBER:-123}}"
  shift 3 || true

  body_file=""
  while [[ $# -gt 0 ]]; do
    case "${1-}" in
      --body-file)
        body_file="${2:-}"
        shift 2
        ;;
      *)
        shift
        ;;
    esac
  done

  if [[ -n "$log_dir" && -n "$body_file" && -f "$body_file" ]]; then
    cp "$body_file" "${log_dir%/}/gh.pr.${pr_number}.body.md" 2>/dev/null || true
  fi
  exit 0
fi

if [[ "${1-}" == "pr" && "${2-}" == "checks" ]]; then
  shift 2
  json_fields=""
  required_only="false"
  while [[ $# -gt 0 ]]; do
    case "${1-}" in
      --required)
        required_only="true"
        shift
        ;;
      --json)
        json_fields="${2:-}"
        shift 2
        ;;
      *)
        shift
        ;;
    esac
  done

  checks_mode="${CODEX_GH_STUB_PR_CHECKS_MODE:-pass}"
  if [[ -n "$json_fields" ]]; then
    case "$checks_mode" in
      none|missing|no_checks)
        printf '[]\n'
        ;;
      fail|failed)
        printf '[{"name":"ci","state":"FAILURE","bucket":"fail"}]\n'
        ;;
      pending)
        printf '[{"name":"ci","state":"PENDING","bucket":"pending"}]\n'
        ;;
      custom)
        if [[ "$required_only" == "true" && "${CODEX_GH_STUB_PR_REQUIRED_CHECKS_MISSING:-0}" == "1" ]]; then
          echo "no required checks reported on the 'feature' branch" >&2
          exit 1
        fi
        if [[ "$required_only" == "true" && -n "${CODEX_GH_STUB_PR_REQUIRED_CHECKS_JSON:-}" ]]; then
          printf '%s\n' "$CODEX_GH_STUB_PR_REQUIRED_CHECKS_JSON"
        else
          printf '%s\n' "${CODEX_GH_STUB_PR_CHECKS_JSON:-[]}"
        fi
        ;;
      *)
        printf '[{"name":"ci","state":"SUCCESS","bucket":"pass"}]\n'
        ;;
    esac
    exit 0
  fi

  case "$checks_mode" in
    none|missing|no_checks)
      echo "no checks reported on the 'main' branch" >&2
      exit 1
      ;;
    fail|failed)
      echo "ci fail" >&2
      exit 1
      ;;
    pending)
      echo "ci pending" >&2
      exit 8
      ;;
  esac
  exit 0
fi

if [[ "${1-}" == "pr" && "${2-}" == "ready" ]]; then
  exit 0
fi

die_unhandled "$@"
