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

usage() {
  cat <<'EOF'
Usage:
  create-node-site --domain DOMAIN --node-version 18|20|22 [--port PORT] [--issue-cert] [--cert-name NAME] [--email EMAIL] [--force] [--release-layout|--no-release-layout] [--run-user USER] [--start-command CMD] [--node-env ENV]
  create-node-site --help

What it does:
  - creates /var/www/nodeXX/DOMAIN
  - creates /var/log/nginx/nodeXX/DOMAIN.access.log and .error.log
  - writes /etc/node-sites/DOMAIN.env
  - writes /etc/nginx/conf.d/DOMAIN.conf as a reverse proxy to localhost:PORT
  - labels the site root for SELinux and labels PORT as http_port_t
  - enables node-site@DOMAIN.service
  - reloads nginx
  - optionally issues a Let's Encrypt certificate and switches the vhost to HTTPS
  - by default initializes current/releases/shared layout and points deployments at current

Examples:
  create-node-site --domain node.example.com --node-version 22 --port 22101
  create-node-site --domain node.example.com --node-version 20 --port 20101 --issue-cert --email ops@example.com
EOF
}

require_root() {
  if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
    echo "This script must run as root" >&2
    exit 1
  fi
}

choose_port() {
  local version="$1"
  local start end port
  case "$version" in
    18) start=18101; end=18199 ;;
    20) start=20101; end=20199 ;;
    22) start=22101; end=22199 ;;
    *) echo "Unsupported node version for port selection: $version" >&2; exit 2 ;;
  esac
  for ((port=start; port<=end; port++)); do
    if ! ss -lnt | awk '{print $4}' | grep -Eq "(^|:)$port$"; then
      echo "$port"
      return 0
    fi
  done
  echo "No free port found for node version $version in range $start-$end" >&2
  exit 1
}

domain=""
node_version=""
port=""
issue_cert=0
cert_name=""
email=""
force=0
release_layout=1
run_user="git"
start_command="npm start"
node_env="production"

while [[ $# -gt 0 ]]; do
  case "$1" in
    --domain) domain="${2:-}"; shift 2 ;;
    --node-version) node_version="${2:-}"; shift 2 ;;
    --port) port="${2:-}"; shift 2 ;;
    --issue-cert) issue_cert=1; shift ;;
    --cert-name) cert_name="${2:-}"; shift 2 ;;
    --email) email="${2:-}"; shift 2 ;;
    --force) force=1; shift ;;
    --release-layout) release_layout=1; shift ;;
    --no-release-layout) release_layout=0; shift ;;
    --run-user) run_user="${2:-}"; shift 2 ;;
    --start-command) start_command="${2:-}"; shift 2 ;;
    --node-env) node_env="${2:-}"; shift 2 ;;
    --help|-h) usage; exit 0 ;;
    *) echo "Unknown argument: $1" >&2; usage; exit 2 ;;
  esac
done

[[ -n "$domain" && -n "$node_version" ]] || { usage; exit 2; }
require_root

case "$node_version" in
  18) root_parent="/var/www/node18"; log_parent="/var/log/nginx/node18" ;;
  20) root_parent="/var/www/node20"; log_parent="/var/log/nginx/node20" ;;
  22) root_parent="/var/www/node22"; log_parent="/var/log/nginx/node22" ;;
  *) echo "Unsupported node version: $node_version (allowed: 18 20 22)" >&2; exit 2 ;;
esac

if [[ -z "$port" ]]; then
  port="$(choose_port "$node_version")"
fi

site_root="$root_parent/$domain"
access_log="$log_parent/$domain.access.log"
error_log="$log_parent/$domain.error.log"
conf_path="/etc/nginx/conf.d/$domain.conf"
env_path="/etc/node-sites/$domain.env"
cert_live_name="${cert_name:-$domain}"
cert_dir="/etc/letsencrypt/live/$cert_live_name"
service_name="node-site@$domain.service"

if [[ -e "$conf_path" && "$force" -ne 1 ]]; then
  echo "Refusing to overwrite existing nginx conf: $conf_path (use --force)" >&2
  exit 1
fi

