#!/usr/bin/env bash
# Regression harness for Gitea PR metadata normalization.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/pr-metadata-gitea}"
REPO_DIR="$WORK_DIR/repo"
FIXTURE_DIR="$WORK_DIR/fixtures"
STUB_DIR="$WORK_DIR/stubs"
HOME_DIR="$WORK_DIR/home"
CREDENTIALS_FILE="$WORK_DIR/credentials.json"

rm -rf "$WORK_DIR"
mkdir -p "$REPO_DIR" "$FIXTURE_DIR" "$STUB_DIR" "$HOME_DIR"

git -C "$REPO_DIR" init -q
git -C "$REPO_DIR" remote add origin https://git.uscllc.com/USC/uconnect.git
# HERMETICITY (#1007) — TWO mechanisms with DIFFERENT jobs; do not conflate them.
#
# OPERATIVE: the empty repo-local `mosaic.gitIdentity` below. get_gitea_token()
# step 0 resolves a per-agent identity from `git config --get mosaic.gitIdentity`,
# which on a provisioned agent seat is set GLOBALLY and so leaks into this fresh
# repo. Step 0 runs BEFORE the credential loader AND before the GITEA_TOKEN env
# check, so the `GITEA_TOKEN="stub-token"` set in the run helpers below is
# silently overridden and a REAL per-slot token from $HOME is what reaches curl.
# Measured on a provisioned seat before this pin: both stub-curl calls carried
# the real token in argv. An empty repo-local value shadows the global one and
# reads back empty at rc=0.
#
# CONTAINMENT: the sandboxed HOME in the three run helpers below. It only has to
# bound a failure that the pin should already have prevented.
#
# NOTE FOR ANYONE AUDITING THIS SUITE: the sandboxed HOME is containment, NOT an
# assay. Running a suite under a decoy HOME to test for this defect REMOVES the
# trigger — ~/.gitconfig is where the global identity lives, so step 0 is skipped
# by construction and every suite reads clean however vulnerable it is. To measure,
# REPLICATE a seat (a decoy HOME whose .gitconfig sets mosaic.gitIdentity, with no
# per-slot token) so step 0 reaches its fail-loud branch. See
# test-gitea-token-identity.sh for the stronger `env -i HOME=…` form used where a
# suite's whole subject IS identity resolution.
#
# Note the env-var route does NOT work: detect-platform.sh reads
# "${MOSAIC_GIT_IDENTITY:-}", and `:-` treats set-but-empty identically to unset.
git -C "$REPO_DIR" config mosaic.gitIdentity ""

# The pin above removes step 0, but this suite has a SECOND, independent
# dependency on operator state, and closing only the first would leave the suite
# red on any hermetic environment. The `GITEA_TOKEN="stub-token"` /
# `GITEA_URL="https://git.example.test"` pair the run helpers set is INERT: step 2
# of get_gitea_token accepts GITEA_TOKEN only when GITEA_URL matches the remote
# host, and this repo's origin is git.uscllc.com, so that pair can never satisfy
# it. Before this fixture the only credential that could reach the authenticated
# curl branch was a REAL one — from step 0 on an agent seat, or from step 1
# reading the operator's own ~/.config/mosaic/credentials.json. That is why the
# "curl success path" case passed: not because the stub credential worked, but
# because a production credential was available.
#
# A fixture is used rather than relying on the sandboxed HOME making step 1 find
# nothing: a test that passes because production configuration is ABSENT fails
# the moment it is present. Step 1 now resolves deterministically to a value that
# is a fixture on every machine.
cat > "$CREDENTIALS_FILE" <<'JSON'
{
  "gitea": {
    "usc": {
      "url": "https://git.uscllc.com",
      "token": "stub-token"
    }
  }
}
JSON

cat > "$FIXTURE_DIR/gitea-standard.json" <<'JSON'
{
  "number": 1905,
  "title": "Smoke gate fix",
  "state": "open",
  "user": {"login": "edith"},
  "head": {"ref": "edith/t_39ce717c-authentik-smoke-gate"},
  "base": {"ref": "main"},
  "labels": [{"name": "ci"}],
  "assignees": [{"login": "edith"}],
  "html_url": "https://git.uscllc.com/USC/uconnect/pulls/1905"
}
JSON

