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

# prepare-proxy.sh
#
# Prepare a clean LOCAL no-auth proxy entrypoint for Codex.
#
# Why this exists:
#   Some Codex HTTP clients (models refresh, MCP / ps/mcp) issue a bare
#   HTTP CONNECT to the configured proxy WITHOUT forwarding proxy credentials.
#   If the upstream proxy requires authentication it answers 407 and resets
#   the stream, surfacing as `stream disconnected before completion` /
#   `Transport channel closed`. The main response path uses SOCKS5 with creds
#   and works, which is why only those two auxiliary features break.
#
# Fix (no Codex change, purely at the proxy layer):
#   Run a LOCAL no-auth proxy listener. Codex sends its bare CONNECT to this
#   local listener (no auth required -> accepted). The local listener then
#   forwards upstream WITH the credentials taken from SEELE_UPSTREAM_PROXY.
#
#   codex --bare CONNECT (no creds)--> 127.0.0.1:<port> (no auth)
#         --with creds--> SEELE_UPSTREAM_PROXY (socks5 exit) --> OpenAI
#
# IMPORTANT config model:
#   The Seele server injects SEELE_PROXY into the Codex environment
#   (ALL_PROXY/HTTP_PROXY/...). So Codex uses SEELE_PROXY directly. Therefore:
#     SEELE_PROXY           = the LOCAL no-auth entrypoint (http://127.0.0.1:<port>)
#     SEELE_UPSTREAM_PROXY  = the real credentialed upstream (socks5 exit)
#   Codex talks to the local no-auth entrypoint; only this script holds the
#   upstream credentials and forwards to the real exit.
#
# After running this, the Codex runtime (via SEELE_PROXY) and any other script
# can simply use:
#   http://127.0.0.1:<port>
# as their proxy, without ever handling credentials themselves.
#
# This replaces all the network-layer workarounds explored earlier
# (WireGuard tunnel, MTU tweaks, custom routes): none of them are needed,
# the upstream SOCKS5 exit is directly reachable.

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

ENV_FILE=""
ACTION="start"

usage() {
  cat <<'EOF'
Usage: ./scripts/deploy/prepare-proxy.sh [--env PATH] [--status|--stop]

Prepares a local no-auth proxy entrypoint that forwards to the credentialed
upstream (SEELE_UPSTREAM_PROXY), so Codex (via SEELE_PROXY) and other scripts
can use http://127.0.0.1:<port> without handling credentials.

Options:
  --env PATH   Load configuration from an env file (e.g. ./seele.env).
  --status     Show whether the local proxy is running and exit.
  --stop       Stop the local proxy and exit.
  -h, --help   Show this help.

Relevant variables (from --env file or environment):
  SEELE_UPSTREAM_PROXY     Real upstream proxy WITH credentials (required).
                           Format: scheme://user:pass@host:port
                           e.g. socks5h://user:pass@47.77.237.69:28888
  SEELE_LOCAL_PROXY_PORT   Local listen port for the no-auth entrypoint.
                           Default: 55860
  SEELE_LOCAL_PROXY_ADDR   Local proxy URL other components should use.
                           Default: http://127.0.0.1:<SEELE_LOCAL_PROXY_PORT>
                           Set SEELE_PROXY to this value so Codex uses it.
  SEELE_GOST_BIN           Path to the gost binary. Default: looked up on
                           PATH, then $SEELE_ROOT/gost, then ./gost.
  SEELE_PROXY_PID_FILE     PID file. Default: $SEELE_ROOT/.runtime/prepare-proxy.pid
                           (falls back to /tmp/seele-prepare-proxy.pid).
  SEELE_PROXY_LOG_FILE     Log file. Default: $SEELE_ROOT/.runtime/prepare-proxy.log
                           (falls back to /tmp/seele-prepare-proxy.log).
EOF
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --env)
      ENV_FILE="${2:-}"
      shift 2
      ;;
    --status)
      ACTION="status"
      shift
      ;;
    --stop)
      ACTION="stop"
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Unknown option: $1" >&2
      usage >&2
      exit 1
      ;;
  esac
done

if [ -n "$ENV_FILE" ]; then
  if [ ! -f "$ENV_FILE" ]; then
    echo "Environment file not found: $ENV_FILE" >&2
    exit 1
  fi
  set -a
  # shellcheck disable=SC1090
  . "$ENV_FILE"
  set +a
fi

LOCAL_PORT="${SEELE_LOCAL_PROXY_PORT:-55860}"
LOCAL_ADDR="${SEELE_LOCAL_PROXY_ADDR:-http://127.0.0.1:${LOCAL_PORT}}"

# Choose a writable runtime location for pid/log.
RUNTIME_BASE=""
if [ -n "${SEELE_ROOT:-}" ] && [ -d "${SEELE_ROOT}/.runtime" ]; then
  RUNTIME_BASE="${SEELE_ROOT}/.runtime"
elif [ -n "${SEELE_RUNTIME_DIR:-}" ] && [ -d "${SEELE_RUNTIME_DIR}" ]; then
  RUNTIME_BASE="${SEELE_RUNTIME_DIR}"
fi
if [ -n "$RUNTIME_BASE" ]; then
  PID_FILE="${SEELE_PROXY_PID_FILE:-${RUNTIME_BASE}/prepare-proxy.pid}"
  LOG_FILE="${SEELE_PROXY_LOG_FILE:-${RUNTIME_BASE}/prepare-proxy.log}"
else
  PID_FILE="${SEELE_PROXY_PID_FILE:-/tmp/seele-prepare-proxy.pid}"
  LOG_FILE="${SEELE_PROXY_LOG_FILE:-/tmp/seele-prepare-proxy.log}"
