#!/usr/bin/env bash
#
# A Room entity may not change without the database version changing with it.
#
# ## The incident that motivated this
#
# Written 2026-08-18, immediately after the check it replaces was proved to be
# green over exactly the failure it claimed to catch.
#
# `core/database/src/test/.../SchemaTest.kt` compares the committed schema JSON
# against the SQL the current code produces, and it looked like a drift guard.
# It is not one, for a reason that is invisible until you try it: **Room
# regenerates the schema export during compilation**, overwriting
# `schemas/<db>/1.json` in place. So by the time any test runs, the "committed"
# file already describes the changed entity, both sides of the comparison agree,
# and the test passes.
#
# It was proved by adding a column to `PeriodRecordEntity` without touching
# `PeriodDatabase.VERSION`. The suite stayed green and the committed schema
# silently gained the column. On a device that is a crash on update, in front of
# a user, after shipping — `Room cannot verify the data integrity`.
#
# GUARDS.md §1 exists for precisely this: a guard nobody has watched fail is not
# yet evidence.
#
# ## What this does instead
#
# It asks git, which Room cannot overwrite. After a build has regenerated the
# exports, an EXISTING schema file that now differs from its committed contents
# means an entity changed under a version that has already shipped. A NEW file
# — `2.json` appearing — is a version bump, which is the correct way to change a
# schema and is allowed.
#
#   bash scripts/schema-guard.sh              # assumes exports are current
#   bash scripts/schema-guard.sh --build      # regenerate them first
#
# ## Exit codes
#
#   0  every committed schema file is unchanged, and one exists for the current
#      version
#   1  a schema file changed without a version bump, or none exists for the
#      current version
#   2  NOTHING WAS CHECKED — no schema directory, or git could not be read.
#      **Two is not a pass** and no hook or CI step may treat it as one.

set -uo pipefail

cd "$(git rev-parse --show-toplevel 2>/dev/null)" || {
  printf '\033[1mschema-guard:\033[0m not a git repository — nothing could be checked.\n' >&2
  exit 2
}

say() { printf '\033[1mschema-guard:\033[0m %s\n' "$*" >&2; }

SCHEMA_DIR="core/database/schemas"
VERSION_SRC="core/database/src/main/kotlin/dev/privacyllc/period/core/database/PeriodDatabase.kt"

if [ "${1:-}" = "--build" ]; then
  say "regenerating the schema exports…"
  ./gradlew --quiet :core:database:assembleDebug >/dev/null 2>&1 || {
    say "the build failed, so the exports are not current and nothing was checked."
    exit 2
  }
fi

if [ ! -d "$SCHEMA_DIR" ]; then
  say "$SCHEMA_DIR does not exist. Either the database module moved or the"
  say "schema export was switched off — both mean this check did not run."
  exit 2
fi

# The declared version, read from the source rather than assumed.
version=$(grep -oP 'const val VERSION\s*=\s*\K[0-9]+' "$VERSION_SRC" 2>/dev/null)
if [ -z "$version" ]; then
  say "could not read PeriodDatabase.VERSION from $VERSION_SRC — nothing checked."
  exit 2
fi

expected="$SCHEMA_DIR/dev.privacyllc.period.core.database.PeriodDatabase/${version}.json"
if [ ! -f "$expected" ]; then
  say "database version is $version but $expected does not exist."
  say "The version was bumped without exporting the schema, so no migration"
  say "into it can ever be validated. Build once and commit the export."
  exit 1
fi

# Tracked files under the schema directory whose contents differ from HEAD.
if ! modified=$(git diff --name-only -- "$SCHEMA_DIR" 2>/dev/null); then
  say "could not read git status for $SCHEMA_DIR — nothing checked."
  exit 2
fi
staged=$(git diff --cached --name-only --diff-filter=M -- "$SCHEMA_DIR" 2>/dev/null)
changed=$(printf '%s\n%s\n' "$modified" "$staged" | grep -v '^$' | sort -u)

if [ -n "$changed" ]; then
  say "a schema file that is already committed has CHANGED:"
  printf '  %s\n' $changed >&2
  say ""
  say "That means an entity changed under database version $version, which has"
  say "already been exported. Room overwrote the export during the build, so"
  say "nothing else will notice — including the unit tests."
  say ""
  say "Either revert the entity change, or bump PeriodDatabase.VERSION, write"
  say "the migration, and let a NEW schema file be exported beside this one."
  say "Then add its row to the migration table in docs/architecture/README.md,"
  say "in the same commit."
  exit 1
fi

# An untracked file here is a new version, which is the correct way to change a
# schema. Reported rather than passed over silently: it needs a migration.
untracked=$(git ls-files --others --exclude-standard -- "$SCHEMA_DIR" 2>/dev/null)
if [ -n "$untracked" ]; then
  say "new schema file(s), so this is a version bump:"
  printf '  %s\n' $untracked >&2
  say "Commit them, write the migration, and add the row to"
  say "docs/architecture/README.md's migration table in the same commit."
fi

count=$(find "$SCHEMA_DIR" -name '*.json' | wc -l)
say "ok — $count committed schema file(s) unchanged; version $version is exported."
exit 0
