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

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
LOCAL_ENV_FILE="$ROOT_DIR/.env"
DELIVERY_ENV_FILE="$ROOT_DIR/deploy/env/.env"
PNPM_VERSION=""
DRY_RUN=0
NO_START=0
PROD_MODE=0
WITH_PYTHON=0
WITH_PLAYWRIGHT=0
RESET_DB=0
ENV_FILE=""

usage() {
  cat <<'EOF'
Usage:
  bash scripts/install.sh [options]

Options:
  --prod             Build and start through scripts/deploy.sh for production-like use.
  --no-start         Install dependencies but do not start the app.
  --with-python      Install uv and Python 3.13. Disabled by default.
  --with-playwright  Install Playwright Chromium after pnpm install.
  --reset-db         Pass --reset-db to scripts/deploy.sh in --prod mode.
  --env-file PATH    Copy PATH to .env when .env does not already exist.
  --dry-run          Print planned actions without changing the machine.
  -h, --help         Show this help.
EOF
}

log() {
  printf '[install] %s\n' "$*"
}

warn() {
  printf '[install] WARN: %s\n' "$*" >&2
}

die() {
  printf '[install] ERROR: %s\n' "$*" >&2
  exit 1
}

command_exists() {
  command -v "$1" >/dev/null 2>&1
}

run_cmd() {
  if [[ "$DRY_RUN" -eq 1 ]]; then
    printf '[install] DRY-RUN:'
    printf ' %q' "$@"
    printf '\n'
    return
  fi

  "$@"
}

run_shell() {
  if [[ "$DRY_RUN" -eq 1 ]]; then
    printf '[install] DRY-RUN: %s\n' "$*"
    return
  fi

  bash -lc "$*"
}

run_sudo() {
  if [[ "${EUID}" -eq 0 ]]; then
    run_cmd "$@"
    return
  fi

  if ! command_exists sudo; then
    die "sudo is required for this operation, but sudo was not found."
  fi

  run_cmd sudo "$@"
}

node_major_version() {
  if ! command_exists node; then
    echo 0
    return
  fi

  node -p "process.versions.node.split('.')[0]" 2>/dev/null || echo 0
}

resolve_pnpm_version() {
  local package_manager
  package_manager="$(grep -E '"packageManager"[[:space:]]*:[[:space:]]*"pnpm@' "$ROOT_DIR/package.json" \
    | sed -E 's/.*"pnpm@([^"+]+)(\+[^"]*)?".*/\1/' \
    | head -n 1 || true)"

  if [[ -n "$package_manager" ]]; then
    PNPM_VERSION="$package_manager"
  else
    PNPM_VERSION="10.28.2"
  fi
}

ensure_node() {
  local current_major
  local os_name
  current_major="$(node_major_version)"
  os_name="$(uname -s)"

  if [[ "$current_major" -ge 20 ]]; then
    log "Node.js $(node -v) detected; skipping Node.js bootstrap."
    return
  fi

  log "Node.js 20+ not found; installing Node.js 22."
  case "$os_name" in
    Linux)
      if ! command_exists apt-get; then
        die "Automatic Node.js installation currently supports Debian/Ubuntu only."
      fi
      run_sudo apt-get update
      run_sudo apt-get install -y curl ca-certificates gnupg
      if [[ "${EUID}" -eq 0 ]]; then
        run_shell "curl -fsSL https://deb.nodesource.com/setup_22.x | bash -"
      else
        run_shell "curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -"
      fi
      run_sudo apt-get install -y nodejs
      ;;
    Darwin)
      if ! command_exists brew; then
        die "Homebrew is required to install Node.js automatically on macOS."
      fi
      run_cmd brew install node
      ;;
    *)
      die "Unsupported operating system for automatic Node.js installation: $os_name"
      ;;
  esac

  if [[ "$DRY_RUN" -eq 0 ]] && { ! command_exists node || [[ "$(node_major_version)" -lt 20 ]]; }; then
    die "Node.js 20+ installation did not complete successfully."
  fi
}

ensure_pnpm() {
  log "Preparing pnpm $PNPM_VERSION."

  if command_exists corepack; then
    run_cmd corepack enable
    run_cmd corepack prepare "pnpm@$PNPM_VERSION" --activate
  elif command_exists pnpm; then
    log "corepack not found; using existing pnpm at $(command -v pnpm)."
  elif [[ "$(uname -s)" == "Darwin" ]] && command_exists brew; then
    run_cmd brew install pnpm
  else
    run_shell "curl -fsSL https://get.pnpm.io/install.sh | env PNPM_VERSION='$PNPM_VERSION' sh -"
    if [[ "$(uname -s)" == "Linux" ]]; then
      export PNPM_HOME="${PNPM_HOME:-$HOME/.local/share/pnpm}"
    else
      export PNPM_HOME="${PNPM_HOME:-$HOME/Library/pnpm}"
    fi
    export PATH="$PNPM_HOME:$PATH"
  fi

  if [[ "$DRY_RUN" -eq 0 ]] && ! command_exists pnpm; then
    die "pnpm installation failed. Install pnpm $PNPM_VERSION manually and retry."
  fi
}

