#!/usr/bin/env bash
# 격리 스택 기동. 순서: Redis · DB(새로 만들 때만 적재) → 서버 OFF · ON → vite OFF · ON → 프록시 OFF · ON → 상태 표.
# 멱등이다: 떠 있는 유닛 · 포트는 건너뛴다. DB 컨테이너가 떠 있으면 비밀 · 계정은 다시 만들지 않는다 (down.sh 뒤에만 새로 만든다).
# 메모리: MemAvailable 7GB 미만이면 멈춘다. 각 유닛은 MemoryMax=3G (systemd-run --user).
set -euo pipefail
here=$(cd "$(dirname "$0")" && pwd)
. "$here/lib.sh"
mkdir -p "$L/logs" "$L/run"
chmod 700 "$L"

avail_gb=$(awk '/MemAvailable/{print int($2/1048576)}' /proc/meminfo)
if [ "$avail_gb" -lt 7 ]; then
  echo "MemAvailable ${avail_gb}GB (7GB 미만): 기동하지 않는다" >&2
  exit 1
fi

FRESH=0
if docker ps --format '{{.Names}}' | grep -qx "$DB_NAME"; then
  echo "DB 컨테이너가 떠 있다: 비밀 · 계정은 그대로 쓴다"
else
  for u in $UNITS; do
    if unit_active "$u"; then
      echo "DB 없이 유닛 $u 가 떠 있다: down.sh 로 내린 뒤 다시 올린다" >&2
      exit 1
    fi
  done
  FRESH=1
  echo "새 비밀 생성 (L/.env.live · run/fcm.pem, 0600)"
  DBPW=$(openssl rand -hex 16)
  {
    echo "DB_HOST=127.0.0.1"
    echo "DB_PORT=$DB_PORT"
    echo "DB_USER=root"
    echo "DB_PASSWORD=$DBPW"
    echo "DB_DATABASE=insuniverse"
    echo "JWT_SECRET=$(openssl rand -hex 32)"
    echo "JWT_ACCESS_TOKEN_EXPIRY=1h"
    echo "JWT_ADMIN_ACCESS_TOKEN_EXPIRY=12h"
    echo "JWT_REFRESH_TOKEN_EXPIRY=1d"
    echo "JWT_MOK_TOKEN_EXPIRY=1h"
    echo "AES_SECRET=$(openssl rand -hex 16)"
    echo "AES_IV=$(openssl rand -hex 8)"
    echo "ADMIN_VIEW_SECRET=$(openssl rand -hex 32)"
    echo "REDIS_URL=redis://127.0.0.1:$REDIS_PORT"
    echo "ALLOW_ORIGINS=http://localhost:$PROXY_OFF_PORT,http://localhost:$PROXY_ON_PORT,http://127.0.0.1:$PROXY_OFF_PORT,http://127.0.0.1:$PROXY_ON_PORT"
    echo "CAR_VIEW_ADMIN_IDS=1,2"
    echo "NODE_ENV=dev"
    echo "FCM_CLIENT_EMAIL=rbac-live@synth.invalid"
    echo "FCM_PROJECT_ID=rbac-live-synth"
    echo "S3_BUCKET=rbac-live-synth"
    echo "ALERT_HEALTH_URL=http://127.0.0.1:9/health"
    echo "MAILER_HOST=127.0.0.1"
    echo "MAILER_PORT=9"
    # 외부 발송 · 결제 · 본인인증 주소는 전부 닫힌 포트로 덮는다 (실제 발송이 나가지 않게)
    for k in ALIGO_BASE_URL ALIGO_ALIMTALK_ENDPOINT ALIGO_HISTORY_DETAIL_ENDPOINT ALIGO_HISTORY_LIST_ENDPOINT \
      ALIGO_SMS_HISTORY_DETAIL_ENDPOINT ALIGO_TEMPLATE_LIST_ENDPOINT CDN_BASE_URL CODEF_DRUG_URL CODEF_NTS_MEDICAL_URL \
      CODEF_OAUTH_URL CODEF_TREAT_CAR_URL CODEF_TREAT_MULTI_URL CODEF_TREAT_URL COOCON_GW_URL DDX_BASE_URL \
      GOOGLE_CHAT_WEBHOOK_URL HYPHEN_BASE_URL INHOUSE_BASE_URL MOK_ORIGIN MOK_RESULT_AUTH_ENDPOINT MOK_RETURN_URL \
      MOK_SITE_URL OCTOVER_BASE_URL SMARTRO_APPROVE_ENDPOINT SMARTRO_BASE_URL SMARTRO_CANCEL_ENDPOINT TOSS_BASE_URL \
      TOSS_ISSUE_BILLING_ENDPOINT TOSS_PAYMENT_BILLING_ENDPOINT TOSS_PAYMENT_CANCEL_ENDPOINT USER_BASE_URL VFAX_BASE_URL \
      YOUTUBE_API_BASE_URL; do
      echo "$k=http://127.0.0.1:9"
    done
  } > "$L/.env.live.new"
  mv "$L/.env.live.new" "$L/.env.live"
  chmod 600 "$L/.env.live"
  openssl genrsa 2048 > "$L/run/fcm.pem" 2>/dev/null
  chmod 600 "$L/run/fcm.pem"

  docker rm -f "$DB_NAME" "$REDIS_NAME" >/dev/null 2>&1 || true
  echo "Redis 컨테이너 기동"
  docker run -d --name "$REDIS_NAME" -p "127.0.0.1:$REDIS_PORT:6379" redis:7 >/dev/null
  for i in $(seq 1 30); do
    [ "$(docker exec "$REDIS_NAME" redis-cli ping 2>/dev/null || true)" = PONG ] && break
    sleep 1
  done
  "$here/db-init.sh"