fi

is_running() {
  # Returns 0 if a local proxy we manage is still alive.
  if [ -f "$PID_FILE" ]; then
    local pid
    pid="$(cat "$PID_FILE" 2>/dev/null || true)"
    if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
      return 0
    fi
  fi
  return 1
}

port_listening() {
  if command -v ss >/dev/null 2>&1; then
    ss -tlnH "( sport = :${LOCAL_PORT} )" 2>/dev/null | grep -q ":${LOCAL_PORT}"
  else
    return 1
  fi
}

stop_proxy() {
  if is_running; then
    local pid
    pid="$(cat "$PID_FILE")"
    kill "$pid" 2>/dev/null || true
    sleep 1
    kill -9 "$pid" 2>/dev/null || true
  fi
  rm -f "$PID_FILE"
}

case "$ACTION" in
  status)
    if is_running; then
      echo "local proxy: RUNNING (pid $(cat "$PID_FILE"), $LOCAL_ADDR)"
      exit 0
    fi
    if port_listening; then
      echo "local proxy: port ${LOCAL_PORT} is listening but not managed by this script"
      exit 0
    fi
    echo "local proxy: NOT running"
    exit 1
    ;;
  stop)
    stop_proxy
    echo "local proxy: stopped"
    exit 0
    ;;
esac

# --- start ---

if [ -z "${SEELE_UPSTREAM_PROXY:-}" ]; then
  echo "SEELE_UPSTREAM_PROXY is required (real upstream proxy WITH credentials)." >&2
  echo "Set it in seele.env, e.g.:" >&2
  echo "  SEELE_UPSTREAM_PROXY=socks5h://user:password@47.77.237.69:28888" >&2
  echo "And set SEELE_PROXY to the local entrypoint so Codex uses it:" >&2
  echo "  SEELE_PROXY=${LOCAL_ADDR}" >&2
  exit 1
fi

# Guard against a misconfiguration where the upstream points back at the
# local entrypoint (would create a forwarding loop).
case "$SEELE_UPSTREAM_PROXY" in
  *127.0.0.1:${LOCAL_PORT}*|*localhost:${LOCAL_PORT}*)
    echo "SEELE_UPSTREAM_PROXY must point at the real upstream exit, not the" >&2
    echo "local entrypoint (${LOCAL_ADDR}). Fix seele.env." >&2
    exit 1
    ;;
esac

# Normalize the upstream URL into a gost -F forward URL.
# gost forwarder does not understand the "h" suffix (socks5h); it always
# resolves the destination at the proxy side, so socks5h -> socks5.
UPSTREAM="$SEELE_UPSTREAM_PROXY"
case "$UPSTREAM" in
  socks5h://*) UPSTREAM="socks5://${UPSTREAM#socks5h://}" ;;
esac

# Locate gost binary.
GOST_BIN="${SEELE_GOST_BIN:-}"
if [ -z "$GOST_BIN" ]; then
  if command -v gost >/dev/null 2>&1; then
    GOST_BIN="$(command -v gost)"
  elif [ -n "${SEELE_ROOT:-}" ] && [ -x "${SEELE_ROOT}/gost" ]; then
    GOST_BIN="${SEELE_ROOT}/gost"
  elif [ -x "${ROOT_DIR}/gost" ]; then
    GOST_BIN="${ROOT_DIR}/gost"
  fi
fi
if [ -z "$GOST_BIN" ] || [ ! -x "$GOST_BIN" ]; then
  echo "gost binary not found." >&2
  echo "Install gost and either put it on PATH, set SEELE_GOST_BIN," >&2
  echo "or place it at \$SEELE_ROOT/gost." >&2
  exit 1
fi

# Idempotent: if already running, restart cleanly to pick up config changes.
if is_running; then
  echo "local proxy already running (pid $(cat "$PID_FILE")), restarting..."
  stop_proxy
fi

if port_listening; then
  echo "Port ${LOCAL_PORT} is already in use by another process." >&2
  echo "Set SEELE_LOCAL_PROXY_PORT to a free port, or stop the other process." >&2
  exit 1
fi

# Start the local no-auth HTTP+SOCKS entrypoint (-L=http://:PORT has no creds)
# forwarding to the credentialed upstream (-F).
nohup "$GOST_BIN" -L="http://:${LOCAL_PORT}" -F="$UPSTREAM" \
  > "$LOG_FILE" 2>&1 &
echo $! > "$PID_FILE"

# Wait for the listener to come up.
ok=0
for _ in 1 2 3 4 5 6 7 8 9 10; do
  if port_listening; then ok=1; break; fi
  sleep 0.3
done

if [ "$ok" != "1" ]; then
  echo "local proxy failed to start; see $LOG_FILE" >&2
  tail -n 20 "$LOG_FILE" >&2 || true
  exit 1
fi

echo "local proxy ready."
echo "  listen  : ${LOCAL_ADDR}  (no auth)"
echo "  upstream: ${UPSTREAM%%@*}@***  (creds from SEELE_UPSTREAM_PROXY)"
echo "  pid     : $(cat "$PID_FILE")"
echo "  log     : ${LOG_FILE}"
echo
echo "Make sure seele.env has SEELE_PROXY pointing at this entrypoint:"
echo "  SEELE_PROXY=${LOCAL_ADDR}"
echo
echo "Other scripts can use this proxy directly:"
echo "  export HTTP_PROXY=${LOCAL_ADDR} HTTPS_PROXY=${LOCAL_ADDR} ALL_PROXY=${LOCAL_ADDR}"