#!/usr/bin/env bash
set -u

usage() {
  cat <<'USAGE'
Usage: run_performance.sh [--quick|--full] [--no-cross]
                          [--baseline <record.md>] [--fail-on-regression]
                          [--throughput-drop-pct N] [--p99-worse-pct N]

Runs the Proto Socket performance validation standard separately from the
compatibility matrix. The wrapper executes run_stress.sh components, merges the
measured rows into one local record, and compares them with a prior performance
record when a baseline is supplied.

Defaults:
  --quick
  throughput regression warning threshold: 20%
  p99 latency regression warning threshold: 25%

Recommended:
  Quick performance signal:
    bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --quick

  Full local baseline candidate:
    bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --full

  Regression comparison:
    bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --quick --baseline agent-test/runs/<baseline>.md
USAGE
}

mode="quick"
run_cross=1
baseline_file=""
fail_on_regression=0
throughput_drop_pct="20"
p99_worse_pct="25"

while [ "$#" -gt 0 ]; do
  case "$1" in
    --quick) mode="quick" ;;
    --full) mode="full" ;;
    --no-cross) run_cross=0 ;;
    --baseline=*) baseline_file="${1#--baseline=}" ;;
    --baseline) shift; baseline_file="${1:-}" ;;
    --fail-on-regression) fail_on_regression=1 ;;
    --throughput-drop-pct=*) throughput_drop_pct="${1#--throughput-drop-pct=}" ;;
    --throughput-drop-pct) shift; throughput_drop_pct="${1:-}" ;;
    --p99-worse-pct=*) p99_worse_pct="${1#--p99-worse-pct=}" ;;
    --p99-worse-pct) shift; p99_worse_pct="${1:-}" ;;
    -h|--help) usage; exit 0 ;;
    *) usage >&2; exit 2 ;;
  esac
  shift
done

case "$mode" in
  quick|full) ;;
  *) usage >&2; exit 2 ;;
esac

case "$throughput_drop_pct" in
  ''|*[!0-9.]*)
    printf 'invalid --throughput-drop-pct: %s\n' "$throughput_drop_pct" >&2
    exit 2
    ;;
esac

case "$p99_worse_pct" in
  ''|*[!0-9.]*)
    printf 'invalid --p99-worse-pct: %s\n' "$p99_worse_pct" >&2
    exit 2
    ;;
esac

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "$script_dir/../../../../.." && pwd)"
stress_script="$script_dir/run_stress.sh"
run_started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
run_record_stamp="$(date -u +%Y%m%d-%H%M%S)"
record_profile="proto-socket-performance-${mode}"
record_dir="$repo_root/agent-test/runs"
record_file="$record_dir/${run_record_stamp}-${record_profile}.md"
log_dir="$(mktemp -d "${TMPDIR:-/tmp}/proto-socket-performance.XXXXXX")"
git_ref="$(git -C "$repo_root" rev-parse --short HEAD 2>/dev/null || echo "unknown")"

mkdir -p "$record_dir"

