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

# Auto-provision this user's Codex provider + API key. Cross-platform and
# dependency-free: uses only tools that ship with macOS and Git Bash
# (awk, sed, curl) -- NO python / jq / node install required.
#
#   - macOS    (Terminal: bash or zsh)
#   - Windows  (Git Bash / MSYS)
#   - Linux    (best effort)
#
# Adapted to the local setup described by
#   D:\project\codex\config.toml  and  D:\project\codex\auth.json
#
#   provider : VC        (base_url https://td.geeknow.top/v1, wire_api responses)
#   model    : gpt-5.5
#   auth     : OPENAI_API_KEY in ~/.codex/auth.json  +  requires_openai_auth = true
#
# The API key is detected automatically (OPENAI_API_KEY env -> existing
# ~/.codex/auth.json -> interactive prompt) and merged into ~/.codex/auth.json.
# Other auth.json fields (e.g. ChatGPT OAuth tokens) are preserved.

DEFAULT_BASE_URL="https://td.geeknow.top/v1"
DEFAULT_MODEL="gpt-5.5"
PROVIDER_NAME="VC"
MARKER_START="# >>> codex-vc-provider >>>"
MARKER_END="# <<< codex-vc-provider <<<"

# --- platform detection (cosmetic; all logic below is platform-neutral) ------
detect_os() {
  case "$(uname -s 2>/dev/null || echo unknown)" in
    Darwin*)              echo "macos" ;;
    MINGW*|MSYS*|CYGWIN*) echo "windows" ;;
    Linux*)               echo "linux" ;;
    *)                    echo "unknown" ;;
  esac
}
OS="$(detect_os)"

print_usage() {
  cat <<'USAGE'
Auto-configure the VC model provider and API key for Codex Desktop / Codex CLI.
Runs on macOS (Terminal) and Windows (Git Bash). No python/jq/node needed.

The API key is read automatically (OPENAI_API_KEY env -> existing
~/.codex/auth.json -> interactive prompt) and merged into ~/.codex/auth.json.
Existing auth.json fields (e.g. ChatGPT OAuth tokens) are preserved.

Usage:
  bash codex-vc-setup.sh

Options:
  --restore      Restore the first pre-install ~/.codex/config.toml backup.
  --smoke-only   Only test the key against the provider; do not modify config.
  --no-smoke     Install without running the API smoke test.
  --with-chatgpt (Windows) Also download & run the ChatGPT desktop app installer.
  --chatgpt-only Only install the ChatGPT desktop app; do not touch Codex config.
  --help         Show this help.

Optional environment variables:
  OPENAI_API_KEY     API key to use (falls back to ~/.codex/auth.json).
  VC_BASE_URL        Defaults to https://td.geeknow.top/v1
  VC_MODEL           Defaults to gpt-5.5
  CODEX_HOME         Defaults to ~/.codex
USAGE
}

require_command() {
  local command_name="$1"
  if ! command -v "$command_name" >/dev/null 2>&1; then
    echo "Missing required command: $command_name" >&2
    exit 1
  fi
}

escape_double_quoted_value() {
  printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
}

# Print the existing OPENAI_API_KEY from auth.json (empty string if none).
# Pure sed -- handles pretty-printed or minified JSON.
read_auth_key() {
  local auth_file="$1"
  if [[ -f "$auth_file" ]]; then
    sed -n 's/.*"OPENAI_API_KEY"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$auth_file" | head -n 1
  fi
}

