#!/usr/bin/env bash
# Local development orchestration. Run inside `nix develop`.
set -euo pipefail

ROOT="${DEV_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
FRONTEND="$ROOT/think.greaterchiangmai.com"
BACKEND="$ROOT/think-backend.greaterchiangmai.com"
STATE="$ROOT/.dev"
MYSQL_DATA="$STATE/mysql"
MYSQL_SOCK="$STATE/mysql.sock"
MYSQL_PID="$STATE/mysql.pid"
MYSQL_LOG="$STATE/mysql.log"
# Production export (phpMyAdmin .sql, or the .zip it came in). Seeded into every fresh DB when present.
DB_DUMP="${DEV_DB_DUMP:-$STATE/greaterc_db2024.sql}"

# Must match DB_* in both apps' .env (only the port differs, to avoid clashing with a system MySQL).
DB_PORT="${DEV_DB_PORT:-3307}"
DB_NAME="greaterc_db2024"
DB_USER="greaterc_dbname2024"
DB_PASS="6vK25y6Pz8LA6Z3y78qH"

FRONTEND_PORT="${DEV_FRONTEND_PORT:-8000}"
BACKEND_PORT="${DEV_BACKEND_PORT:-8001}"

# Process env beats .env in Laravel, so this redirects both apps to the local DB without editing .env.
export DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT="$DB_PORT" DB_DATABASE="$DB_NAME" DB_USERNAME="$DB_USER" DB_PASSWORD="$DB_PASS"
export APP_ENV=local APP_DEBUG=true TEST_LOCAL=true

usage() {
    cat <<EOF
Usage: dev <command>

  setup                 composer install both apps + init local MariaDB
  db start|stop|status  manage local MariaDB (data in .dev/mysql, port $DB_PORT); start runs backend migrations
  db shell              mysql client on the local DB
  db import <dump.sql>  drop + recreate the DB from a production dump (schema + data)
  db reset              drop + recreate the DB, seed from .dev/greaterc_db2024.sql[.zip] if present, migrate
  serve frontend        php artisan serve  -> http://127.0.0.1:$FRONTEND_PORT
  serve backend         php artisan serve  -> http://127.0.0.1:$BACKEND_PORT
  up                    db + both apps; Ctrl-C stops everything
  down                  stop db and any background servers
  artisan <app> ...     run artisan for frontend|backend with local env
EOF
}

app_dir() {
    case "$1" in
        frontend) echo "$FRONTEND" ;;
        backend) echo "$BACKEND" ;;
        *) echo "unknown app: $1 (frontend|backend)" >&2; exit 2 ;;
    esac
}

db_running() {
    [ -f "$MYSQL_PID" ] && kill -0 "$(cat "$MYSQL_PID")" 2>/dev/null
}

db_init() {
    [ -d "$MYSQL_DATA" ] && return
    mkdir -p "$MYSQL_DATA"
    mariadb-install-db --datadir="$MYSQL_DATA" --auth-root-authentication-method=normal >"$STATE/install.log" 2>&1
}

