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

REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
SOURCE="${REPO_ROOT}/sql/pgcontext--0.2.0.sql"
OPTIONS_SOURCE="${REPO_ROOT}/crates/context-pg/src/hnsw_am/options.rs"
SETTINGS_SOURCE="${REPO_ROOT}/crates/context-pg/src/settings.rs"
POLICY_SOURCE="${REPO_ROOT}/crates/context-core/src/policy.rs"
CATALOG_SOURCE="${REPO_ROOT}/crates/context-pg/src/contract/contract_catalog_objects.rs"
OUTPUT="${REPO_ROOT}/docs/user_guide/sql_object_inventory.md"
CHECK_ONLY=false

if [[ "${1:-}" == "--check" ]]; then
  CHECK_ONLY=true
elif [[ $# -ne 0 ]]; then
  echo "usage: $0 [--check]" >&2
  exit 2
fi

for path in "${SOURCE}" "${OPTIONS_SOURCE}" "${SETTINGS_SOURCE}" \
  "${POLICY_SOURCE}" "${CATALOG_SOURCE}"; do
  if [[ ! -f "${path}" ]]; then
    echo "missing inventory source: ${path}" >&2
    exit 1
  fi
done

tmp="$(mktemp "${TMPDIR:-/tmp}/pgcontext-sql-inventory.XXXXXX")"
source_reloptions="$(mktemp "${TMPDIR:-/tmp}/pgcontext-source-reloptions.XXXXXX")"
documented_reloptions="$(mktemp "${TMPDIR:-/tmp}/pgcontext-documented-reloptions.XXXXXX")"
source_quantization="$(mktemp "${TMPDIR:-/tmp}/pgcontext-source-quantization.XXXXXX")"
documented_quantization="$(mktemp "${TMPDIR:-/tmp}/pgcontext-documented-quantization.XXXXXX")"
trap 'rm -f "${tmp}" "${source_reloptions}" "${documented_reloptions}" "${source_quantization}" "${documented_quantization}"' EXIT

count() {
  grep -Ec "$1" "${SOURCE}" || true
}

unique_type_count() {
  awk '/^CREATE TYPE / { name = $3; gsub(/[;(]/, "", name); seen[name] = 1 }
       END { for (name in seen) count++; print count + 0 }' "${SOURCE}"
}

operator_count() {
  awk '/^CREATE OPERATOR / && $3 != "CLASS" { count++ }
       END { print count + 0 }' "${SOURCE}"
}

{
  printf '%s\n\n' '# Installed SQL Object And Option Inventory'
  printf '%s\n' 'Generated by `scripts/generate-sql-object-inventory.sh` from'
  printf '%s\n' '`sql/pgcontext--0.2.0.sql` and the HNSW reloptions callback. Run the generator'
  printf '%s\n' 'after changing the installed SQL contract, or use'
  printf '%s\n' '`scripts/generate-sql-object-inventory.sh --check` to verify it. Do not edit'
  printf '%s\n\n' 'this file by hand.'
  printf '%s\n\n' 'The SQL contract registry owns lifecycle classification; this inventory pins the installed object and option shape consumed by the capability contract.'
  printf 'Full SQL artifact SHA-256: `%s`\n\n' "$(shasum -a 256 "${SOURCE}" | awk '{ print $1 }')"
  printf '%s\n\n' 'The artifact fingerprint covers every object declaration, function result shape, cast method/context, operator identity, and opclass strategy. `contract_registry` separately compares installed functions and catalog objects bidirectionally, including typed operator and access-method/input-type opclass identities.'
  printf '%s\n' '| Object kind | Installed count |'
  printf '%s\n' '|---|---:|'
  printf '| Types | %s |\n' "$(unique_type_count)"
  printf '| Schemas | %s |\n' "$(count '^CREATE SCHEMA ')"
  printf '| Functions | %s |\n' "$(count '^CREATE  FUNCTION |^CREATE FUNCTION ')"
  printf '| Tables | %s |\n' "$(count '^CREATE TABLE ')"
  printf '| Views | %s |\n' "$(count '^CREATE VIEW ')"
  printf '| Triggers | %s |\n' "$(count '^CREATE TRIGGER ')"
  printf '| Casts | %s |\n' "$(count '^CREATE CAST ')"
  printf '| Operators | %s |\n' "$(operator_count)"
  printf '| Operator classes | %s |\n' "$(count '^CREATE OPERATOR CLASS ')"
  printf '| Aggregates | %s |\n' "$(count '^CREATE AGGREGATE ')"
  printf '| Access methods | %s |\n\n' "$(count '^CREATE ACCESS METHOD ')"

  printf '%s\n\n' '## Types'
  awk '/^CREATE TYPE / { name = $3; gsub(/[;(]/, "", name); print "- `" name "`" }' "${SOURCE}" | sort -u

  printf '\n%s\n\n' '## Schemas, Tables, Views, And Triggers'
  awk '
    /^CREATE SCHEMA / { print "- schema `pgcontext`" }
    /^CREATE TABLE / { name = $3; gsub(/[;(]/, "", name); print "- table `" name "`" }
    /^CREATE VIEW / { name = $3; gsub(/[;(]/, "", name); print "- view `" name "`" }
    /^CREATE TRIGGER / { name = $3; gsub(/[;(]/, "", name); print "- trigger `" name "`" }
  ' "${SOURCE}" | sort -u

  printf '\n%s\n\n' '## Function Names'
  printf '%s\n\n' 'Overload argument and result identities are pinned by the artifact fingerprint and bidirectional `contract_registry` test.'
  awk '/^CREATE  FUNCTION / || /^CREATE FUNCTION / {
         name = $3; sub(/\(.*/, "", name); print "- `" name "`"
       }' "${SOURCE}" | sort -u

  printf '\n%s\n\n' '## Cast Identities'
  awk '/^CREATE CAST / { text = $0; sub(/^CREATE CAST /, "", text); print "- `" text "`" }' "${SOURCE}" | sort -u

  printf '\n%s\n\n' '## Typed Operator Registry Entries'
  printf '%s\n' '| Entry | Kind | Applies to | Lifecycle |'
  printf '%s\n' '|---|---|---|---|'
  awk '/pgcontext_operator\(/ || /pgcontext_operator_class\(/ {
         kind = ($0 ~ /pgcontext_operator_class\(/) ? "operator class" : "operator"
         split($0, q, "\"")
         life = "—"
         if ($0 ~ /SqlLifecycle::Stable/) life = "Stable"
         else if ($0 ~ /SqlLifecycle::Experimental/) life = "Experimental"
         name = q[2]; applies = q[4]
         gsub(/\|/, "\\|", name); gsub(/\|/, "\\|", applies)
         printf "| `%s` | %s | %s | %s |\n", name, kind, applies, life
       }' "${CATALOG_SOURCE}"

  printf '\n%s\n\n' '## Aggregate Identities'
  awk '/^CREATE AGGREGATE / { text = $0; sub(/^CREATE AGGREGATE /, "", text); print "- `" text "`" }' "${SOURCE}" | sort -u

  printf '\n%s\n\n' '## Access Methods And Operator Classes'
  awk '
    /^CREATE ACCESS METHOD / { name = $4; gsub(/[;(]/, "", name); print "- access method `" name "`" }
    /^CREATE OPERATOR CLASS / { name = $4; gsub(/[;(]/, "", name); print "- operator class `" name "`" }
  ' "${SOURCE}" | sort -u

  printf '\n%s\n\n' '## `pgcontext_hnsw` Index Reloptions'
  printf '%s\n' '| Option | Accepted shape | Lifecycle |'
  printf '%s\n' '|---|---|---|'
  printf '%s\n' '| `quantization` | `none`, `scalar`, `sq8`, or `pq` | Experimental until quantized serving is complete |'
  printf '%s\n' '| `scalar_min` | finite lower bound | Experimental |'
  printf '%s\n' '| `scalar_max` | finite upper bound greater than `scalar_min` | Experimental |'
  printf '%s\n' '| `scalar_levels` | integer from 2 through 256 | Experimental |'
  printf '%s\n' '| `pq_subvector_dimensions` | positive divisor of vector dimensions | Experimental |'
  printf '%s\n' '| `pq_codebooks` | validated JSON codebook array | Experimental |'

  printf '\n%s\n\n' '## HNSW GUCs'
  printf '%s\n' '| Setting | Default | Lifecycle |'
  printf '%s\n' '|---|---:|---|'
  printf '%s\n' '| `pgcontext.hnsw_m` | `16` | Experimental HNSW serving policy |'
  printf '%s\n' '| `pgcontext.hnsw_ef_construction` | `64` | Experimental HNSW build policy |'
  printf '%s\n' '| `pgcontext.hnsw_ef_search` | `32` | Experimental HNSW search policy |'
  printf '%s\n' '| `pgcontext.hnsw_candidate_budget` | `32` | Experimental filtered/iterative policy |'
  printf '%s\n' '| `pgcontext.hnsw_iterative_expansion_limit` | `10000` | Experimental bounded expansion policy |'
  printf '%s\n' '| `pgcontext.hnsw_recall_threshold` | `0.95` | Experimental recall-health policy |'
} >"${tmp}"

for option in quantization scalar_min scalar_max scalar_levels pq_subvector_dimensions pq_codebooks; do
  if ! grep -Fq "c\"${option}\"" "${OPTIONS_SOURCE}"; then
    echo "documented HNSW reloption is not registered: ${option}" >&2
    exit 1
  fi
done

awk '
  /let parse_elements = \[/ { in_options = 1; next }
  in_options && /^    \];/ { in_options = 0 }
  in_options && match($0, /c"[^"]+"/) {
    option = substr($0, RSTART + 2, RLENGTH - 3)
    print option
  }
' "${OPTIONS_SOURCE}" | sort -u >"${source_reloptions}"
printf '%s\n' quantization scalar_min scalar_max scalar_levels \
  pq_subvector_dimensions pq_codebooks | sort -u >"${documented_reloptions}"
if ! diff -u "${documented_reloptions}" "${source_reloptions}" >&2; then
  echo "HNSW reloption inventory is not exhaustive" >&2
  exit 1
fi

awk '
  /static mut HNSW_QUANTIZATION_MEMBERS/ { in_members = 1; next }
  in_members && /^\];/ { in_members = 0 }
  in_members && match($0, /c"[^"]+"/) {
    member = substr($0, RSTART + 2, RLENGTH - 3)
    print member
  }
' "${OPTIONS_SOURCE}" | sort -u >"${source_quantization}"
printf '%s\n' none scalar sq8 pq | sort -u >"${documented_quantization}"
if ! diff -u "${documented_quantization}" "${source_quantization}" >&2; then
  echo "HNSW quantization enum inventory is not exhaustive" >&2
  exit 1
fi

for fragment in \
  'HNSW_SCALAR_MIN_LEVELS: i32 = 2;' \
  'HNSW_SCALAR_MAX_LEVELS: i32 = 256;' \
  'c"none".as_ptr()' 'c"scalar".as_ptr()' 'c"sq8".as_ptr()' 'c"pq".as_ptr()' \
  'scalar_min and scalar_max must be finite' \
  'scalar_min must be less than scalar_max' \
  'pq_subvector_dimensions must be positive when quantization is pq' \
  'pq_codebooks must be a JSON array'; do
  if ! grep -Fq "${fragment}" "${OPTIONS_SOURCE}"; then
    echo "documented HNSW reloption rule is not source-backed: ${fragment}" >&2
    exit 1
  fi
done

for guc in \
  pgcontext.hnsw_m pgcontext.hnsw_ef_construction pgcontext.hnsw_ef_search \
  pgcontext.hnsw_candidate_budget pgcontext.hnsw_iterative_expansion_limit \
  pgcontext.hnsw_recall_threshold; do
  if ! grep -Fq "c\"${guc}\"" "${SETTINGS_SOURCE}"; then
    echo "documented HNSW GUC is not registered: ${guc}" >&2
    exit 1
  fi
done


for fragment in \
  'DEFAULT_HNSW_M: usize = 16;' \
  'DEFAULT_HNSW_EF_CONSTRUCTION: usize = 64;' \
  'DEFAULT_HNSW_EF_SEARCH: usize = 32;' \
  'DEFAULT_HNSW_CANDIDATE_BUDGET: usize = DEFAULT_HNSW_EF_SEARCH;' \
  'MAX_RECALL_CHECK_POINT_IDS: usize = 10_000;' \
  'DEFAULT_HNSW_RECALL_THRESHOLD: f64 = 0.95;'; do
  if ! grep -Fq "${fragment}" "${POLICY_SOURCE}"; then
    echo "documented HNSW GUC default is not source-backed: ${fragment}" >&2
    exit 1
  fi
done

if "${CHECK_ONLY}"; then
  if [[ ! -f "${OUTPUT}" ]]; then
    echo "missing generated SQL object inventory: ${OUTPUT}" >&2
    exit 1
  fi
  diff -u "${OUTPUT}" "${tmp}"
else
  cp "${tmp}" "${OUTPUT}"
fi
