#!/usr/bin/env bash
# Regression tests for od-incidents: the resolve arg-gate (refuses without --artifact)
# plus one mocked-HTTP happy path per subcommand (list/show/search/resolve) and one
# non-2xx error-handling check. Mock collector is a tiny Bun.serve instance on a
# random loopback port; no real collector is contacted.
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
CLI="$ROOT/bin/od-incidents"
PASS=0; FAIL=0
ok()  { PASS=$((PASS+1)); printf 'PASS %s\n' "$1"; }
bad() { FAIL=$((FAIL+1)); printf 'FAIL %s\n     %s\n' "$1" "$2"; }

TMP=$(mktemp -d "${TMPDIR:-/tmp}/od-incidents-XXXX")
trap 'rm -rf "$TMP"; [[ -n "${SERVER_PID:-}" ]] && kill "$SERVER_PID" 2>/dev/null' EXIT

TOKEN="test-token-abc"

cat >"$TMP/mock-server.mjs" <<'EOF'
const token = process.env.MOCK_TOKEN;
const incidents = [
  { id: "INC-1", state: "filed", type: "resource-overload", priority: "P1", title: "cpu pegged", description: "load avg spiked on debian1" },
  { id: "INC-2", state: "resolved", type: "machine-down", priority: "P0", title: "e14 crash", description: "silent hang overnight" },
];

const server = Bun.serve({
  port: 0,
  async fetch(req) {
    const auth = req.headers.get("authorization");
    if (auth !== `Bearer ${token}`) return new Response("unauthorized", { status: 401 });
    const url = new URL(req.url);
    if (url.pathname === "/incidents" && req.method === "GET") {
      const query = url.searchParams.get("query");
      let list = incidents;
      if (query) {
        const needle = query.toLowerCase();
        list = incidents.filter(
          (i) => i.title.toLowerCase().includes(needle) || i.description.toLowerCase().includes(needle)
        );
      }
      return Response.json({ incidents: list, coverage: { stale: false } });
    }
    const showMatch = /^\/incidents\/([^/]+)$/.exec(url.pathname);
    if (showMatch && req.method === "GET") {
      const found = incidents.find((i) => i.id === showMatch[1]);
      if (!found) return Response.json({ error: "not-found" }, { status: 404 });
      return Response.json({ ...found, brief: "assembled brief text", activity: [] });
    }
    if (url.pathname === "/actions/incident.resolve" && req.method === "POST") {
      const body = await req.json().catch(() => ({}));
      const args = body?.args;
      if (!args || typeof args.incidentId !== "string" || typeof args.artifact !== "string") {
        return new Response("bad shape", { status: 400 });
      }
      return Response.json({ id: args.incidentId, state: "resolved", ...args });
    }
    return new Response("not found", { status: 404 });
  },
});
console.log(server.port);
await new Promise(() => {});
EOF

MOCK_TOKEN="$TOKEN" bun "$TMP/mock-server.mjs" >"$TMP/server.out" 2>"$TMP/server.err" &
SERVER_PID=$!

for _ in $(seq 1 50); do
  [[ -s "$TMP/server.out" ]] && break
  sleep 0.1
done
PORT=$(cat "$TMP/server.out" 2>/dev/null)
if [[ -z "$PORT" ]]; then
  bad "mock collector server starts" "no port printed; stderr=$(cat "$TMP/server.err")"
  echo; echo "PASS=$PASS FAIL=$FAIL"; exit 1
else
  ok "mock collector server starts"
fi

export COLLECTOR_URL="http://127.0.0.1:$PORT"
export COLLECTOR_TOKEN="$TOKEN"

run() { bun "$CLI" "$@" >"$TMP/out" 2>"$TMP/err"; echo $?; }

# --- arg gate: resolve without --artifact ---
rc=$(run resolve INC-1)
[[ "$rc" -eq 2 && "$(cat "$TMP/err")" == *"--artifact"* ]] \
  && ok "resolve without --artifact exits 2 and names the rule" \
  || bad "resolve without --artifact exits 2 and names the rule" "rc=$rc err=$(cat "$TMP/err")"