if [ -n "$baseline_file" ]; then
  case "$baseline_file" in
    /*) ;;
    *) baseline_file="$repo_root/$baseline_file" ;;
  esac
  if [ ! -f "$baseline_file" ]; then
    printf 'baseline file not found: %s\n' "$baseline_file" >&2
    exit 2
  fi
fi

run_cmd="bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --$mode"
[ "$run_cross" -eq 0 ] && run_cmd="$run_cmd --no-cross"
[ -n "$baseline_file" ] && run_cmd="$run_cmd --baseline ${baseline_file#$repo_root/}"
[ "$fail_on_regression" -eq 1 ] && run_cmd="$run_cmd --fail-on-regression"
run_cmd="$run_cmd --throughput-drop-pct $throughput_drop_pct --p99-worse-pct $p99_worse_pct"

component_names=()
component_cmds=()
component_logs=()
component_records=()
component_results=()
component_exits=()

add_component() {
  component_names+=("$1")
  component_cmds+=("$2")
}

same_profiles="roundtrip,burst,sustained,parallel,payload"
all_langs="dart,go,kotlin,python,typescript"
all_transports="tcp,ws"

add_component "same-language" "bash $(printf '%q' "$stress_script") --$mode --lang $all_langs --transport $all_transports --profile $same_profiles"
if [ "$run_cross" -eq 1 ]; then
  add_component "cross-language" "bash $(printf '%q' "$stress_script") --$mode --lang $all_langs --transport $all_transports --profile cross"
fi
add_component "typescript-gateway" "bash $(printf '%q' "$stress_script") --$mode --lang typescript --transport $all_transports --profile gateway"

component_result_from_exit() {
  local exit_code="$1" parsed="$2"
  if [ -n "$parsed" ]; then
    printf '%s' "$parsed"
    return
  fi
  case "$exit_code" in
    0) printf 'PASS' ;;
    3) printf 'BLOCKED' ;;
    4) printf 'INCOMPLETE' ;;
    *) printf 'FAIL' ;;
  esac
}

run_component() {
  local index="$1"
  local name="${component_names[$index]}"
  local cmd="${component_cmds[$index]}"
  local log_file="$log_dir/${name}.log"
  local exit_code parsed_result record_path

  printf 'RUN performance %s: %s\n' "$name" "$cmd" >&2
  (
    cd "$repo_root" &&
      bash -c "$cmd"
  ) >"$log_file" 2>&1
  exit_code=$?

  tail -n 20 "$log_file" >&2 || true

  parsed_result="$(sed -n 's/^- 전체 결과값: //p; s/^전체 결과값: //p' "$log_file" | tail -n 1)"
  record_path="$(sed -n 's/^결과 기록 파일: `\(.*\)`/\1/p' "$log_file" | tail -n 1)"
  if [ -n "$record_path" ]; then
    case "$record_path" in
      /*) ;;
      *) record_path="$repo_root/$record_path" ;;
    esac
  fi

  component_logs[$index]="$log_file"
  component_records[$index]="$record_path"
  component_results[$index]="$(component_result_from_exit "$exit_code" "$parsed_result")"
  component_exits[$index]="$exit_code"
}

extract_measurement_rows() {
  local file="$1"
  [ -f "$file" ] || return 0
  awk '
    /^## 측정 결과/ { in_rows=1; next }
    in_rows && /^## / { in_rows=0 }
    in_rows && /^\| / {
      if ($0 ~ /^\|---/) next
      if ($0 ~ /^\| Profile /) next
      print
    }
  ' "$file"
}

build_component_summary() {
  local i
  printf '| 구성 | 결과 | exit code | 결과 기록 | 로그 |\n'
  printf '|---|---|---:|---|---|\n'
  for i in "${!component_names[@]}"; do
    local record="${component_records[$i]:-}"
    local log="${component_logs[$i]:-}"
    [ -n "$record" ] && record="${record#$repo_root/}"
    [ -n "$log" ] && log="$log"
    printf '| %s | %s | %s | %s | %s |\n' \
      "${component_names[$i]}" "${component_results[$i]:--}" "${component_exits[$i]:--}" \
      "${record:-미기록}" "$log"
  done
}

trim_awk='
function trim(s) {
  gsub(/^[[:space:]]+|[[:space:]]+$/, "", s)
  return s
}
'

compare_rows() {
  local baseline_rows="$1" current_rows="$2" out_file="$3" status_file="$4"
  awk -F'|' \
    -v throughput_limit="$throughput_drop_pct" \
    -v p99_limit="$p99_worse_pct" \
    -v status_file="$status_file" \
    "$trim_awk"'
    function row_key() {
      return profile "|" axis "|" language "|" transport "|" payload "|" clients "|" gateway
    }
    function parse_row() {
      profile=trim($2)
      axis=trim($3)
      language=trim($4)
      transport=trim($5)
      payload=trim($6)
      clients=trim($7)
      request_count=trim($8)
      throughput=trim($9) + 0
      p99=trim($12) + 0
      gateway=trim($20)
      result=trim($22)
    }
    BEGIN {
      warning_count=0
      compared_count=0
      missing_count=0
      printf "| Key | Baseline throughput | Current throughput | Drop %% | Baseline p99 | Current p99 | Worse %% | 결과 |\n"
      printf "|---|---:|---:|---:|---:|---:|---:|---|\n"
    }
    FNR == NR {
      parse_row()
      key=row_key()
      base_throughput[key]=throughput
      base_p99[key]=p99
      next
    }
    {
      parse_row()
      key=row_key()
      label=sprintf("%s / %s / %s / %s / %sB / clients=%s / requests=%s / gateway=%s", profile, axis, language, transport, payload, clients, request_count, gateway)
      if (!(key in base_throughput)) {
        missing_count++
        printf "| %s | - | %.3f | - | - | %.3f | - | SKIPPED(no baseline) |\n", label, throughput, p99
        next
      }

      compared_count++
      drop=0
      worse=0
      row_status="PASS"
      if (base_throughput[key] > 0) {
        drop=((base_throughput[key] - throughput) / base_throughput[key]) * 100
      }
      if (base_p99[key] > 0) {
        worse=((p99 - base_p99[key]) / base_p99[key]) * 100
      }
      if (drop >= throughput_limit || worse >= p99_limit) {
        row_status="WARN"
        warning_count++
      }
      printf "| %s | %.3f | %.3f | %.1f | %.3f | %.3f | %.1f | %s |\n", \
        label, base_throughput[key], throughput, drop, base_p99[key], p99, worse, row_status
    }
    END {
      if (warning_count > 0) {
        print "WARN" > status_file
      } else {
        print "PASS" > status_file
      }
      printf "%d\n", compared_count > status_file ".compared"
      printf "%d\n", missing_count > status_file ".missing"
      printf "%d\n", warning_count > status_file ".warnings"
    }
  ' "$baseline_rows" "$current_rows" >"$out_file"
}

for i in "${!component_names[@]}"; do
  run_component "$i"
done

overall_result="PASS"
for result in "${component_results[@]}"; do
  case "$result" in
    FAIL) overall_result="FAIL" ;;
  esac
done
if [ "$overall_result" = "PASS" ]; then
  for result in "${component_results[@]}"; do
    case "$result" in
      INCOMPLETE) overall_result="INCOMPLETE" ;;
    esac
  done
fi
if [ "$overall_result" = "PASS" ]; then
  for result in "${component_results[@]}"; do
    case "$result" in
      BLOCKED) overall_result="BLOCKED" ;;
    esac
  done
fi

current_rows_file="$log_dir/current.rows"
: >"$current_rows_file"
for record in "${component_records[@]}"; do
  extract_measurement_rows "$record" >>"$current_rows_file"
done

regression_result="SKIPPED"
comparison_file="$log_dir/regression-comparison.md"
compared_count=0
missing_count=0
warning_count=0
if [ -n "$baseline_file" ]; then
  baseline_rows_file="$log_dir/baseline.rows"
  extract_measurement_rows "$baseline_file" >"$baseline_rows_file"
  status_file="$log_dir/regression.status"
  if compare_rows "$baseline_rows_file" "$current_rows_file" "$comparison_file" "$status_file"; then
    regression_result="$(cat "$status_file" 2>/dev/null || echo "PASS")"
    compared_count="$(cat "$status_file.compared" 2>/dev/null || echo "0")"
    missing_count="$(cat "$status_file.missing" 2>/dev/null || echo "0")"
    warning_count="$(cat "$status_file.warnings" 2>/dev/null || echo "0")"
    if [ "$overall_result" = "PASS" ] && [ "$regression_result" = "WARN" ]; then
      overall_result="WARN"
    fi
  else
    regression_result="FAIL"
    overall_result="FAIL"
    printf '| 오류 | baseline 비교 파서 실패 | - | - | - | - | - | FAIL |\n' >"$comparison_file"
  fi
fi

{
  printf '%s\n' '---'
  printf 'test_env: local\n'
  printf 'record_type: performance-result\n'
  printf 'test_profile: %s\n' "$record_profile"
  printf 'created_at: %s\n' "$run_started_at"
  printf 'overall_result: %s\n' "$overall_result"
  printf 'regression_result: %s\n' "$regression_result"
  printf '%s\n\n' '---'
  printf '# %s local 결과 기록\n\n' "$record_profile"
  printf '## 실행 정보\n\n'
  printf -- '- 실행 일시: %s\n' "$run_started_at"
  printf -- '- git ref: %s\n' "$git_ref"
  printf -- '- 실행 명령: `%s`\n' "$run_cmd"
  printf -- '- mode: %s\n' "$mode"
  printf -- '- 로그 디렉터리: `%s`\n' "$log_dir"
  if [ -n "$baseline_file" ]; then
    printf -- '- baseline: `%s`\n' "${baseline_file#$repo_root/}"
  else
    printf -- '- baseline: 없음\n'
  fi
  printf -- '- throughput regression threshold: %s%% drop\n' "$throughput_drop_pct"
  printf -- '- p99 regression threshold: %s%% worse\n' "$p99_worse_pct"
  printf -- '- 전체 결과값: %s\n' "$overall_result"
  printf -- '- regression 결과값: %s\n\n' "$regression_result"

  printf '## 성능 판정 기준\n\n'
  printf -- '- stability hard gate: timeout, nonce mismatch, response type mismatch, per-connection FIFO violation, pending leak, queue/gateway backlog leak가 모두 0이어야 한다.\n'
  printf -- '- performance regression gate: baseline이 주어지면 같은 row key에서 throughput %s%% 이상 하락 또는 p99 latency %s%% 이상 악화를 WARN으로 기록한다.\n' "$throughput_drop_pct" "$p99_worse_pct"
  printf -- '- same-language stress row key에는 harness topology가 axis에 포함될 수 있으며, baseline 비교는 같은 topology 조건끼리 해석한다.\n'
  printf -- '- 절대 throughput/latency는 local 환경 기준값이며, baseline 비교는 같은 host/runtime/profile에서 실행한 결과끼리 수행한다.\n'
  printf -- '- `--fail-on-regression`을 지정하면 regression WARN도 non-zero exit로 처리한다.\n\n'

  printf '## 구성요소 요약\n\n'
  build_component_summary

  printf '\n## 측정 결과\n\n'
  printf '| Profile | Axis | Language | Transport | Payload(bytes) | Clients | Requests | Throughput(rps) | p50(ms) | p95(ms) | p99(ms) | Timeout | NonceMis | TypeMis | FIFOViol | PendLeak | QueueBacklog | GatewayBacklog | Gateway | Mem(MB) | 결과 |\n'
  printf '|---|---|---|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---|---:|---|\n'
  cat "$current_rows_file"

  printf '\n## Regression 비교\n\n'
  if [ -n "$baseline_file" ]; then
    printf -- '- baseline: `%s`\n' "${baseline_file#$repo_root/}"
    printf -- '- compared rows: %s\n' "$compared_count"
    printf -- '- missing baseline rows: %s\n' "$missing_count"
    printf -- '- warning rows: %s\n\n' "$warning_count"
    cat "$comparison_file"
  else
    printf -- '- SKIPPED: `--baseline <record.md>`가 지정되지 않았다.\n'
  fi

  printf '\n## git status 요약\n\n'
  printf '```text\n'
  git -C "$repo_root" status --short 2>/dev/null || true
  printf '```\n\n'

  printf '## 구성요소 로그 tail\n\n'
  for i in "${!component_names[@]}"; do
    printf '### %s\n\n' "${component_names[$i]}"
    printf '```text\n'
    tail -n 30 "${component_logs[$i]}" 2>/dev/null || true
    printf '```\n\n'
  done
} >"$record_file"

printf '\n=== performance 측정 결과 ===\n' >&2
build_component_summary >&2
printf '\n전체 결과값: %s\n' "$overall_result"
printf 'Regression 결과값: %s\n' "$regression_result"
printf '결과 기록 파일: `%s`\n' "${record_file#$repo_root/}"

case "$overall_result" in
  PASS) exit 0 ;;
  WARN)
    if [ "$fail_on_regression" -eq 1 ]; then
      exit 5
    fi
    exit 0
    ;;
  BLOCKED) exit 3 ;;
  INCOMPLETE) exit 4 ;;
  *) exit 1 ;;
esac
