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

# On-VPS deploy entrypoint.
# - Clones repo if missing, pulls branch
# - Installs deps (offline-friendly)
# - Builds API/Web with larger Node heap
# - Syncs web dist and restarts services

umask 022

BRANCH="${BRANCH:-main}"
APP_USER="${APP_USER:-myroadtrip}"
APP_DIR="${APP_DIR:-/srv/myroadtrip}"
REPO_DIR="${REPO_DIR:-$APP_DIR/repo}"
WEB_ROOT="${WEB_ROOT:-/srv/www/roadtripper}"
REPO_URL="${REPO_URL:-https://github.com/HectorLayak/RoadTripper.git}"
ENV_FILE="${ENV_FILE:-$APP_DIR/.env.production}"
API_ENV_FILE="${API_ENV_FILE:-$REPO_DIR/apps/api/.env.production}"
WEB_ENV_FILE="${WEB_ENV_FILE:-$REPO_DIR/apps/web/.env.production}"
NODE_HEAP="${NODE_OPTIONS:---max-old-space-size=4096}"
PNPM_FLAGS_DEFAULT="--frozen-lockfile --prefer-offline"
PNPM_FLAGS="${PNPM_FLAGS:-$PNPM_FLAGS_DEFAULT}"

BUILD_API=true
BUILD_WEB=true
INSTALL_DEPS=true
RESTART_SERVICES=true
PULL=true
AUTO_CLONE=true

# Honor BUILD_WEB/BUILD_API env defaults before flags.
if [[ "${BUILD_WEB:-}" == "false" ]]; then BUILD_WEB=false; fi
if [[ "${BUILD_API:-}" == "false" ]]; then BUILD_API=false; fi

# Toggle API deployment mode: systemd + pnpm vs Docker Compose
# When API_VIA_DOCKER=true, host build is skipped and myroadtrip-api-docker.service is restarted instead.
API_VIA_DOCKER="${API_VIA_DOCKER:-false}"

log() { printf '[%s] %s\n' "$(date +'%F %T')" "$*"; }
warn() { printf '[%s] [warn] %s\n' "$(date +'%F %T')" "$*" >&2; }
die() { printf '[%s] [error] %s\n' "$(date +'%F %T')" "$*" >&2; exit 1; }

usage() {
  cat <<'EOF'
Usage: deploy_on_vps.sh [options]
  --branch <name>     Git branch to deploy (default: main)
  --skip-web          Skip web build + sync
  --skip-api          Skip API build
  --web-only          Build/sync web only
  --api-only          Build API only
  --skip-install      Skip pnpm install
  --no-restart        Skip systemd restarts
  --no-pull           Do not git pull (use current checkout)
  --no-clone          Do not clone if repo missing
  --app-dir <path>    App root (default: /srv/myroadtrip)
  --repo-dir <path>   Repo path (default: /srv/myroadtrip/repo)
  --web-root <path>   Web root (default: /srv/www/roadtripper)
  --pnpm-flags "<...>" Override pnpm install flags
  -h, --help          Show this help
Env vars: APP_USER, REPO_URL, NODE_OPTIONS, PNPM_FLAGS, BUILD_WEB, BUILD_API.
EOF
}

on_err() { warn "deploy failed (line ${BASH_LINENO[0]:-?}, cmd: ${BASH_COMMAND:-?})"; }
trap on_err ERR

while [[ $# -gt 0 ]]; do
  case "$1" in
    --branch) BRANCH="$2"; shift 2 ;;
    --skip-web) BUILD_WEB=false; shift ;;
    --skip-api) BUILD_API=false; shift ;;
    --web-only) BUILD_WEB=true; BUILD_API=false; shift ;;
    --api-only) BUILD_API=true; BUILD_WEB=false; shift ;;
    --skip-install) INSTALL_DEPS=false; shift ;;
    --no-restart) RESTART_SERVICES=false; shift ;;
    --no-pull) PULL=false; shift ;;
    --no-clone) AUTO_CLONE=false; shift ;;
    --app-dir) APP_DIR="$2"; shift 2 ;;
    --repo-dir) REPO_DIR="$2"; shift 2 ;;
    --web-root) WEB_ROOT="$2"; shift 2 ;;
    --pnpm-flags) PNPM_FLAGS="$2"; shift 2 ;;
    -h|--help) usage; exit 0 ;;
    *) die "unknown argument: $1" ;;
  esac
done