# Merge the key into auth.json, preserving any other fields (tokens, etc.).
# Pure awk, operating on the whole file as one string so single-line and
# multi-line JSON are both handled. Creates the file if missing/empty.
write_auth_key() {
  local auth_file="$1" key="$2"
  mkdir -p "$(dirname "$auth_file")"
  local tmp="${auth_file}.tmp"
  awk -v file="$auth_file" -v key="$key" '
    BEGIN {
      s = ""
      while ((getline l < file) > 0) s = s l "\n"
      close(file)
      if (match(s, /"OPENAI_API_KEY"[[:space:]]*:[[:space:]]*"[^"]*"/)) {
        # key already present -> replace its value in place
        s = substr(s, 1, RSTART-1) "\"OPENAI_API_KEY\": \"" key "\"" substr(s, RSTART+RLENGTH)
      } else {
        p = index(s, "{")
        if (p == 0) {
          # no JSON object at all -> write a fresh one
          s = "{\n  \"OPENAI_API_KEY\": \"" key "\"\n}\n"
        } else {
          rest = substr(s, p+1)
          t = rest
          sub(/^[[:space:]]+/, "", t)
          if (substr(t, 1, 1) == "}") {
            # empty object {} -> insert without a trailing comma
            s = substr(s, 1, p) "\n  \"OPENAI_API_KEY\": \"" key "\"\n" substr(s, p+1)
          } else {
            # non-empty object -> insert as first field with a comma
            s = substr(s, 1, p) "\n  \"OPENAI_API_KEY\": \"" key "\"," substr(s, p+1)
          }
        }
      }
      printf "%s", s
    }
  ' > "$tmp" && mv "$tmp" "$auth_file"
  chmod 600 "$auth_file"
}

read_api_key() {
  local auth_file="$1"
  # Priority: explicit OPENAI_API_KEY env override -> existing auth.json -> prompt.
  # QUOTAFLOW_API_KEY is intentionally NOT consulted (different service).
  local api_key="${OPENAI_API_KEY:-}"
  if [[ -z "$api_key" ]]; then
    api_key="$(read_auth_key "$auth_file")"
  fi
  if [[ -n "$api_key" ]]; then
    printf '%s' "$api_key"
    return 0
  fi
  if [[ -t 0 ]]; then
    printf 'Enter your API key (input hidden): ' >&2
    stty -echo 2>/dev/null || true
    read -r api_key
    stty echo 2>/dev/null || true
    printf '\n' >&2
    printf '%s' "$api_key"
    return 0
  fi
  echo "No API key found. Set OPENAI_API_KEY, add it to $auth_file, or run interactively." >&2
  exit 1
}

smoke_test_key() {
  local api_key="$1" base_url="$2" model="$3"
  local tmp_dir curl_auth_config response_body status
  tmp_dir="$(mktemp -d)"
  trap "rm -rf '$tmp_dir'" RETURN
  curl_auth_config="$tmp_dir/curl-auth.conf"
  response_body="$tmp_dir/response.json"
  umask 077
  printf 'header = "Authorization: Bearer %s"\n' "$api_key" > "$curl_auth_config"
  status="$(curl -sS -m 180 -o "$response_body" -w '%{http_code}' \
    -q --config "$curl_auth_config" \
    -H 'Content-Type: application/json' \
    -H 'User-Agent: Codex Desktop/vc-install-smoke' \
    --data-binary @- "$base_url/responses" <<JSON
{"model":"$model","input":"Reply with only: connected","max_output_tokens":32,"stream":false}
JSON
)"
  if [[ "$status" != "200" ]]; then
    echo "Provider /responses smoke failed: HTTP $status" >&2
    head -c 1000 "$response_body" >&2 || true
    echo >&2
    echo "No Codex config was changed." >&2
    return 1
  fi
  rm -rf "$tmp_dir"
  trap - RETURN
  echo "Provider API smoke passed for base URL: $base_url"
}

# Download the ChatGPT desktop app (Microsoft Store) installer to /tmp and run it.
# Windows-only. The URL serves Microsoft's Authenticode-signed StoreInstaller.exe.
install_chatgpt_app() {
  if [[ "$OS" != "windows" ]]; then
    echo "ChatGPT desktop installer is Windows-only; skipping on $OS." >&2
    return 0
  fi
  local url="https://get.microsoft.com/installer/download/9PLM9XGG6VKS?cid=website_cta_psi"
  local exe="/tmp/ChatGPT-Installer.exe"
  echo "Downloading ChatGPT desktop installer..."
  curl -fsSL -m 600 -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" -o "$exe" "$url"
  if [[ "$(head -c 2 "$exe" 2>/dev/null)" != "MZ" ]]; then
    echo "Downloaded file is not a Windows executable; aborting install." >&2
    return 1
  fi
  echo "Downloaded Microsoft Store installer to: $exe"
  # Install: winget is the reliable silent path; the downloaded exe is the fallback.
  if command -v winget >/dev/null 2>&1; then
    echo "Installing via winget (silent)..."
    winget install --id 9PLM9XGG6VKS --source msstore --silent --accept-package-agreements --accept-source-agreements
  else
    echo "winget not found; launching the downloaded installer (a window may appear)..."
    "$exe" || echo "Installer exited non-zero (it may still have installed)." >&2
  fi
  echo "ChatGPT install step finished."
}

