#!/usr/bin/env bash
# tidbcloud-exec.sh - Execute TiDB Cloud API requests without exposing credentials.
#
# Usage:
#   scripts/tidbcloud-exec.sh http '<json_request>' [--sut tidbx|tidbx-premium]
#   scripts/tidbcloud-exec.sh poll '<json_request>' [--sut tidbx|tidbx-premium]
#
# HTTP request JSON format:
#   {"method":"GET","path":"/clusters"}
#   {"method":"POST","path":"/clusters","body":{...}}
#
# Poll request JSON format (extends HTTP):
#   {"method":"GET","path":"/clusters/<id>","expect":"body.state == ACTIVE","max_retries":60,"delay":10}
#
# Output: JSON with {success, status_code, body, error, duration_ms}

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=tidbcloud-auth.sh
source "$SCRIPT_DIR/tidbcloud-auth.sh"

_json_error() {
  printf '{"success":false,"error":"%s"}\n' "$1"
  exit 1
}

_expand_env_in_string() {
  local str="$1"
  while [[ "$str" =~ \$\{([A-Z0-9_]+)(:-([^}]*))?\} ]]; do
    local var_name="${BASH_REMATCH[1]}"
    local default_val="${BASH_REMATCH[3]:-}"
    local actual_val="${!var_name:-$default_val}"
    str="${str//${BASH_REMATCH[0]}/$actual_val}"
  done
  echo "$str"
}

_execute_http() {
  local request_json="$1"

  request_json="$(_expand_env_in_string "$request_json")"

  local method path body headers_json query_json
  method="$(echo "$request_json" | jq -r '.method // "GET"' | tr '[:lower:]' '[:upper:]')"
  path="$(echo "$request_json" | jq -r '.path // ""')"
  body="$(echo "$request_json" | jq -c '.body // empty')"
  headers_json="$(echo "$request_json" | jq -c '.headers // {}')"
  query_json="$(echo "$request_json" | jq -c '.query_params // {}')"

  if [[ -z "$path" ]]; then
    _json_error "Missing 'path' in request"
  fi

  local url="${TIDBCLOUD_API_BASE_URL}${path}"

  local -a curl_args=(-s -w '\n%{http_code}\n%{time_total}' -X "$method")

  if [[ -n "$body" ]]; then
    curl_args+=(-H 'Content-Type: application/json' -d "$body")
  fi

  # Add custom headers
  while IFS='=' read -r key val; do
    [[ -n "$key" ]] && curl_args+=(-H "$key: $val")
  done < <(echo "$headers_json" | jq -r 'to_entries[] | "\(.key)=\(.value)"' 2>/dev/null || true)

  # Add query parameters
  while IFS='=' read -r key val; do
    [[ -n "$key" ]] && curl_args+=(--data-urlencode "$key=$val")
  done < <(echo "$query_json" | jq -r 'to_entries[] | "\(.key)=\(.value)"' 2>/dev/null || true)
  if [[ "$(echo "$query_json" | jq 'length')" -gt 0 ]]; then
    curl_args+=(-G)
  fi

  local raw_output
  raw_output="$(tidbcloud_curl "${curl_args[@]}" "$url" 2>&1)" || true

  local response_body http_code time_total
  time_total="$(echo "$raw_output" | tail -1)"
  http_code="$(echo "$raw_output" | tail -2 | head -1)"
  response_body="$(echo "$raw_output" | sed '$d' | sed '$d')"

  local duration_ms
  duration_ms="$(echo "$time_total" | awk '{printf "%d", $1 * 1000}')"

  local parsed_body
  parsed_body="$(echo "$response_body" | jq '.' 2>/dev/null)" || parsed_body="{\"raw\":$(echo "$response_body" | head -c 1000 | jq -Rs .)}"

  local success="true"
  local error="null"
  if [[ "$http_code" -lt 200 || "$http_code" -ge 300 ]] 2>/dev/null; then
    success="false"
    error="\"HTTP ${http_code}\""
  fi

  jq -n \
    --argjson success "$success" \
    --arg status_code "$http_code" \
    --argjson body "$parsed_body" \
    --argjson error "$error" \
    --argjson duration_ms "$duration_ms" \
    '{success: $success, status_code: ($status_code | tonumber), body: $body, error: $error, duration_ms: $duration_ms}'
}

# Minimal CEL-like evaluator: supports "body.foo == BAR" or "body.foo == 'BAR'"
_evaluate_expect() {
  local body="$1" expect="$2"

  [[ -z "$expect" ]] && return 0

  if [[ ! "$expect" =~ == ]]; then
    return 1
  fi

  local left right
  left="$(echo "$expect" | sed 's/==.*//' | xargs)"
  right="$(echo "$expect" | sed 's/.*==//' | xargs)"
  right="${right//\'/}"
  right="${right//\"/}"

  if [[ ! "$left" =~ ^body\. ]]; then
    return 1
  fi

  local jq_path=".${left#body.}"
  local actual
  actual="$(echo "$body" | jq -r "$jq_path" 2>/dev/null)" || return 1

  [[ "$actual" == "$right" ]]
}

_execute_poll() {
  local request_json="$1"

  request_json="$(_expand_env_in_string "$request_json")"

  local expect max_retries delay
  expect="$(echo "$request_json" | jq -r '.expect // ""')"
  max_retries="$(echo "$request_json" | jq -r '.max_retries // 60')"
  delay="$(echo "$request_json" | jq -r '.delay // 10')"

  local http_request
  http_request="$(echo "$request_json" | jq -c 'del(.expect, .max_retries, .delay)')"

  local last_result=""
  local attempt=0
  while [[ $attempt -lt $max_retries ]]; do
    last_result="$(_execute_http "$http_request")"

    local success
    success="$(echo "$last_result" | jq -r '.success')"

    if [[ "$success" == "true" ]]; then
      local body
      body="$(echo "$last_result" | jq -c '.body')"
      if _evaluate_expect "$body" "$expect"; then
        echo "$last_result"
        return 0
      fi
    fi

    sleep "$delay"
    attempt=$((attempt + 1))
  done

  if [[ -n "$last_result" ]]; then
    echo "$last_result" | jq '.success = false | .error = "Poll timeout: condition not met"'
  else
    printf '{"success":false,"status_code":null,"body":{},"error":"No attempts executed","duration_ms":0}\n'
  fi
  return 1
}

main() {
  if [[ $# -lt 2 ]]; then
    _json_error "Usage: tidbcloud-exec.sh <http|poll> '<json_request>' [--sut <sut_name>]"
  fi

  local command="$1"
  local request_json="$2"
  shift 2

  local sut_args=()
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --sut) sut_args=(--sut "$2"); shift 2 ;;
      *) shift ;;
    esac
  done

  load_tidbcloud_auth "${sut_args[@]+"${sut_args[@]}"}"

  case "$command" in
    http)
      _execute_http "$request_json"
      ;;
    poll)
      _execute_poll "$request_json"
      ;;
    *)
      _json_error "Unknown command: $command. Use http or poll."
      ;;
  esac
}

main "$@"
