#!/usr/bin/env bash
#
# Runs the backend suite against a real MySQL 8 instead of SQLite.
#
# The suite defaults to SQLite in memory because it is fast, but SQLite is
# permissive in ways MySQL is not: it ignores column signedness, treats ENUM as
# free text, and has no InnoDB index key limit. A green SQLite run is therefore
# not evidence that the schema works on the deployment target.
#
# This script stands up a throwaway MySQL 8 in a temporary directory, runs the
# suite against it, and tears it down. It needs no root and does not touch any
# MySQL already installed on the machine.
#
# Usage:  ./scripts/test-mysql.sh [phpunit args...]

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
MYSQLD="${MYSQLD:-/usr/sbin/mysqld}"

# A free port is found rather than assumed, so an interrupted previous run or a
# second concurrent run does not make the script fail with a confusing bind
# error. Override with MYSQL_TEST_PORT when a fixed port is wanted.
find_free_port() {
  local candidate
  for candidate in $(seq 3399 3450); do
    if ! ss -ltn 2>/dev/null | grep -q ":$candidate "; then
      printf '%s' "$candidate"
      return 0
    fi
  done
  return 1
}

PORT="${MYSQL_TEST_PORT:-$(find_free_port)}"
[ -n "$PORT" ] || fail 'No free port between 3399 and 3450.'

# Unix sockets are capped at 107 characters, which a temp path under a long
# working directory will exceed -- so the socket goes somewhere short. Keyed by
# port so two concurrent runs cannot contend for the same socket or lock file.
SOCKET="${MYSQL_TEST_SOCKET:-/tmp/dsr-test-mysql-$PORT.sock}"
DATADIR="$(mktemp -d -t domainsansar-mysql-XXXXXX)"

info() { printf '\033[1;34m==>\033[0m %s\n' "$1"; }
fail() { printf '\033[1;31mxx\033[0m %s\n' "$1" >&2; exit 1; }

[ -x "$MYSQLD" ] || fail "mysqld not found at $MYSQLD. Set MYSQLD to its path."

# Clear anything a previously interrupted run left behind.
rm -f "$SOCKET" "$SOCKET.lock"

cleanup() {
  if [ -f "$DATADIR/mysqld.pid" ]; then
    info 'Stopping the temporary MySQL'
    kill "$(cat "$DATADIR/mysqld.pid")" 2>/dev/null || true
    # Give InnoDB a moment to shut down cleanly before the data goes.
    for _ in $(seq 1 20); do
      [ -f "$DATADIR/mysqld.pid" ] || break
      sleep 0.5
    done
  fi
  rm -rf "$DATADIR"
  # The lock file matters as much as the socket: mysqld refuses to start if a
  # stale one is left behind, which turns one interrupted run into a permanently
  # broken script.
  rm -f "$SOCKET" "$SOCKET.lock"
}
trap cleanup EXIT

info "Initialising MySQL in $DATADIR"
"$MYSQLD" --initialize-insecure --datadir="$DATADIR/data" \
  --log-error="$DATADIR/init.log" >/dev/null 2>&1 \
  || { cat "$DATADIR/init.log"; fail 'MySQL initialisation failed.'; }

info "Starting MySQL on port $PORT"
"$MYSQLD" --datadir="$DATADIR/data" --socket="$SOCKET" --port="$PORT" \
  --pid-file="$DATADIR/mysqld.pid" --log-error="$DATADIR/error.log" \
  --tmpdir="$DATADIR" --bind-address=127.0.0.1 --mysqlx=0 >/dev/null 2>&1 &

for _ in $(seq 1 60); do
  if mysqladmin --socket="$SOCKET" -u root ping 2>/dev/null | grep -q alive; then
    break
  fi
  sleep 1
done

mysqladmin --socket="$SOCKET" -u root ping 2>/dev/null | grep -q alive \
  || { tail -20 "$DATADIR/error.log"; fail 'MySQL did not start.'; }

info 'Creating the test database'
mysql --socket="$SOCKET" -u root <<'SQL'
CREATE DATABASE domainsansar_test CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
SQL

info "MySQL $(mysql --socket="$SOCKET" -u root -N -B -e 'select version()')"
info 'Running the suite'

# Exported so they win over the sqlite defaults in phpunit.xml: PHPUnit does not
# overwrite an environment variable that is already set.
cd "$ROOT"
DB_CONNECTION=mysql \
DB_HOST=127.0.0.1 \
DB_PORT="$PORT" \
DB_SOCKET="$SOCKET" \
DB_DATABASE=domainsansar_test \
DB_USERNAME=root \
DB_PASSWORD= \
CACHE_STORE=array \
SESSION_DRIVER=array \
QUEUE_CONNECTION=sync \
MAIL_MAILER=array \
./vendor/bin/phpunit "$@"