fi

# 빌드 산출이 없으면 워크트리에서 빌드한다 (서버 두 벌 · dist 는 워크트리 안에만 있다)
for s in "$OFF_SRV" "$ON_SRV"; do
  if [ ! -f "$s/dist/apps/api/apps/api/src/main.js" ]; then
    echo "빌드: $s"
    (cd "$s" && systemd-run --user --scope -q -p MemoryMax=3G -p MemorySwapMax=0 npx nest build) > "$L/logs/build-$(basename "$s").log" 2>&1
  fi
done

start_srv() {
  local name=$1 which=$2 port=$3
  if unit_active "$name"; then echo "유닛 $name 가 떠 있다 (건너뜀)"; return 0; fi
  if port_listening "$port"; then echo "포트 $port 가 이미 쓰이고 있다 (유닛 $name 기동 중단)" >&2; return 1; fi
  unit_run "$name" "$L" bash "$here/run-server.sh" "$which"
  wait_http "http://127.0.0.1:$port/live" 120 || { echo "서버 $which 가 기동하지 않았다 (logs/$name.log)" >&2; return 1; }
  echo "서버 $which 기동 (port $port)"
}
start_adm() {
  local name=$1 which=$2 port=$3 wd=$4
  if unit_active "$name"; then echo "유닛 $name 가 떠 있다 (건너뜀)"; return 0; fi
  if port_listening "$port"; then echo "포트 $port 가 이미 쓰이고 있다 (유닛 $name 기동 중단)" >&2; return 1; fi
  unit_run "$name" "$wd" bash "$here/run-vite.sh" "$which"
  wait_http "http://127.0.0.1:$port/" 180 || { echo "관리자 vite $which 가 뜨지 않았다 (logs/$name.log)" >&2; return 1; }
  echo "관리자 화면 $which 기동 (port $port)"
}
start_proxy() {
  local name=$1 label=$2 port=$3 api=$4 vite=$5
  if unit_active "$name"; then echo "유닛 $name 가 떠 있다 (건너뜀)"; return 0; fi
  if port_listening "$port"; then echo "포트 $port 가 이미 쓰이고 있다 (유닛 $name 기동 중단)" >&2; return 1; fi
  unit_run "$name" "$here" /usr/bin/env PROXY_LABEL="$label" PROXY_PORT="$port" API_PORT="$api" VITE_PORT="$vite" node "$here/proxy.js"
  wait_http "http://127.0.0.1:$port/" 30 || true
  echo "프록시 $label 기동 (listen 127.0.0.1:$port → API $api · vite $vite)"
}

start_srv rbac-live-srv-off off "$SRV_OFF_PORT"
start_srv rbac-live-srv-on on "$SRV_ON_PORT"
start_adm rbac-live-adm-off off "$ADM_OFF_PORT" "$OFF_ADM"
start_adm rbac-live-adm-on on "$ADM_ON_PORT" "$ON_ADM"
start_proxy rbac-live-proxy-off off "$PROXY_OFF_PORT" "$SRV_OFF_PORT" "$ADM_OFF_PORT"
start_proxy rbac-live-proxy-on on "$PROXY_ON_PORT" "$SRV_ON_PORT" "$ADM_ON_PORT"

echo
"$here/status.sh"
