#!/usr/bin/env bash
# Phase 2 dimensions 1 (idempotent re-install) and 2 (source disappears →
# fail-closed verify) on one source-backed pack. Designed to run from the
# repo root with TiDB available at TIDB_HOST:TIDB_PORT (default localhost:4000).
#
# Usage:
#   ./tests/integration/l0-completeness/idempotent-and-source-missing.sh \
#     <pack-source> <pack-name> [dependency-source ...]
#
# <pack-source> may be a local path, local_path:<path>, or official_repo:<name>.
# Set SETUP_GO_TAGS when the pack needs distribution-registered L1 capabilities
# (for example SETUP_GO_TAGS=crm for salesforce-crm-standard).
# Each optional dependency source is installed in argument order before the
# target pack in every fresh database used by this probe. Dependency validation
# remains owned by the installer; the probe only supplies the declared sources.
#
# Outputs assertions to stderr + a one-line PASS/FAIL summary.
# Each Phase 2 dimension is a separate function; exit code = number of
# dimensions that failed.

set -u

PACK_SOURCE="${1:-}"
PACK_NAME="${2:-}"
PACK_DEPENDENCY_SOURCES=("${@:3}")
REPO_ROOT="$(cd "$(dirname "$0")"/../../.. && pwd)"

if [[ -z "$PACK_SOURCE" || -z "$PACK_NAME" ]]; then
  echo "usage: $0 <pack-source-path> <pack-name>" >&2
  exit 2
fi

: "${TIDB_HOST:=127.0.0.1}"
: "${TIDB_PORT:=4000}"
: "${TIDB_USER:=root}"
: "${TIDB_PASSWORD:=}"
: "${TIDB_DATABASE:=nexuscrm}"

export TIDB_HOST TIDB_PORT TIDB_USER TIDB_PASSWORD TIDB_DATABASE

OUT_DIR="${OUT_DIR:-/tmp/sfmeta/run/phase2}"
mkdir -p "$OUT_DIR"
source "$(dirname "$0")/sql-client.sh"
TAG="$(echo "$PACK_NAME" | tr -c 'a-zA-Z0-9-' '-')"
RESULT_TAG="$(printf '%s' "$PACK_NAME" | tr -c 'a-zA-Z0-9-' '-')"
RESULT_JSON="${RESULT_JSON:-$OUT_DIR/$RESULT_TAG-idempotent-and-source-missing.json}"

DIM_IDEMPOTENT_STATUS="fail"
DIM_IDEMPOTENT_DETAIL="probe did not complete"
DIM_SOURCE_MISSING_STATUS="fail"
DIM_SOURCE_MISSING_DETAIL="probe did not complete"
RESULTS_WRITTEN=0

json_escape() {
  printf '%s' "$1" | sed \
    -e 's/\\/\\\\/g' \
    -e 's/"/\\"/g' \
    -e 's/	/\\t/g'
}

write_results_json() {
  cat > "$RESULT_JSON" <<JSON
{
  "schemaVersion": 1,
  "probe": "idempotent-and-source-missing",
  "pack": "$(json_escape "$PACK_NAME")",
  "source": "$(json_escape "$PACK_SOURCE")",
  "dimensions": [
    {
      "id": "install.idempotent_reinstall",
      "title": "idempotent re-install",
      "status": "$(json_escape "$DIM_IDEMPOTENT_STATUS")",
      "detail": "$(json_escape "$DIM_IDEMPOTENT_DETAIL")"
    },
    {
      "id": "verify.source_missing_fail_closed",
      "title": "source disappears -> verify-pack fail-closed",
      "status": "$(json_escape "$DIM_SOURCE_MISSING_STATUS")",
      "detail": "$(json_escape "$DIM_SOURCE_MISSING_DETAIL")"
    }
  ]
}
JSON
  RESULTS_WRITTEN=1
}

write_results_on_exit() {
  local rc=$?
  if [[ "$RESULTS_WRITTEN" != "1" ]]; then
    write_results_json || true
    echo "results: $RESULT_JSON" >&2
  fi
  exit "$rc"
}

trap write_results_on_exit EXIT

reset_db() {
  l0_completeness_sql_reset_database
}

setup_init_l0() {
  run_setup "$1" init-l0
}

setup_install_pack() {
  local source="$1" log_prefix="$2"
  run_setup "$log_prefix" install-pack --source "$source"
}

