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

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"

APP_ENV_FILE="$ROOT_DIR/.env"
APP_EXAMPLE_FILE="$ROOT_DIR/.env.example"
SKIP_START="${SKIP_START:-false}"
SKIP_SYSTEM_PACKAGES="${SKIP_SYSTEM_PACKAGES:-false}"

log() {
  printf "\n[%s] %s\n" "$(date '+%H:%M:%S')" "$1"
}

warn() {
  printf "\n[WARN] %s\n" "$1"
}

fail() {
  printf "\n[ERROR] %s\n" "$1"
  exit 1
}

require_linux() {
  if [[ "$(uname -s)" != "Linux" ]]; then
    fail "This installer is intended for Linux servers."
  fi
}

run_with_sudo() {
  if [[ "$(id -u)" -eq 0 ]]; then
    "$@"
  elif command -v sudo >/dev/null 2>&1; then
    sudo "$@"
  else
    fail "sudo is required to install system packages."
  fi
}

install_system_packages() {
  if [[ "$SKIP_SYSTEM_PACKAGES" == "true" ]]; then
    warn "Skipping system package installation because SKIP_SYSTEM_PACKAGES=true."
    return
  fi

  if command -v apt-get >/dev/null 2>&1; then
    log "Installing base system packages"
    run_with_sudo apt-get update
    run_with_sudo apt-get install -y ca-certificates curl git build-essential
  else
    warn "apt-get was not found. Install curl/git/build-essential manually if needed."
  fi
}

ensure_node() {
  if command -v node >/dev/null 2>&1; then
    local current_major
    current_major="$(node -p "process.versions.node.split('.')[0]")"
    if [[ "$current_major" -eq 18 ]]; then
      log "Node.js already installed: $(node -v)"
      return
    fi
  fi

  if ! command -v apt-get >/dev/null 2>&1; then
    fail "Node.js 18.x is required. Install it manually on this distro."
  fi

  log "Installing Node.js 18.x"
  run_with_sudo bash -c "curl -fsSL https://deb.nodesource.com/setup_18.x | bash -"
  run_with_sudo apt-get install -y nodejs
  log "Node.js installed: $(node -v)"
}

ensure_npm() {
  command -v npm >/dev/null 2>&1 || fail "npm is missing after Node.js installation."
  log "npm available: $(npm -v)"
}

prepare_env() {
  log "Preparing environment file"
  [[ -f "$APP_EXAMPLE_FILE" ]] || fail ".env.example file is missing."
  node scripts/setup.js
  node scripts/check-env.js
}

extract_db_json() {
  node - <<'NODE'
const fs = require("fs");
const path = require("path");
const dotenv = require("dotenv");
const envPath = path.resolve(process.cwd(), ".env");
const parsed = dotenv.parse(fs.readFileSync(envPath));
const url = new URL(parsed.DATABASE_URL.replace(/^"|"$/g, ""));
const payload = {
  databaseUrl: parsed.DATABASE_URL.replace(/^"|"$/g, ""),
  dbName: url.pathname.replace(/^\//, ""),
  dbUser: decodeURIComponent(url.username),
  dbPassword: decodeURIComponent(url.password),
  dbHost: url.hostname,
  dbPort: Number(url.port || 3306),
  bootstrapEnabled: (parsed.DB_BOOTSTRAP_ENABLED || "false") === "true",
  bootstrapRootUser: parsed.DB_BOOTSTRAP_ROOT_USER || "root",
  bootstrapRootPassword: parsed.DB_BOOTSTRAP_ROOT_PASSWORD || "",
  bootstrapHost: parsed.DB_BOOTSTRAP_HOST || url.hostname,
  bootstrapPort: Number(parsed.DB_BOOTSTRAP_PORT || url.port || 3306)
};
process.stdout.write(JSON.stringify(payload));
NODE
}

bootstrap_database_if_enabled() {
  local db_json db_name db_user db_password bootstrap_enabled root_user root_password bootstrap_host bootstrap_port
  db_json="$(extract_db_json)"
  db_name="$(node -e "const data = JSON.parse(process.argv[1]); console.log(data.dbName);" "$db_json")"
  db_user="$(node -e "const data = JSON.parse(process.argv[1]); console.log(data.dbUser);" "$db_json")"
  db_password="$(node -e "const data = JSON.parse(process.argv[1]); console.log(data.dbPassword);" "$db_json")"
  bootstrap_enabled="$(node -e "const data = JSON.parse(process.argv[1]); console.log(data.bootstrapEnabled);" "$db_json")"
  root_user="$(node -e "const data = JSON.parse(process.argv[1]); console.log(data.bootstrapRootUser);" "$db_json")"
  root_password="$(node -e "const data = JSON.parse(process.argv[1]); console.log(data.bootstrapRootPassword);" "$db_json")"
  bootstrap_host="$(node -e "const data = JSON.parse(process.argv[1]); console.log(data.bootstrapHost);" "$db_json")"
  bootstrap_port="$(node -e "const data = JSON.parse(process.argv[1]); console.log(data.bootstrapPort);" "$db_json")"

  if [[ "$bootstrap_enabled" != "true" ]]; then
    warn "Automatic database bootstrap is disabled. Set DB_BOOTSTRAP_ENABLED=true to let the installer create the DB/user."
    return
  fi

  command -v mysql >/dev/null 2>&1 || fail "mysql client is required when DB_BOOTSTRAP_ENABLED=true."

  log "Bootstrapping MariaDB/MySQL database and user"
  MYSQL_PWD="$root_password" mysql \
    --host="$bootstrap_host" \
    --port="$bootstrap_port" \
    --user="$root_user" \
    --protocol=tcp \
    -e "CREATE DATABASE IF NOT EXISTS \`$db_name\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER IF NOT EXISTS '$db_user'@'%' IDENTIFIED BY '$db_password'; GRANT ALL PRIVILEGES ON \`$db_name\`.* TO '$db_user'@'%'; FLUSH PRIVILEGES;"
}

install_dependencies() {
  log "Installing Node.js dependencies"
  npm install
}

run_database_checks() {
  log "Checking database connection"
  node scripts/init-database.js
}

run_app_bootstrap() {
  log "Running Prisma migrations"
  npx prisma migrate deploy
  log "Running seed"
  node prisma/seed.js
  log "Ensuring superadmin account exists"
  node scripts/create-admin.js
}

start_application() {
  if [[ "$SKIP_START" == "true" ]]; then
    warn "Skipping application start because SKIP_START=true."
    return
  fi

  log "Starting application"
  npm start
}

main() {
  require_linux
  install_system_packages
  ensure_node
  ensure_npm
  prepare_env
  bootstrap_database_if_enabled
  install_dependencies
  run_database_checks
  run_app_bootstrap
  start_application
}

main "$@"