ensure_uv_python() {
  if [[ "$WITH_PYTHON" -eq 0 ]]; then
    log "Skipping uv + Python 3.13; pass --with-python if this deployment needs Python runtime support."
    return
  fi

  log "Preparing uv + Python 3.13."
  if ! command_exists uv; then
    run_shell "curl -LsSf https://astral.sh/uv/install.sh | sh"
    export PATH="$HOME/.local/bin:$PATH"
  fi

  if [[ "$DRY_RUN" -eq 0 ]] && ! command_exists uv; then
    die "uv installation failed. Install uv manually and retry."
  fi

  if [[ "$DRY_RUN" -eq 0 ]] && uv python find 3.13 >/dev/null 2>&1; then
    log "Python 3.13 is already available through uv."
  else
    run_cmd uv python install 3.13
  fi
}

ensure_env_file() {
  local source_env
  source_env=""

  if [[ -n "$ENV_FILE" ]]; then
    source_env="$ENV_FILE"
  elif [[ -f "$DELIVERY_ENV_FILE" ]]; then
    source_env="$DELIVERY_ENV_FILE"
  fi

  if [[ -f "$LOCAL_ENV_FILE" ]]; then
    log ".env already exists; keeping the existing file."
    return
  fi

  if [[ -n "$source_env" ]]; then
    [[ -f "$source_env" ]] || die "Env file not found: $source_env"
    log "copy env file to .env"
    run_cmd cp "$source_env" "$LOCAL_ENV_FILE"
    run_cmd chmod 600 "$LOCAL_ENV_FILE"
    return
  fi

  if [[ -f "$ROOT_DIR/.env.example" ]]; then
    warn ".env was not found; creating it from .env.example. Fill secrets before running production workflows."
    run_cmd cp "$ROOT_DIR/.env.example" "$LOCAL_ENV_FILE"
    run_cmd chmod 600 "$LOCAL_ENV_FILE"
    return
  fi

  die "No .env, deploy/env/.env, or .env.example file was found."
}

install_dependencies() {
  log "Running pnpm install --frozen-lockfile."
  (
    cd "$ROOT_DIR"
    run_cmd pnpm install --frozen-lockfile
  )
}

install_playwright() {
  if [[ "$WITH_PLAYWRIGHT" -eq 0 ]]; then
    return
  fi

  log "Installing Playwright Chromium."
  (
    cd "$ROOT_DIR"
    if [[ "$(uname -s)" == "Linux" ]]; then
      run_cmd pnpm exec playwright install --with-deps chromium
    else
      run_cmd pnpm exec playwright install chromium
    fi
  )
}

start_dev() {
  if [[ "$NO_START" -eq 1 ]]; then
    log "Skipping app start because --no-start was provided."
    return
  fi

  log "Starting development workflow with pnpm run dev."
  (
    cd "$ROOT_DIR"
    run_cmd pnpm run dev
  )
}

run_prod_deploy() {
  local args
  args=()
  [[ "$RESET_DB" -eq 1 ]] && args+=(--reset-db)
  [[ "$NO_START" -eq 1 ]] && args+=(--no-start)

  log "Running production deployment through scripts/deploy.sh."
  (
    cd "$ROOT_DIR"
    run_cmd bash scripts/deploy.sh "${args[@]}"
  )
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --prod)
      PROD_MODE=1
      ;;
    --no-start)
      NO_START=1
      ;;
    --with-python)
      WITH_PYTHON=1
      ;;
    --with-playwright)
      WITH_PLAYWRIGHT=1
      ;;
    --reset-db)
      RESET_DB=1
      ;;
    --env-file)
      shift
      [[ $# -gt 0 ]] || die "--env-file requires a path."
      ENV_FILE="$1"
      ;;
    --dry-run)
      DRY_RUN=1
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      die "Unknown argument: $1"
      ;;
  esac
  shift
done

resolve_pnpm_version
log "Customer deployment mode: $([[ "$PROD_MODE" -eq 1 ]] && echo prod || echo dev)."
ensure_node
ensure_pnpm
ensure_uv_python
ensure_env_file

if [[ "$PROD_MODE" -eq 1 ]]; then
  run_prod_deploy
else
  install_dependencies
  install_playwright
  start_dev
fi