require_cmds() {
  local missing=()
  for bin in "$@"; do
    if ! command -v "$bin" >/dev/null 2>&1; then
      missing+=("$bin")
    fi
  done
  if [[ ${#missing[@]} -gt 0 ]]; then
    die "missing required commands: ${missing[*]}"
  fi
}

run_root() {
  if [[ $EUID -eq 0 ]]; then
    "$@"
  else
    sudo "$@"
  fi
}

run_as_app() {
  if [[ "$(id -un)" == "$APP_USER" ]]; then
    NODE_OPTIONS="$NODE_HEAP" "$@"
  else
    sudo -u "$APP_USER" NODE_OPTIONS="$NODE_HEAP" "$@"
  fi
}

assert_app_user() {
  if ! id -u "$APP_USER" >/dev/null 2>&1; then
    die "app user '$APP_USER' does not exist"
  fi
}

ensure_dirs() {
  run_root install -d -o "$APP_USER" -g "$APP_USER" "$APP_DIR" "$REPO_DIR"
  run_root install -d -o "$APP_USER" -g "$APP_USER" "$WEB_ROOT"
}

check_env_file() {
  if [[ ! -f "$ENV_FILE" ]]; then
    warn "Environment file missing at $ENV_FILE (set ENV_FILE to silence or update path)"
  fi
  if [[ ! -f "$API_ENV_FILE" ]]; then
    warn "API env missing at $API_ENV_FILE (set API_ENV_FILE to override)"
  fi
  if [[ ! -f "$WEB_ENV_FILE" ]]; then
    warn "Web env missing at $WEB_ENV_FILE (set WEB_ENV_FILE to override)"
  fi
}

ensure_cors_config() {
  # Automatically add https://localhost to CORS_ORIGIN if missing
  # This is required for Capacitor Android app
  local target_file="$API_ENV_FILE"
  if [[ -f "$target_file" ]]; then
    if ! grep -q "https://localhost" "$target_file"; then
      log "Adding https://localhost to CORS_ORIGIN in $target_file"
      # Append to existing line or add new line
      if grep -q "^CORS_ORIGIN=" "$target_file"; then
        run_as_app sed -i '/^CORS_ORIGIN=/ s/$/,https:\/\/localhost/' "$target_file"
      else
        run_as_app bash -c "echo 'CORS_ORIGIN=http://localhost:5173,https://roadtripper.io,https://localhost' >> '$target_file'"
      fi
    fi
  fi
}

ensure_repo() {
  if [[ -d "$REPO_DIR/.git" ]]; then
    return
  fi
  if ! $AUTO_CLONE; then
    die "repo not found at $REPO_DIR and cloning disabled"
  fi
  log "Cloning repo into $REPO_DIR (branch: $BRANCH)"
  run_as_app git clone --branch "$BRANCH" "$REPO_URL" "$REPO_DIR"
}

update_repo() {
  if ! $PULL; then
    log "Skipping git pull (PULL=false)"
    return
  fi
  log "Updating repo (branch: $BRANCH)"
  run_as_app bash -lc "
    set -euo pipefail
    cd '$REPO_DIR'
    git fetch origin --prune
    git checkout '$BRANCH'
    git pull --ff-only origin '$BRANCH'
  "
}

ensure_pnpm() {
  if command -v pnpm >/dev/null 2>&1; then
    return
  fi
  log "pnpm missing; enabling via corepack"
  run_as_app corepack enable pnpm >/dev/null 2>&1 || die "failed to enable pnpm"
}

install_deps() {
  if ! $INSTALL_DEPS; then
    log "Skipping pnpm install (INSTALL_DEPS=false)"
    return
  fi
  log "Installing dependencies (pnpm ${PNPM_FLAGS})"
  run_as_app bash -lc "
    set -euo pipefail
    cd '$REPO_DIR'
    pnpm install $PNPM_FLAGS
  "
}

build_libs() {
  log "Building shared packages"
  run_as_app bash -lc "
    set -euo pipefail
    cd '$REPO_DIR'
    pnpm --filter @my-roadtrip/utils build
    pnpm --filter @my-roadtrip/types build
    pnpm --filter @my-roadtrip/validators build
  "
}

build_api() {
  if ! $BUILD_API; then
    log "Skipping API build"
    return
  fi
  if [[ "$API_VIA_DOCKER" == "true" ]]; then
    log "Skipping host API build (API_VIA_DOCKER=true; Docker image will be built via docker compose)"
    return
  fi
  log "Building API"
  run_as_app bash -lc "
    set -euo pipefail
    cd '$REPO_DIR'
    pnpm --filter @my-roadtrip/api build
  "
}

build_web() {
  if ! $BUILD_WEB; then
    log "Skipping web build"
    return
  fi
  log "Building web"
  run_as_app bash -lc "
    set -euo pipefail
    cd '$REPO_DIR'
    pnpm --filter @my-roadtrip/web build
  "
}

sync_web() {
  # If build was skipped but dist exists (e.g. uploaded via sync-code), we should still sync.
  # If build was skipped and no dist, then we return.
  if ! $BUILD_WEB && ! run_root test -d "$REPO_DIR/apps/web/dist"; then
    return
  fi
  
  log "DEBUG: Listing apps/web content:"
  run_root ls -la "$REPO_DIR/apps/web"
  
  if ! run_root test -d "$REPO_DIR/apps/web/dist"; then
    die "web dist not found at $REPO_DIR/apps/web/dist (build failed or produced no output)"
  fi
  log "Syncing web dist -> $WEB_ROOT"
  # Remove --delete to prevent "missing chunk" errors for users with open tabs during deploy
  run_root rsync -a "$REPO_DIR/apps/web/dist/" "$WEB_ROOT/"
}

ensure_redis_password() {
  local redis_pass_file="/etc/redis/.redis_pass"
  local redis_conf_file="/etc/redis/redis.conf"
  local password=""

  if run_root test -f "$redis_pass_file"; then
    password=$(run_root cat "$redis_pass_file")
  elif run_root test -f "$redis_conf_file"; then
    # Extract password from redis.conf (requirepass mypassword)
    # Use grep and awk to get the second word
    password=$(run_root grep '^requirepass ' "$redis_conf_file" | awk '{print $2}' | tr -d '"')
  fi

  if [[ -n "$password" ]]; then
    log "Injecting Redis password into .env.production..."
    # Escape special characters for sed
    local escaped_pass=$(printf '%s\n' "$password" | sed -e 's/[\/&]/\\&/g')
    run_root sed -i "s/^REDIS_PASSWORD=.*/REDIS_PASSWORD=$escaped_pass/" "$REPO_DIR/apps/api/.env.production"
  else
    warn "Redis password not found in $redis_pass_file or $redis_conf_file; skipping injection"
  fi
}

ensure_docker_service() {
  if [[ "$API_VIA_DOCKER" == "true" ]]; then
    local target_file="/etc/systemd/system/myroadtrip-api-docker.service"
    
    log "Installing myroadtrip-api-docker.service..."
    
    # Create service file directly
    run_root bash -c "cat > '$target_file'" <<EOF
[Unit]
Description=MyRoadTrip Fastify API (Docker)
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/myroadtrip/repo
Environment=COMPOSE_FILE=/srv/myroadtrip/repo/scripts/vps/docker-compose.api.yml
ExecStart=/usr/bin/docker compose up -d --build
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target
EOF

    run_root systemctl daemon-reload
    run_root systemctl enable myroadtrip-api-docker
  fi
}

ensure_docker_dns() {
  # Fix Docker DNS issues on VPS by forcing Google DNS
  if [[ ! -f "/etc/docker/daemon.json" ]]; then
    log "Configuring Docker DNS (8.8.8.8)..."
    run_root bash -c "cat > /etc/docker/daemon.json" <<EOF
{
  "dns": ["8.8.8.8", "1.1.1.1"]
}
EOF
    run_root systemctl restart docker
  fi
}

restart_services() {
  if ! $RESTART_SERVICES; then
    log "Skipping service restarts"
    return
  fi
  if command -v systemctl >/dev/null 2>&1; then
    if [[ "$API_VIA_DOCKER" == "true" ]]; then
      log "Stopping host myroadtrip-api service (switching to Docker)"
      run_root systemctl stop myroadtrip-api || true
      run_root systemctl disable myroadtrip-api || true
      
      log "Restarting myroadtrip-api-docker (Docker-based API service)"
      if ! run_root systemctl restart myroadtrip-api-docker; then
        warn "myroadtrip-api-docker restart failed. Dumping logs:"
        run_root journalctl -xeu myroadtrip-api-docker.service -n 50 --no-pager
        warn "End of logs"
    else
      log "Restarting myroadtrip-api"
      run_root systemctl restart myroadtrip-api || warn "myroadtrip-api restart failed"
    fi
    # Sync Caddyfile from repo to prevent configuration drift
    log "Syncing Caddyfile"
    if run_root test -f "$REPO_DIR/scripts/vps/Caddyfile"; then
      run_root cp "$REPO_DIR/scripts/vps/Caddyfile" /etc/caddy/Caddyfile
      log "Reloading caddy"
      run_root systemctl reload caddy || warn "caddy reload failed"
    else
      warn "Caddyfile not found in repo, skipping sync"
    fi
    
    # Sync docker-compose files
    log "Syncing docker-compose files"
    if run_root test -f "$REPO_DIR/scripts/vps/docker-compose.api.yml"; then
      run_root cp "$REPO_DIR/scripts/vps/docker-compose.api.yml" "$REPO_DIR/docker-compose.api.yml"
    fi
    if run_root test -f "$REPO_DIR/scripts/vps/docker-compose.maps.yml"; then
      run_root cp "$REPO_DIR/scripts/vps/docker-compose.maps.yml" "$REPO_DIR/docker-compose.maps.yml"
    fi
    
    # Sync systemd service files
    log "Syncing systemd service files"
    if run_root test -f "$REPO_DIR/scripts/vps/myroadtrip-api-docker.service"; then
      run_root cp "$REPO_DIR/scripts/vps/myroadtrip-api-docker.service" /etc/systemd/system/myroadtrip-api-docker.service
      run_root systemctl daemon-reload
    fi
    if run_root test -f "$REPO_DIR/scripts/vps/myroadtrip-api.service"; then
      run_root cp "$REPO_DIR/scripts/vps/myroadtrip-api.service" /etc/systemd/system/myroadtrip-api.service
      run_root systemctl daemon-reload
    fi
    if run_root test -f "$REPO_DIR/scripts/vps/maps-stack.service"; then
      run_root cp "$REPO_DIR/scripts/vps/maps-stack.service" /etc/systemd/system/maps-stack.service
      run_root systemctl daemon-reload
    fi
  else
    warn "systemctl not available; skipping restarts"
  fi
}

main() {
  require_cmds git rsync bash sudo curl
  log "Starting deploy (branch=$BRANCH, repo=$REPO_DIR, web=$BUILD_WEB, api=$BUILD_API)"
  
  # Debug Docker version
  if command -v docker >/dev/null 2>&1; then
    log "Docker version: $(docker --version)"
    log "Docker Compose version: $(docker compose version 2>/dev/null || echo 'not found')"
  fi

  if command -v systemctl >/dev/null 2>&1; then
    systemctl is-active --quiet redis-server || warn "redis-server is not active; rate-limit/cache may be degraded"
  fi
  assert_app_user
  ensure_dirs
  check_env_file
  ensure_cors_config
  ensure_repo
  update_repo
  ensure_pnpm
  install_deps
  build_libs
  build_api
  build_web
  sync_web
  ensure_redis_password
  ensure_docker_dns
  ensure_docker_service
  
  # Run backup before restarting/changing things
  if [[ -f "$REPO_DIR/scripts/vps/backup.sh" ]]; then
    log "Running pre-deployment backup..."
    run_root chmod +x "$REPO_DIR/scripts/vps/backup.sh"
    run_root "$REPO_DIR/scripts/vps/backup.sh" || warn "Backup failed!"
  else
    warn "Backup script not found, skipping backup."
  fi

  restart_services
  
  # Diagnostic: Check Redis
  log "Diagnosing Redis connection..."
  if command -v redis-cli >/dev/null 2>&1; then
    if redis-cli ping >/dev/null 2>&1; then
      log "✅ Redis is reachable WITHOUT password."
    elif [[ -f "/etc/redis/redis.conf" ]] && redis-cli -a "$(grep '^requirepass ' /etc/redis/redis.conf | awk '{print $2}' | tr -d '"')" ping >/dev/null 2>&1; then
      log "✅ Redis is reachable WITH password from redis.conf."
    else
      warn "⚠️ Redis is NOT reachable or password unknown."
    fi
  fi

  if command -v curl >/dev/null 2>&1; then
    log "Healthcheck: http://localhost:3000/health"
    # Retry a few times waiting for start
    for i in {1..12}; do
      if curl -s -f http://localhost:3000/health >/dev/null; then
        log "✅ Healthcheck passed!"
        return 0
      fi
      sleep 5
    done
    warn "Healthcheck failed; dumping container logs:"
    run_root docker logs myroadtrip-api --tail 50
  fi
  log "✅ Deploy complete"
}

main "$@"