cat > "$FIXTURE_DIR/gitea-fallback.json" <<'JSON'
{
  "number": 1908,
  "title": "Fallback branch fields",
  "state": "open",
  "user": {"login": "edith"},
  "head_branch": "fix/fallback-head",
  "base_branch": "main",
  "html_url": "https://git.uscllc.com/USC/uconnect/pulls/1908"
}
JSON

cat > "$FIXTURE_DIR/gitea-refs-pull-label.json" <<'JSON'
{
  "number": 1908,
  "title": "Closed merged PR with synthetic pull ref",
  "state": "closed",
  "user": {"login": "edith"},
  "head": {"ref": "refs/pull/1908/head", "label": "fix/t_23fa9e1d-portal-health-backend"},
  "base": {"ref": "main"},
  "html_url": "https://git.uscllc.com/USC/uconnect/pulls/1908"
}
JSON

cat > "$FIXTURE_DIR/gitea-error.json" <<'JSON'
{"message": "user does not exist [uid: 0, name: ]", "url": "https://git.uscllc.com/api/swagger"}
JSON

cat > "$STUB_DIR/curl" <<'SH'
#!/usr/bin/env bash
set -euo pipefail

output_file=""
while [[ $# -gt 0 ]]; do
    case "$1" in
        -o)
            output_file="$2"
            shift 2
            ;;
        -w|-H|-u)
            shift 2
            ;;
        -s|-S|-sS)
            shift
            ;;
        *)
            shift
            ;;
    esac
done

if [[ -z "$output_file" ]]; then
    echo "curl stub expected -o <output_file>" >&2
    exit 2
fi

case "${MOSAIC_STUB_CURL_MODE:-success}" in
    success)
        cat > "$output_file" <<'JSON'
{
  "number": 1910,
  "title": "Live curl path",
  "state": "open",
  "user": {"login": "edith"},
  "head": {"ref": "fix/live-curl-path"},
  "base": {"ref": "main"},
  "html_url": "https://git.example.test/acme/widgets/pulls/1910"
}
JSON
        printf '200'
        ;;
    cat-fails-after-2xx)
        rm -f -- "$output_file"
        ln -s /nonexistent/pr-metadata-body "$output_file"
        printf '200'
        ;;
    *)
        echo "unknown MOSAIC_STUB_CURL_MODE=${MOSAIC_STUB_CURL_MODE:-}" >&2
        exit 2
        ;;
esac
SH
chmod +x "$STUB_DIR/curl"

assert_tmpdir_empty() {
    local tmpdir="$1" leftover
    leftover=$(find "$tmpdir" -mindepth 1 -print -quit)
    if [[ -n "$leftover" ]]; then
        echo "Expected tmpfile cleanup, found leftover: $leftover" >&2
        find "$tmpdir" -mindepth 1 -maxdepth 1 -ls >&2
        exit 1
    fi
}

run_curl_success_case() {
    local tmpdir="$WORK_DIR/tmp-success" stderr_file="$WORK_DIR/curl-success.stderr"
    local output status
    mkdir -p "$tmpdir"

    set +e
    output=$(cd "$REPO_DIR" && \
        PATH="$STUB_DIR:$PATH" \
        HOME="$HOME_DIR" \
        MOSAIC_CREDENTIALS_FILE="$CREDENTIALS_FILE" \
        TMPDIR="$tmpdir" \
        GITEA_TOKEN="stub-token" \
        GITEA_URL="https://git.example.test" \
        MOSAIC_STUB_CURL_MODE="success" \
        "$SCRIPT_DIR/pr-metadata.sh" -n 1910 2>"$stderr_file")
    status=$?
    set -e

    if [[ "$status" -ne 0 ]]; then
        echo "Expected curl success path to pass, got status $status" >&2
        cat "$stderr_file" >&2
        exit 1
    fi
    if grep -q "unbound variable" "$stderr_file"; then
        echo "curl success path emitted unbound-variable cleanup noise" >&2
        cat "$stderr_file" >&2
        exit 1
    fi
    assert_tmpdir_empty "$tmpdir"

    PR_METADATA_OUTPUT="$output" python3 - <<'PY'
import json
import os

data = json.loads(os.environ["PR_METADATA_OUTPUT"])
assert data["number"] == 1910, data
assert data["baseRefName"] == "main", data
assert data["headRefName"] == "fix/live-curl-path", data
PY
}

