#!/usr/bin/env bash
# Regenerate sql/complete-database-setup.sql from the current contents of
# sql/migrations/. Spins up a throwaway MariaDB instance, applies every
# YYYY.MM.N migration in version order on top of the existing canonical
# (or empty schema if --from-scratch is passed), dumps the result, and
# overwrites complete-database-setup.sql with a normalized canonical form.
#
# Modes:
#   regenerate-canonical-schema.sh                # update the file
#   regenerate-canonical-schema.sh --check        # exit 1 if file would change (CI lint)
#   regenerate-canonical-schema.sh --from-scratch # ignore current canonical, rebuild from migrations alone
#
# Backend:
#   Native (mariadbd + mariadb-dump on PATH) is preferred for speed.
#   Falls back to a docker mariadb:10.11 container, which is what the
#   build server has. If neither is available, exits 0 with a warning
#   (so the lint never fails the build solely for missing tooling).

set -euo pipefail

MODE="regenerate"
FROM_SCRATCH=false
for arg in "$@"; do
    case "$arg" in
        --check) MODE="check" ;;
        --from-scratch) FROM_SCRATCH=true ;;
    esac
done

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CANONICAL="$ROOT/sql/complete-database-setup.sql"
TMP_DIR="$(mktemp -d)"

# Pick a backend. Native if mariadbd available; otherwise docker.
BACKEND=""
if command -v mariadbd >/dev/null 2>&1 && command -v mariadb-dump >/dev/null 2>&1 && command -v mysql_install_db >/dev/null 2>&1; then
    BACKEND="native"
elif command -v docker >/dev/null 2>&1; then
    BACKEND="docker"
else
    echo "WARNING: neither mariadbd nor docker available; skipping canonical-schema check."
    exit 0
fi

CONTAINER=""
SOCK="$TMP_DIR/mariadb.sock"
PID="$TMP_DIR/mariadb.pid"
DATA="$TMP_DIR/data"

cleanup() {
    if [ "$BACKEND" = "docker" ] && [ -n "$CONTAINER" ]; then
        docker rm -f "$CONTAINER" >/dev/null 2>&1 || true
    fi
    if [ "$BACKEND" = "native" ] && [ -f "$PID" ]; then
        kill "$(cat "$PID")" 2>/dev/null || true
        sleep 1
    fi
    rm -rf "$TMP_DIR"
}
trap cleanup EXIT

run_sql() {
    # stdin = SQL; $1 = optional database name
    local db="${1:-whp}"
    if [ "$BACKEND" = "native" ]; then
        mariadb --socket="$SOCK" "$db"
    else
        docker exec -i "$CONTAINER" mariadb -uroot -plinttest "$db"
    fi
}

run_sql_cmd() {
    # $1 = SQL command; runs against the server (not scoped to a DB)
    if [ "$BACKEND" = "native" ]; then
        mariadb --socket="$SOCK" -e "$1"
    else
        docker exec "$CONTAINER" mariadb -uroot -plinttest -e "$1"
    fi
}

# Bring up the backend.
if [ "$BACKEND" = "native" ]; then
    mkdir -p "$DATA" /run/mysqld 2>/dev/null || true
    mysql_install_db --datadir="$DATA" --user="$(id -un)" >/dev/null 2>&1
    mariadbd --datadir="$DATA" --socket="$SOCK" --pid-file="$PID" \
             --skip-networking --user="$(id -un)" --bind-address=127.0.0.1 \
             >"$TMP_DIR/mariadb.log" 2>&1 &
    for i in $(seq 1 30); do
        [ -S "$SOCK" ] && break
        sleep 0.5
    done
else
    CONTAINER="whp-canonical-lint-$$"
    docker run -d --name "$CONTAINER" \
        -e MARIADB_ROOT_PASSWORD=linttest \
        mariadb:10.11 >/dev/null
    # Wait for mariadb to be ready.
    for i in $(seq 1 60); do
        if docker exec "$CONTAINER" mariadb -uroot -plinttest -e "SELECT 1" >/dev/null 2>&1; then
            break
        fi
        sleep 1
    done
fi

run_sql_cmd "CREATE DATABASE whp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

# Seed: existing canonical (so the lint succeeds when canonical is correct
# and migrations are idempotent on top of it) or empty (--from-scratch).
if ! $FROM_SCRATCH && [ -f "$CANONICAL" ]; then
    run_sql whp < "$CANONICAL" >/dev/null
fi

# Apply every versioned migration in order, then staging/ on top. Staging
# is included so an in-flight migration that hasn't shipped yet still
# shows up in the canonical -- otherwise build-release's --check would
# report drift right after the migration is added.
for d in $(ls -d "$ROOT"/sql/migrations/[0-9]*/ 2>/dev/null | sort -V); do
    for f in $(find "$d" -maxdepth 1 -name "*.sql" | sort); do
        # Migrations are idempotent so most "failures" are duplicate-column
        # / index-already-exists. Don't abort.
        run_sql whp < "$f" >>"$TMP_DIR/migrate.log" 2>&1 || true
    done
done
if [ -d "$ROOT/sql/migrations/staging" ]; then
    for f in $(find "$ROOT/sql/migrations/staging" -maxdepth 1 -name "*.sql" | sort); do
        run_sql whp < "$f" >>"$TMP_DIR/migrate.log" 2>&1 || true
    done
fi

# Dump the result into normalized canonical form.
if [ "$BACKEND" = "native" ]; then
    mariadb-dump --socket="$SOCK" --no-data --skip-add-drop-table \
                 --skip-comments --no-tablespaces whp \
        > "$TMP_DIR/dumped.sql"
else
    docker exec "$CONTAINER" mariadb-dump -uroot -plinttest \
        --no-data --skip-add-drop-table --skip-comments --no-tablespaces whp \
        > "$TMP_DIR/dumped.sql"
fi

python3 - "$TMP_DIR/dumped.sql" > "$TMP_DIR/canonical.sql" <<'PYEOF'
import sys, re
with open(sys.argv[1]) as f:
    body = f.read()
body = re.sub(r'/\*M!999999.*?\*/;\n', '', body, flags=re.MULTILINE)
body = re.sub(r'utf8mb4_general_ci', 'utf8mb4_unicode_ci', body)
# AUTO_INCREMENT=N is mariadb-dump preserving the current counter value;
# it varies run-to-run and would falsely trip the lint. Strip it.
body = re.sub(r' AUTO_INCREMENT=\d+', '', body)
banner = """-- WHP canonical database schema.
-- Generated by applying every versioned migration to a fresh MariaDB.
-- DO NOT manually edit individual statements unless you ALSO add a
-- corresponding migration under sql/migrations/staging/.
-- Regenerate: bash scripts/regenerate-canonical-schema.sh

USE whp;
ALTER DATABASE whp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

"""
sys.stdout.write(banner + body)
PYEOF

if [ "$MODE" = "check" ]; then
    if ! diff -q "$CANONICAL" "$TMP_DIR/canonical.sql" >/dev/null 2>&1; then
        echo "ERROR: sql/complete-database-setup.sql is stale relative to sql/migrations/."
        echo "Run: bash scripts/regenerate-canonical-schema.sh"
        echo "--- diff (first 50 lines) ---"
        diff "$CANONICAL" "$TMP_DIR/canonical.sql" | head -50 || true
        exit 1
    fi
    echo "OK: canonical schema in sync with migrations."
    exit 0
fi

cp "$TMP_DIR/canonical.sql" "$CANONICAL"
echo "Updated $CANONICAL ($(wc -l < "$CANONICAL") lines, $(grep -c 'CREATE TABLE' "$CANONICAL") tables)."