db_start() {
    mkdir -p "$STATE"
    db_init
    if db_running; then
        echo "db: already running (pid $(cat "$MYSQL_PID"))"
        db_migrate
        return
    fi
    # Ignore terminal Ctrl-C in the DB so `up` can shut it down cleanly after the app servers.
    (trap '' INT; exec mariadbd --datadir="$MYSQL_DATA" --socket="$MYSQL_SOCK" --pid-file="$MYSQL_PID" \
        --port="$DB_PORT" --bind-address=127.0.0.1 --skip-name-resolve) \
        >"$MYSQL_LOG" 2>&1 &
    for _ in $(seq 1 50); do
        mariadb-admin --socket="$MYSQL_SOCK" -uroot ping >/dev/null 2>&1 && break
        sleep 0.2
    done
    mariadb-admin --socket="$MYSQL_SOCK" -uroot ping >/dev/null 2>&1 || { echo "db: failed to start, see $MYSQL_LOG" >&2; exit 1; }
    mariadb --socket="$MYSQL_SOCK" -uroot <<SQL
CREATE USER IF NOT EXISTS '$DB_USER'@'127.0.0.1' IDENTIFIED BY '$DB_PASS';
CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';
GRANT ALL ON \`$DB_NAME\`.* TO '$DB_USER'@'127.0.0.1', '$DB_USER'@'localhost';
FLUSH PRIVILEGES;
SQL
    if ! mariadb --socket="$MYSQL_SOCK" -uroot -e "USE \`$DB_NAME\`" 2>/dev/null; then
        db_recreate
        db_seed
    fi
    db_migrate
    echo "db: listening on 127.0.0.1:$DB_PORT (pid $(cat "$MYSQL_PID"))"
}

# The schema lives in think-backend/database/migrations; `migrate` is a no-op once applied.
db_migrate() {
    [ -f "$BACKEND/vendor/autoload.php" ] || composer_install
    (cd "$BACKEND" && php artisan migrate --force --no-interaction)
}

db_recreate() {
    mariadb --socket="$MYSQL_SOCK" -uroot -e "DROP DATABASE IF EXISTS \`$DB_NAME\`; CREATE DATABASE \`$DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
}

# Load the production export into the (empty) DB. Unzips once if only the archive is present.
db_seed() {
    if [ ! -f "$DB_DUMP" ] && [ -f "$DB_DUMP.zip" ]; then
        unzip -o -q "$DB_DUMP.zip" "$(basename "$DB_DUMP")" -d "$(dirname "$DB_DUMP")"
    fi
    if [ ! -f "$DB_DUMP" ]; then
        echo "db: no dump at $DB_DUMP, starting with empty tables"
        return
    fi
    mariadb --socket="$MYSQL_SOCK" -uroot "$DB_NAME" <"$DB_DUMP"
    echo "db: seeded from $DB_DUMP"
}

db_stop() {
    db_running || { echo "db: not running"; return; }
    mariadb-admin --socket="$MYSQL_SOCK" -uroot shutdown
    echo "db: stopped"
}

db_status() {
    if db_running; then
        echo "db: running on 127.0.0.1:$DB_PORT (pid $(cat "$MYSQL_PID"))"
        mariadb --socket="$MYSQL_SOCK" -uroot "$DB_NAME" -e "SHOW TABLES"
    else
        echo "db: stopped"
    fi
}

# phpMyAdmin dumps carry bare CREATE TABLEs, so start from an empty database.
db_import() {
    local dump="${1:?usage: dev db import <dump.sql>}"
    [ -f "$dump" ] || { echo "db: no such file: $dump" >&2; exit 1; }
    db_running || db_start
    db_recreate
    mariadb --socket="$MYSQL_SOCK" -uroot "$DB_NAME" <"$dump"
    echo "db: imported $dump"
    mariadb --socket="$MYSQL_SOCK" -uroot "$DB_NAME" -e "SHOW TABLES"
}

db_reset() {
    db_running || db_start
    db_recreate
    db_seed
    db_migrate
    echo "db: reset"
}

composer_install() {
    for app in "$FRONTEND" "$BACKEND"; do
        [ -f "$app/.env" ] || { echo "missing $app/.env" >&2; exit 1; }
        (cd "$app" && composer install --no-interaction)
    done
}

serve() {
    local dir port
    dir="$(app_dir "$1")"
    case "$1" in frontend) port="$FRONTEND_PORT" ;; backend) port="$BACKEND_PORT" ;; esac
    cd "$dir" && exec php artisan serve --no-reload --host=127.0.0.1 --port="$port"
}

up() {
    for app in "$FRONTEND" "$BACKEND"; do
        [ -f "$app/vendor/autoload.php" ] || composer_install
    done
    db_start
    local pids=()
    cleanup() {
        trap - EXIT INT TERM
        set +e
        kill "${pids[@]}" 2>/dev/null
        wait "${pids[@]}" 2>/dev/null
        db_stop
    }
    trap cleanup EXIT INT TERM
    # --no-reload: without it `artisan serve` drops our DB_*/APP_* overrides from the php -S child env.
    (cd "$FRONTEND" && php artisan serve --no-reload --host=127.0.0.1 --port="$FRONTEND_PORT") & pids+=($!)
    (cd "$BACKEND" && php artisan serve --no-reload --host=127.0.0.1 --port="$BACKEND_PORT") & pids+=($!)
    echo "frontend: http://127.0.0.1:$FRONTEND_PORT"
    echo "backend:  http://127.0.0.1:$BACKEND_PORT"
    wait
}

down() {
    pkill -f "artisan serve --no-reload --host=127.0.0.1 --port=($FRONTEND_PORT|$BACKEND_PORT)" 2>/dev/null || true
    db_stop
}

cmd="${1:-help}"; shift || true
case "$cmd" in
    setup) composer_install; db_start ;;
    db)
        sub="${1:-status}"; shift || true
        case "$sub" in
            start) db_start ;;
            stop) db_stop ;;
            status) db_status ;;
            shell) db_running || db_start; exec mariadb --socket="$MYSQL_SOCK" -uroot "$DB_NAME" ;;
            reset) db_reset ;;
            import) db_import "$@" ;;
            *) usage; exit 2 ;;
        esac ;;
    serve) serve "${1:?usage: dev serve frontend|backend}" ;;
    up) up ;;
    down) down ;;
    artisan) dir="$(app_dir "${1:?usage: dev artisan frontend|backend ...}")"; shift; cd "$dir" && exec php artisan "$@" ;;
    help|-h|--help) usage ;;
    *) usage; exit 2 ;;
esac