run_curl_early_exit_cleanup_case() {
    local tmpdir="$WORK_DIR/tmp-early-exit" stderr_file="$WORK_DIR/curl-early-exit.stderr"
    local output status
    mkdir -p "$tmpdir"

    set +e
    output=$(cd "$REPO_DIR" && \
        PATH="$STUB_DIR:$PATH" \
        HOME="$HOME_DIR" \
        MOSAIC_CREDENTIALS_FILE="$CREDENTIALS_FILE" \
        TMPDIR="$tmpdir" \
        GITEA_TOKEN="stub-token" \
        GITEA_URL="https://git.example.test" \
        MOSAIC_STUB_CURL_MODE="cat-fails-after-2xx" \
        "$SCRIPT_DIR/pr-metadata.sh" -n 1910 2>"$stderr_file")
    status=$?
    set -e

    if [[ "$status" -eq 0 ]]; then
        echo "Expected unreadable 2xx body path to fail" >&2
        printf '%s\n' "$output" >&2
        exit 1
    fi
    if grep -q "unbound variable" "$stderr_file"; then
        echo "curl early-exit path emitted unbound-variable cleanup noise" >&2
        cat "$stderr_file" >&2
        exit 1
    fi
    if ! grep -q "No such file or directory" "$stderr_file"; then
        echo "Expected body-read failure from broken symlink path" >&2
        cat "$stderr_file" >&2
        exit 1
    fi
    if grep -q "Gitea API returned non-JSON" "$stderr_file"; then
        echo "curl helper masked body-read failure as later JSON parsing failure" >&2
        cat "$stderr_file" >&2
        exit 1
    fi
    assert_tmpdir_empty "$tmpdir"
}

run_case() {
    local fixture="$1" expected_number="$2" expected_head="$3"
    local output
    output=$(cd "$REPO_DIR" && HOME="$HOME_DIR" MOSAIC_CREDENTIALS_FILE="$CREDENTIALS_FILE" \
        MOSAIC_GITEA_PR_METADATA_RAW_FILE="$fixture" "$SCRIPT_DIR/pr-metadata.sh" -n "$expected_number")
    PR_METADATA_OUTPUT="$output" python3 - "$expected_number" "$expected_head" <<'PY'
import json
import os
import sys

data = json.loads(os.environ["PR_METADATA_OUTPUT"])
expected_number = int(sys.argv[1])
expected_head = sys.argv[2]
assert data["number"] == expected_number, data
assert data["baseRefName"] == "main", data
assert data["headRefName"] == expected_head, data
PY
}

run_case "$FIXTURE_DIR/gitea-standard.json" 1905 edith/t_39ce717c-authentik-smoke-gate
run_case "$FIXTURE_DIR/gitea-fallback.json" 1908 fix/fallback-head
run_case "$FIXTURE_DIR/gitea-refs-pull-label.json" 1908 fix/t_23fa9e1d-portal-health-backend
run_curl_success_case
run_curl_early_exit_cleanup_case

if cd "$REPO_DIR" && MOSAIC_GITEA_PR_METADATA_RAW_FILE="$FIXTURE_DIR/gitea-error.json" "$SCRIPT_DIR/pr-metadata.sh" -n 1909 >/dev/null 2>"$WORK_DIR/error.log"; then
    echo "Expected API error fixture to fail" >&2
    exit 1
fi
grep -q "Gitea API error" "$WORK_DIR/error.log"

echo "Gitea PR metadata regression harness passed"