setup_install_dependencies() {
  local log_prefix="$1" source index=0
  if (( ${#PACK_DEPENDENCY_SOURCES[@]} == 0 )); then
    return 0
  fi
  for source in "${PACK_DEPENDENCY_SOURCES[@]}"; do
    index=$((index + 1))
    setup_install_pack "$source" "$log_prefix-$index" || return
  done
}

setup_verify_pack() {
  local name="$1" log_prefix="$2"
  run_setup "$log_prefix" verify-pack "$name"
}

run_setup() {
  local log_prefix="$1"
  shift
  local cmd="${1:-}"
  local setup_dir="$REPO_ROOT/backend"
  local go_env=()
  local go_tags=()
  if [[ "$cmd" != "init-l0" && -n "${SETUP_GO_TAGS:-}" ]]; then
    setup_dir="$REPO_ROOT/backend/distributions"
    go_env=(GOWORK=off)
    go_tags=(-tags "$SETUP_GO_TAGS")
  fi
  (
    cd "$setup_dir"
    if [[ "${#go_tags[@]}" -gt 0 ]]; then
      env "${go_env[@]}" go run "${go_tags[@]}" ./cmd/setup "$@"
    else
      go run ./cmd/setup "$@"
    fi
  ) > "$log_prefix.stdout" 2> "$log_prefix.stderr"
}

source_path_for_missing_probe() {
  case "$PACK_SOURCE" in
    official_repo:*)
      local pack="${PACK_SOURCE#official_repo:}"
      if [[ -z "${NEXUSCRM_OFFICIAL_APP_PACKS_ROOT:-}" ]]; then
        return 1
      fi
      printf '%s/%s\n' "$NEXUSCRM_OFFICIAL_APP_PACKS_ROOT" "$pack"
      ;;
    local_path:*)
      printf '%s\n' "${PACK_SOURCE#local_path:}"
      ;;
    *)
      printf '%s\n' "$PACK_SOURCE"
      ;;
  esac
}

installed_pack_hash() {
  l0_completeness_sql_query "$TIDB_DATABASE" \
    "SELECT AppliedPayloadHash FROM _System_InstalledPack WHERE Name='$1';"
}

installed_pack_status() {
  l0_completeness_sql_query "$TIDB_DATABASE" \
    "SELECT Status FROM _System_InstalledPack WHERE Name='$1';"
}

installed_pack_count() {
  l0_completeness_sql_query "$TIDB_DATABASE" \
    "SELECT COUNT(*) FROM _System_InstalledPack WHERE Name='$1';"
}

field_count() {
  l0_completeness_sql_query "$TIDB_DATABASE" "
    SELECT COUNT(*) FROM _System_Field f
    JOIN _System_Object o ON f.ObjectId = o.Id
    WHERE o.ApiName IN (
      SELECT ApiName FROM _System_Object
    );"
}

# ---------- Dimension 1: idempotent re-install ----------
test_idempotent() {
  echo "═══ idempotent re-install on $PACK_NAME ═══" >&2
  DIM_IDEMPOTENT_STATUS="fail"
  DIM_IDEMPOTENT_DETAIL="started but did not complete"
  reset_db
  setup_init_l0 "$OUT_DIR/$TAG-idem-init"
  if [[ $? -ne 0 ]]; then
    echo "  ❌ init-l0 failed; check $OUT_DIR/$TAG-idem-init.stderr" >&2
    DIM_IDEMPOTENT_DETAIL="init-l0 failed"
    return 1
  fi
  setup_install_dependencies "$OUT_DIR/$TAG-idem-dependency"
  if [[ $? -ne 0 ]]; then
    echo "  ❌ dependency install failed; check $OUT_DIR/$TAG-idem-dependency-*.stderr" >&2
    DIM_IDEMPOTENT_DETAIL="dependency install failed"
    return 1
  fi

  echo "  install #1..." >&2
  setup_install_pack "$PACK_SOURCE" "$OUT_DIR/$TAG-idem-install1"
  local ec1=$?
  if [[ $ec1 -ne 0 ]]; then
    echo "  ❌ first install failed (exit $ec1); check $OUT_DIR/$TAG-idem-install1.stderr" >&2
    DIM_IDEMPOTENT_DETAIL="first install failed with exit $ec1"
    return 1
  fi
  local hash1 status1 count1 fields1
  hash1="$(installed_pack_hash "$PACK_NAME")"
  status1="$(installed_pack_status "$PACK_NAME")"
  count1="$(installed_pack_count "$PACK_NAME")"
  fields1="$(field_count)"

  echo "  install #2 (same source)..." >&2
  setup_install_pack "$PACK_SOURCE" "$OUT_DIR/$TAG-idem-install2"
  local ec2=$?
  if [[ $ec2 -ne 0 ]]; then
    echo "  ❌ second install failed (exit $ec2); check $OUT_DIR/$TAG-idem-install2.stderr" >&2
    DIM_IDEMPOTENT_DETAIL="second install failed with exit $ec2"
    return 1
  fi
  local hash2 status2 count2 fields2
  hash2="$(installed_pack_hash "$PACK_NAME")"
  status2="$(installed_pack_status "$PACK_NAME")"
  count2="$(installed_pack_count "$PACK_NAME")"
  fields2="$(field_count)"

  local fail=0
  local reasons=()
  echo "  hash:    $hash1 → $hash2" >&2
  [[ "$hash1" != "$hash2" ]] && { echo "  ❌ hash drifted across re-install" >&2; fail=1; reasons+=("hash drifted"); }
  echo "  status:  $status1 → $status2" >&2
  [[ "$status2" != "active" ]] && { echo "  ❌ second install did not converge to active" >&2; fail=1; reasons+=("second install status=$status2"); }
  echo "  rows:    $count1 → $count2" >&2
  [[ "$count1" -ne 1 || "$count2" -ne 1 ]] && { echo "  ❌ duplicate _System_InstalledPack rows" >&2; fail=1; reasons+=("installed pack row count $count1->$count2"); }
  echo "  fields:  $fields1 → $fields2" >&2
  [[ "$fields1" -ne "$fields2" ]] && { echo "  ❌ _System_Field row count drifted ($fields1 → $fields2) on no-op re-install" >&2; fail=1; reasons+=("field row count $fields1->$fields2"); }

  if [[ "$fail" -eq 0 ]]; then
    echo "  ✅ idempotent" >&2
    DIM_IDEMPOTENT_STATUS="pass"
    DIM_IDEMPOTENT_DETAIL="hash/status/row/field counts stable across reinstall"
  else
    DIM_IDEMPOTENT_STATUS="fail"
    DIM_IDEMPOTENT_DETAIL="$(IFS='; '; echo "${reasons[*]}")"
  fi
  return "$fail"
}