write_restore_script() {
  local restore_file="$1" codex_home="$2"
  cat > "$restore_file" <<RESTORE
#!/usr/bin/env bash
set -euo pipefail
codex_home="$(escape_double_quoted_value "$codex_home")"
backup_dir="\$codex_home/backups"
original_backup="\$backup_dir/config.toml.pre-vc-original.bak"
if [[ ! -f "\$original_backup" ]]; then
  echo "No pre-install backup found in \$backup_dir" >&2
  exit 1
fi
cp "\$original_backup" "\$codex_home/config.toml"
chmod 600 "\$codex_home/config.toml"
echo "Restored Codex config from: \$original_backup"
echo "Note: ~/.codex/auth.json was left untouched."
echo "Restart Codex Desktop after restore."
RESTORE
  chmod 700 "$restore_file"
}

restore_config() {
  local codex_home="$1"
  local backup_dir="$codex_home/backups"
  local original_backup="$backup_dir/config.toml.pre-vc-original.bak"
  if [[ ! -f "$original_backup" ]]; then
    echo "No pre-install backup found in $backup_dir" >&2
    exit 1
  fi
  cp "$original_backup" "$codex_home/config.toml"
  chmod 600 "$codex_home/config.toml"
  echo "Restored Codex config from: $original_backup"
  echo "Note: ~/.codex/auth.json was left untouched."
  echo "Restart Codex Desktop after restore."
}

# Rewrite config.toml: remove any managed marker block + any pre-existing
# [model_providers.VC] table, upsert top-level model/model_provider, then append
# one fresh provider block. Pure awk (line-oriented; TOML tables are line-based).
update_codex_config() {
  local config_file="$1" provider_name="$2" base_url="$3" model="$4"
  local marker_start="$5" marker_end="$6"
  local tmp="${config_file}.tmp"
  awk -v provider="$provider_name" -v base_url="$base_url" -v model="$model" \
      -v mstart="$marker_start" -v mend="$marker_end" '
    {
      line = $0
      # 1) skip a previously-managed marker block
      if (index(line, mstart) > 0) { inmarker = 1; next }
      if (inmarker) { if (index(line, mend) > 0) inmarker = 0; next }
      # 2) skip a pre-existing [model_providers.<name>] table (and sub-tables)
      s = line; gsub(/^[[:space:]]+/, "", s)
      if (substr(s, 1, 1) == "[") {
        body = substr(s, 2)
        prefix = "model_providers." provider
        if (substr(body, 1, length(prefix)) == prefix) {
          c = substr(body, length(prefix)+1, 1)
          if (c == "]" || c == ".") { skip = 1; next }
        }
        skip = 0
      }
      if (skip) next
      lines[++n] = line
    }
    END {
      # trim trailing blank/whitespace-only lines
      while (n > 0 && lines[n] ~ /^[[:space:]]*$/) n--
      # locate first table header
      first = n + 1
      for (i = 1; i <= n; i++) {
        t = lines[i]; gsub(/^[[:space:]]+/, "", t)
        if (substr(t, 1, 1) == "[") { first = i; break }
      }
      # upsert top-level model / model_provider in the header region
      hm = 0; hp = 0
      for (i = 1; i < first; i++) {
        if (lines[i] ~ /^[[:space:]]*model[[:space:]]*=/) {
          if (!hm) { lines[i] = "model = \"" model "\""; hm = 1 } else del[i] = 1
        } else if (lines[i] ~ /^[[:space:]]*model_provider[[:space:]]*=/) {
          if (!hp) { lines[i] = "model_provider = \"" provider "\""; hp = 1 } else del[i] = 1
        }
      }
      if (!hm) printf "model = \"%s\"\n", model
      if (!hp) printf "model_provider = \"%s\"\n", provider
      for (i = 1; i <= n; i++) { if (del[i]) continue; printf "%s\n", lines[i] }
      # append one fresh provider block
      printf "\n"
      printf "%s\n", mstart
      printf "# VC provider (auto-configured). API key is read from ~/.codex/auth.json.\n"
      printf "[model_providers.%s]\n", provider
      printf "base_url = \"%s\"\n", base_url
      printf "name = \"%s\"\n", provider
      printf "requires_openai_auth = true\n"
      printf "wire_api = \"responses\"\n"
      printf "%s\n", mend
    }
  ' "$config_file" > "$tmp" && mv "$tmp" "$config_file"
  chmod 600 "$config_file"
}

