#!/usr/bin/env bash
# Write logs/ports.env for PM2 (API port always from node/.env PORT).
#
# API port: node/.env PORT only — never random, never from logs/ports.env cache.
# Dev static apps (PM2-only): optional port allocation when not in Apache mode.
set -euo pipefail

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
PORTS_FILE="$ROOT/logs/ports.env"
NODE_ENV_FILE="$ROOT/node/.env"
# shellcheck source=lib/node-env-port.sh
source "$ROOT/scripts/lib/node-env-port.sh"

mkdir -p "$ROOT/logs"

if [[ -f "$NODE_ENV_FILE" ]] && grep -qE '^NODE_ENV=production' "$NODE_ENV_FILE"; then
  export CALENDAR_APACHE_MODE="${CALENDAR_APACHE_MODE:-1}"
fi

CALENDAR_API_PORT="$(node_env_port "$ROOT")"

DEFAULT_HOST_PORT=4200
DEFAULT_PUBLIC_PORT=4201
DEFAULT_EMBED_PORT=4202
STATIC_RANGE_MIN="${CALENDAR_STATIC_PORT_MIN:-4200}"
STATIC_RANGE_MAX="${CALENDAR_STATIC_PORT_MAX:-4999}"

saved_host="" saved_public="" saved_embed=""
if [[ "${CALENDAR_APACHE_MODE:-0}" != "1" && -f "$PORTS_FILE" ]]; then
  # Dev only: reuse saved static app ports (not API port)
  # shellcheck disable=SC1090
  source "$PORTS_FILE"
  saved_host="${CALENDAR_HOST_PORT:-}"
  saved_public="${CALENDAR_PUBLIC_PORT:-}"
  saved_embed="${CALENDAR_EMBED_PORT:-}"
fi

is_port_listening() {
  local port=$1
  if command -v lsof >/dev/null 2>&1; then
    lsof -nP -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1
  elif command -v ss >/dev/null 2>&1; then
    ss -ltn 2>/dev/null | grep -qE ":${port}\b"
  elif [[ -n "${BASH_VERSION:-}" ]]; then
    (echo >/dev/tcp/127.0.0.1/"$port") 2>/dev/null
  else
    return 1
  fi
}

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

pick_static_port() {
  local var_name=$1
  local pm2_app=$2
  local preferred=$3
  local saved=$4
  local range_min=$5
  local range_max=$6

  if pm2_process_running "$pm2_app" && [[ -n "$saved" ]]; then
    echo "$saved"
    return
  fi

  local candidate="${saved:-$preferred}"
  if ! is_port_listening "$candidate"; then
    echo "$candidate"
    return
  fi

  if [[ "$candidate" != "$preferred" ]] && ! is_port_listening "$preferred"; then
    echo "$preferred"
    return
  fi

  local tries=0
  local span=$((range_max - range_min + 1))
  while (( tries < 100 )); do
    local port=$((range_min + RANDOM % span))
    if ! is_port_listening "$port"; then
      printf 'Allocated random port %s for %s (%s was busy).\n' "$port" "$var_name" "$candidate" >&2
      echo "$port"
      return
    fi
    tries=$((tries + 1))
  done

  echo "Error: no free port for $var_name in range $range_min-$range_max" >&2
  exit 1
}

if is_port_listening "$CALENDAR_API_PORT" && ! pm2_process_running calendar-api; then
  echo "Error: PORT=$CALENDAR_API_PORT (node/.env) is in use by another process." >&2
  echo "  Free the port or change PORT in node/.env, then redeploy." >&2
  exit 1
fi

if [[ "${CALENDAR_APACHE_MODE:-0}" == "1" ]]; then
  CALENDAR_HOST_PORT=""
  CALENDAR_PUBLIC_PORT=""
  CALENDAR_EMBED_PORT=""
else
  CALENDAR_HOST_PORT=$(pick_static_port CALENDAR_HOST_PORT calendar-host "$DEFAULT_HOST_PORT" "$saved_host" "$STATIC_RANGE_MIN" "$STATIC_RANGE_MAX")
  CALENDAR_PUBLIC_PORT=$(pick_static_port CALENDAR_PUBLIC_PORT calendar-public "$DEFAULT_PUBLIC_PORT" "$saved_public" "$STATIC_RANGE_MIN" "$STATIC_RANGE_MAX")
  CALENDAR_EMBED_PORT=$(pick_static_port CALENDAR_EMBED_PORT calendar-embed "$DEFAULT_EMBED_PORT" "$saved_embed" "$STATIC_RANGE_MIN" "$STATIC_RANGE_MAX")
fi

cat > "$PORTS_FILE" <<EOF
# Auto-generated by scripts/allocate-ports.sh — do not edit
# API port is always read from node/.env PORT (this file mirrors it for PM2).
export CALENDAR_APACHE_MODE=${CALENDAR_APACHE_MODE:-0}
export CALENDAR_API_PORT=$CALENDAR_API_PORT
export CALENDAR_HOST_PORT=${CALENDAR_HOST_PORT:-}
export CALENDAR_PUBLIC_PORT=${CALENDAR_PUBLIC_PORT:-}
export CALENDAR_EMBED_PORT=${CALENDAR_EMBED_PORT:-}
export PORT=$CALENDAR_API_PORT
EOF

export CALENDAR_APACHE_MODE="${CALENDAR_APACHE_MODE:-0}"
export CALENDAR_API_PORT PORT
[[ -n "${CALENDAR_HOST_PORT:-}" ]] && export CALENDAR_HOST_PORT
[[ -n "${CALENDAR_PUBLIC_PORT:-}" ]] && export CALENDAR_PUBLIC_PORT
[[ -n "${CALENDAR_EMBED_PORT:-}" ]] && export CALENDAR_EMBED_PORT

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
  printf '\nPorts (API from node/.env PORT=%s):\n' "$CALENDAR_API_PORT"
  printf '  API:             %s\n' "$CALENDAR_API_PORT"
  if [[ "${CALENDAR_APACHE_MODE:-0}" == "1" ]]; then
    printf '  (Apache mode — static apps not started by PM2)\n'
  else
    printf '  Host dashboard:  %s\n' "$CALENDAR_HOST_PORT"
    printf '  Public booking:  %s\n' "$CALENDAR_PUBLIC_PORT"
    printf '  Embed widget:    %s\n' "$CALENDAR_EMBED_PORT"
  fi
fi
