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

# Fixture/mutation tests for author-service-kind-contract.sh.
#
# Each scenario copies the real contract files into a throwaway root, applies
# one mutation (or an equivalent-formatting rewrite), and asserts the guardrail
# verdict. Any consumer silently dropping a kind must go RED; equivalent
# formatting must stay GREEN. Run directly or via scripts/ci-backend-required.sh.

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
GUARD="$SCRIPT_DIR/author-service-kind-contract.sh"
REPO_ROOT="${AGENTTEAM_MESSAGE_SERVICE_KIND_GUARDRAIL_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd -P)}"

CONTRACT_FILES=(
  internal/store/message/api_types.go
  web/src/agentteam/api/messages.ts
  web/src/agentteam/api/conversations.ts
  web/src/agentteam/api/tasks/collaboration.ts
  web/src/agentteam/api/tasks/related.ts
  web/src/agentteam/pages/workspace-shell/socketPayloadGuards.ts
  web/src/agentteam/workspace/shared/messageAuthorProfile.ts
  web/src/types/conversation.ts
  shared/src/types.ts
)

fixtures=()
failed=0

new_fixture() {
  local root
  root="$(mktemp -d "${TMPDIR:-/tmp}/servicekind-contract-test.XXXXXX")"
  fixtures+=("$root")
  local file
  for file in "${CONTRACT_FILES[@]}"; do
    if [[ ! -f "$REPO_ROOT/$file" ]]; then
      printf 'BAIL: missing contract file %s\n' "$file" >&2
      exit 2
    fi
    mkdir -p "$root/$(dirname "$file")"
    cp "$REPO_ROOT/$file" "$root/$file"
  done
  FIXTURE_ROOT="$root"
}

# Apply a sed program to a file inside the current fixture, portably.
mutate() {
  local file="$1"
  local program="$2"
  sed -e "$program" "$FIXTURE_ROOT/$file" > "$FIXTURE_ROOT/$file.tmp"
  mv "$FIXTURE_ROOT/$file.tmp" "$FIXTURE_ROOT/$file"
}

# Drop lines matching an awk regex from a file inside the current fixture.
drop_lines() {
  local file="$1"
  local pattern="$2"
  awk -v drop="$pattern" '$0 ~ drop { next } { print }' \
    "$FIXTURE_ROOT/$file" > "$FIXTURE_ROOT/$file.tmp"
  mv "$FIXTURE_ROOT/$file.tmp" "$FIXTURE_ROOT/$file"
}

# Rewrite a `serviceKind?: a | b | ...` union (single or multi-line) into an
# equivalent multi-line union. Formatting changes, the kind set must not.
wrap_union() {
  local file="$1"
  awk '
    /serviceKind\?:/ {
      indent = match($0, /[^ \t]/) - 1
      pad = substr($0, 1, indent)
      body = $0
      sub(/^[^:]*:[[:space:]]*/, "", body)
      n = split(body, parts, /[|]/)
      printf "%sserviceKind?:\n", pad
      for (i = 1; i <= n; i++) {
        t = parts[i]
        gsub(/^[[:space:]]+|[[:space:]]+$/, "", t)
        if (t != "") printf "%s  | %s\n", pad, t
      }
      next
    }
    { print }
  ' "$FIXTURE_ROOT/$file" > "$FIXTURE_ROOT/$file.tmp"
  mv "$FIXTURE_ROOT/$file.tmp" "$FIXTURE_ROOT/$file"
}

run_guard() {
  if AGENTTEAM_MESSAGE_SERVICE_KIND_GUARDRAIL_ROOT="$FIXTURE_ROOT" \
    bash "$GUARD" >/dev/null 2>&1; then
    return 0
  fi
  return 1
}

scenario() {
  local name="$1"
  local want="$2" # green | red
  local got
  if run_guard; then got="green"; else got="red"; fi
  if [[ "$got" == "$want" ]]; then
    printf 'ok   %s (%s)\n' "$name" "$got"
  else
    printf 'FAIL %s: expected %s, got %s\n' "$name" "$want" "$got" >&2
    failed=$((failed + 1))
  fi
}

new_fixture
scenario "pristine contract files" green

new_fixture
mutate internal/store/message/api_types.go \
  's/AuthorServiceKindSchedule:/"future_kind", AuthorServiceKindSchedule:/'
scenario "backend switch gains a raw-string kind" red

new_fixture
mutate web/src/agentteam/api/messages.ts \
  's/"", "app", "service_account", "workflow", "schedule"/"", "app", "service_account", "schedule"/'
scenario "messages.ts refine allow-list drops workflow" red

new_fixture
mutate web/src/agentteam/api/conversations.ts \
  's/"", "app", "service_account", "workflow", "schedule"/"", "app", "service_account", "schedule"/'
scenario "conversations.ts enum drops workflow" red

new_fixture
mutate web/src/agentteam/api/tasks/collaboration.ts \
  's/"", "app", "service_account", "workflow", "schedule"/"", "app", "service_account", "schedule"/'
scenario "tasks/collaboration.ts enum drops workflow" red

new_fixture
mutate web/src/agentteam/api/tasks/related.ts \
  's/"", "app", "service_account", "workflow", "schedule"/"", "app", "service_account", "schedule"/'
scenario "tasks/related.ts enum drops workflow" red

new_fixture
drop_lines web/src/agentteam/pages/workspace-shell/socketPayloadGuards.ts \
  'value === "workflow"'
scenario "realtime socket guard drops workflow" red

new_fixture
drop_lines web/src/agentteam/workspace/shared/messageAuthorProfile.ts \
  'normalized === "workflow"'
scenario "display normalizer drops workflow" red

new_fixture
mutate web/src/types/conversation.ts 's/ | "workflow"//'
scenario "web type union drops workflow" red

new_fixture
mutate shared/src/types.ts 's/ | "schedule"//'
scenario "shared type union drops schedule" red

new_fixture
wrap_union shared/src/types.ts
scenario "shared type union reformatted multi-line stays green" green

new_fixture
wrap_union web/src/types/conversation.ts
scenario "web type union reformatted multi-line stays green" green

cleanup() {
  local root
  for root in "${fixtures[@]}"; do
    rm -rf "$root"
  done
}
trap cleanup EXIT

if (( failed > 0 )); then
  printf 'author-service-kind-contract mutation tests: %d failure(s)\n' "$failed" >&2
  exit 1
fi

printf 'author-service-kind-contract mutation tests passed.\n'