# --- list happy path ---
rc=$(run list)
[[ "$rc" -eq 0 && "$(cat "$TMP/out")" == *"INC-1"* && "$(cat "$TMP/out")" == *"INC-2"* ]] \
  && ok "list prints a table with both incidents" \
  || bad "list prints a table with both incidents" "rc=$rc out=$(cat "$TMP/out")"

# --- list --type filter (client-side) ---
rc=$(run list --type machine-down)
[[ "$rc" -eq 0 && "$(cat "$TMP/out")" == *"INC-2"* && "$(cat "$TMP/out")" != *"INC-1"* ]] \
  && ok "list --type filters to matching incidents" \
  || bad "list --type filters to matching incidents" "rc=$rc out=$(cat "$TMP/out")"

# --- show happy path ---
rc=$(run show INC-1)
[[ "$rc" -eq 0 && "$(cat "$TMP/out")" == *'"brief": "assembled brief text"'* ]] \
  && ok "show prints the full record including the brief" \
  || bad "show prints the full record including the brief" "rc=$rc out=$(cat "$TMP/out")"

# --- show unknown id: non-2xx surfaces status + body, nonzero exit ---
rc=$(run show INC-999)
[[ "$rc" -ne 0 && "$(cat "$TMP/err")" == *"404"* && "$(cat "$TMP/err")" == *"not-found"* ]] \
  && ok "show of unknown id prints status+body and exits nonzero" \
  || bad "show of unknown id prints status+body and exits nonzero" "rc=$rc err=$(cat "$TMP/err")"

# --- search happy path ---
rc=$(run search crash)
[[ "$rc" -eq 0 && "$(cat "$TMP/out")" == *"INC-2"* && "$(cat "$TMP/out")" != *"INC-1"* ]] \
  && ok "search matches title/description substring" \
  || bad "search matches title/description substring" "rc=$rc out=$(cat "$TMP/out")"

# --- resolve happy path ---
rc=$(run resolve INC-1 --artifact "https://example.test/pr/1" --summary "fixed via cgroup kill")
[[ "$rc" -eq 0 && "$(cat "$TMP/out")" == *'"state": "resolved"'* && "$(cat "$TMP/out")" == *"fixed via cgroup kill"* ]] \
  && ok "resolve with --artifact transitions state and records fields" \
  || bad "resolve with --artifact transitions state and records fields" "rc=$rc out=$(cat "$TMP/out")"

# --- base URL falls back to config.toml's port when COLLECTOR_URL is unset ---
mkdir -p "$TMP/cfgdir"
printf 'port = %s\nbindHost = "127.0.0.1"\n' "$PORT" >"$TMP/cfgdir/config.toml"
rc=$(env -u COLLECTOR_URL OVERDECK_CONFIG_DIR="$TMP/cfgdir" COLLECTOR_TOKEN="$TOKEN" bun "$CLI" list >"$TMP/out" 2>"$TMP/err"; echo $?)
[[ "$rc" -eq 0 && "$(cat "$TMP/out")" == *"INC-1"* ]] \
  && ok "base URL falls back to config.toml's port when COLLECTOR_URL is unset" \
  || bad "base URL falls back to config.toml's port when COLLECTOR_URL is unset" "rc=$rc out=$(cat "$TMP/out") err=$(cat "$TMP/err")"

# --- connection failure names the URL it tried, not a bare "unable to connect" ---
mkdir -p "$TMP/emptycfg"
rc=$(env -u COLLECTOR_URL OVERDECK_CONFIG_DIR="$TMP/emptycfg" COLLECTOR_TOKEN="$TOKEN" bun "$CLI" list >"$TMP/out" 2>"$TMP/err"; echo $?)
[[ "$rc" -ne 0 && "$(cat "$TMP/err")" == *"could not reach collector"* && "$(cat "$TMP/err")" == *"127.0.0.1:4980"* ]] \
  && ok "connection failure names the URL it tried" \
  || bad "connection failure names the URL it tried" "rc=$rc err=$(cat "$TMP/err")"

echo
echo "PASS=$PASS FAIL=$FAIL"
[[ "$FAIL" -eq 0 ]]