mkdir -p "$site_root" "$log_parent" /etc/node-sites
chown -R git:webread "$site_root"
find "$site_root" -type d -exec chmod 2750 {} + || true
if command -v setfacl >/dev/null 2>&1; then
  setfacl -R -m u:nginx:rx,u:cicd:rwx,u:git:rwx,g:webread:rx "$site_root"
  setfacl -R -d -m u:nginx:rx,u:cicd:rwx,u:git:rwx,g:webread:rx "$site_root"
fi

if [[ "$release_layout" -eq 1 ]]; then
  /usr/local/bin/init-release-layout --site-root "$site_root"
fi

cat > "$env_path" <<EOF2
RUN_USER='$run_user'
SITE_ROOT='$site_root'
NODE_VERSION='$node_version'
PORT='$port'
HOST='127.0.0.1'
NODE_ENV='$node_env'
START_COMMAND='$start_command'
EOF2
chmod 0644 "$env_path"

cat > "$conf_path" <<EOF2
server {
    listen 80;
    listen [::]:80;
    server_name $domain;

    access_log $access_log;
    error_log  $error_log warn;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/_letsencrypt;
        default_type text/plain;
        try_files \$uri =404;
    }

    location / {
        proxy_pass http://127.0.0.1:$port;
        include /etc/nginx/snippets/node-proxy-common.conf;
    }
}
EOF2

touch "$access_log" "$error_log"
chown nginx:nginx "$access_log" "$error_log"
chmod 0644 "$access_log" "$error_log"

if command -v semanage >/dev/null 2>&1; then
  semanage fcontext -a -t httpd_sys_content_t "$root_parent(/.*)?" 2>/dev/null || semanage fcontext -m -t httpd_sys_content_t "$root_parent(/.*)?"
  restorecon -RF "$root_parent" >/dev/null 2>&1 || true
  semanage port -a -t http_port_t -p tcp "$port" 2>/dev/null || semanage port -m -t http_port_t -p tcp "$port"
fi

if command -v setsebool >/dev/null 2>&1; then
  setsebool -P httpd_can_network_connect on >/dev/null 2>&1 || true
fi

systemctl daemon-reload
systemctl enable "$service_name" >/dev/null
nginx -t
systemctl reload nginx

echo "HTTP node site created: $domain -> 127.0.0.1:$port"
echo "Service enabled: $service_name"

echo "Env file: $env_path"

if [[ "$issue_cert" -eq 1 ]]; then
  certbot_args=(certbot certonly --webroot -w /var/www/_letsencrypt -d "$domain" --non-interactive --agree-tos --key-type ecdsa)
  if [[ -n "$email" ]]; then
    certbot_args+=(--email "$email")
  else
    certbot_args+=(--register-unsafely-without-email)
  fi
  "${certbot_args[@]}"

  if [[ ! -f "$cert_dir/fullchain.pem" || ! -f "$cert_dir/privkey.pem" ]]; then
    echo "Certificate files not found under $cert_dir" >&2
    exit 1
  fi

  cat > "$conf_path" <<EOF2
server {
    listen 80;
    listen [::]:80;
    server_name $domain;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/_letsencrypt;
        default_type text/plain;
        try_files \$uri =404;
    }

    location / {
        return 301 https://\$host\$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name $domain;

    access_log $access_log;
    error_log  $error_log warn;

    ssl_certificate     $cert_dir/fullchain.pem;
    ssl_certificate_key $cert_dir/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_session_timeout 1d;
    ssl_session_cache   shared:SSL:10m;
    ssl_prefer_server_ciphers off;

    add_header X-Frame-Options SAMEORIGIN always;
    add_header X-Content-Type-Options nosniff always;
    add_header Referrer-Policy strict-origin-when-cross-origin always;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/_letsencrypt;
        default_type text/plain;
        try_files \$uri =404;
    }

    location / {
        proxy_pass http://127.0.0.1:$port;
        include /etc/nginx/snippets/node-proxy-common.conf;
    }
}
EOF2

  nginx -t
  systemctl reload nginx
  echo "HTTPS enabled for $domain using cert live path: $cert_dir"
fi