run_install() {
  local codex_home="$1" api_key="$2" base_url="$3" model="$4" run_smoke="$5"
  local config_file="$codex_home/config.toml"
  local auth_file="$codex_home/auth.json"
  local backup_dir="$codex_home/backups"
  local bin_dir="$codex_home/bin"
  local restore_file="$bin_dir/vc-provider-restore.sh"
  local original_backup="$backup_dir/config.toml.pre-vc-original.bak"
  local timestamp
  if [[ "$run_smoke" == "true" ]]; then
    echo "Testing API key before changing Codex config..."
    smoke_test_key "$api_key" "$base_url" "$model"
  fi
  timestamp="$(date +%Y%m%d%H%M%S)"
  mkdir -p "$codex_home" "$backup_dir" "$bin_dir"
  touch "$config_file"
  chmod 600 "$config_file"
  if [[ ! -f "$original_backup" ]]; then
    cp "$config_file" "$original_backup"
    chmod 600 "$original_backup"
  fi
  cp "$config_file" "$backup_dir/config.toml.pre-vc-$timestamp.bak"
  chmod 600 "$backup_dir/config.toml.pre-vc-$timestamp.bak"
  write_auth_key "$auth_file" "$api_key"
  write_restore_script "$restore_file" "$codex_home"
  update_codex_config "$config_file" "$PROVIDER_NAME" "$base_url" "$model" "$MARKER_START" "$MARKER_END"
  cat <<SUMMARY
VC provider installed and API key configured.  (detected OS: $OS)

Files written:
  $config_file   (model_provider = "VC", model = "$model")
  $auth_file     (OPENAI_API_KEY set; other fields preserved)

Backup:
  $original_backup

Restart Codex Desktop, then test with:
  Reply with only: connected

Manual rollback:
  bash $restore_file
SUMMARY
}

main() {
  local restore=false smoke_only=false run_smoke=true with_chatgpt=false chatgpt_only=false
  for arg in "$@"; do
    case "$arg" in
      --help|-h) print_usage; exit 0 ;;
      --restore) restore=true ;;
      --smoke-only) smoke_only=true ;;
      --no-smoke) run_smoke=false ;;
      --with-chatgpt) with_chatgpt=true ;;
      --chatgpt-only) chatgpt_only=true ;;
      *) echo "Unknown option: $arg" >&2; print_usage >&2; exit 1 ;;
    esac
  done
  require_command curl
  require_command sed
  require_command awk
  if [[ "$chatgpt_only" == "true" ]]; then
    install_chatgpt_app
    exit 0
  fi
  local codex_home="${CODEX_HOME:-$HOME/.codex}"
  local auth_file="$codex_home/auth.json"
  local base_url="${VC_BASE_URL:-${OPENAI_BASE_URL:-$DEFAULT_BASE_URL}}"
  local model="${VC_MODEL:-$DEFAULT_MODEL}"
  if [[ "$restore" == "true" ]]; then
    restore_config "$codex_home"
    exit 0
  fi
  local api_key
  api_key="$(read_api_key "$auth_file")"
  if [[ "$smoke_only" == "true" ]]; then
    smoke_test_key "$api_key" "$base_url" "$model"
    exit 0
  fi
  run_install "$codex_home" "$api_key" "$base_url" "$model" "$run_smoke"
  if [[ "$with_chatgpt" == "true" ]]; then
    install_chatgpt_app
  fi
}

main "$@"