# ---------- Dimension 2: source disappears → fail-closed verify ----------
test_source_missing() {
  echo "═══ source disappears → verify-pack must fail-closed on $PACK_NAME ═══" >&2
  DIM_SOURCE_MISSING_STATUS="fail"
  DIM_SOURCE_MISSING_DETAIL="started but did not complete"
  # Assume already installed from previous dimension; if not, install fresh.
  if [[ "$(installed_pack_status "$PACK_NAME")" != "active" ]]; then
    reset_db
    setup_init_l0 "$OUT_DIR/$TAG-srcmiss-init"
    setup_install_dependencies "$OUT_DIR/$TAG-srcmiss-dependency"
    [[ $? -ne 0 ]] && { echo "  ❌ dependency install failed before source-missing test" >&2; DIM_SOURCE_MISSING_DETAIL="dependency install failed before source-missing test"; return 1; }
    setup_install_pack "$PACK_SOURCE" "$OUT_DIR/$TAG-srcmiss-install"
    [[ $? -ne 0 ]] && { echo "  ❌ install failed before source-missing test" >&2; DIM_SOURCE_MISSING_DETAIL="install failed before source-missing test"; return 1; }
  fi

  # Move the resolved source directory aside (don't delete — restore at the end).
  # For official_repo sources this proves verify-pack really re-reads the
  # official app-packs checkout instead of silently trusting the installed row.
  local source_path
  source_path="$(source_path_for_missing_probe)" || {
    echo "  ❌ cannot resolve source path for $PACK_SOURCE" >&2
    DIM_SOURCE_MISSING_STATUS="fail"
    DIM_SOURCE_MISSING_DETAIL="could not resolve source path for missing-source probe"
    return 1
  }
  if [[ ! -d "$source_path" ]]; then
    echo "  ❌ resolved source path is not a directory: $source_path" >&2
    DIM_SOURCE_MISSING_STATUS="fail"
    DIM_SOURCE_MISSING_DETAIL="resolved source path is not a directory"
    return 1
  fi
  local moved="${source_path}.MOVED-$$"
  mv "$source_path" "$moved"
  echo "  source moved: $source_path → $moved" >&2

  setup_verify_pack "$PACK_NAME" "$OUT_DIR/$TAG-srcmiss-verify"
  local ec=$?

  # Restore unconditionally
  mv "$moved" "$source_path"
  echo "  source restored" >&2

  local fail=0
  if [[ $ec -eq 0 ]]; then
    echo "  ❌ verify-pack returned 0 (silent pass) when source was missing — should fail-closed" >&2
    DIM_SOURCE_MISSING_STATUS="fail"
    DIM_SOURCE_MISSING_DETAIL="verify-pack returned 0 when source was missing"
    fail=1
  else
    echo "  verify-pack exit=$ec (non-zero, fail-closed candidate)" >&2
    # Sanity: error message should mention the source
    if grep -qiE 'source|resolve|local_path' "$OUT_DIR/$TAG-srcmiss-verify.stderr"; then
      echo "  ✅ fail-closed with source-related diagnostic" >&2
      DIM_SOURCE_MISSING_STATUS="pass"
      DIM_SOURCE_MISSING_DETAIL="verify-pack failed closed with source-related diagnostic"
    else
      echo "  ❌ verify-pack failed, but diagnostic doesn't mention source — not proof of source-missing fail-closed behavior" >&2
      echo "  --- stderr tail ---" >&2
      tail -5 "$OUT_DIR/$TAG-srcmiss-verify.stderr" >&2
      DIM_SOURCE_MISSING_STATUS="fail"
      DIM_SOURCE_MISSING_DETAIL="verify-pack failed, but diagnostic did not mention source"
      fail=1
    fi
  fi
  return "$fail"
}

# ---------- Run ----------
fails=0
test_idempotent      || fails=$((fails+1))
test_source_missing  || fails=$((fails+1))

echo "" >&2
if [[ $fails -eq 0 ]]; then
  echo "════════ $PACK_NAME: ALL PASS (2/2 dimensions) ════════" >&2
else
  echo "════════ $PACK_NAME: $fails FAIL(S) ════════" >&2
fi
write_results_json
echo "results: $RESULT_JSON" >&2
exit "$fails"
