#!/usr/bin/env bash
#
# A04. PostgreSQL 16 설치 — OnBridge 전용 클러스터
#
#   설계: /opt/OnBridge/Docs/A04-PostgreSQL-설치-설계.md
#   실행: sudo bash /opt/OnBridge/Docs/A04-postgres-install.sh
#
# 멱등하지 않다. 최초 1회만 실행한다.
# 사전 점검(0단계)에서 기존 설치가 발견되면 아무것도 바꾸지 않고 중단한다.

set -euo pipefail

PG_VER=16
CLUSTER=onbridge
ROOT=/opt/OnBridge
PGDATA="$ROOT/data/postgres"
LOGDIR="$ROOT/logs/postgres"
CONFDIR="/etc/postgresql/$PG_VER/$CLUSTER"
ENVFILE="$ROOT/conf/onbridge.env"
APP_USER=bridge
DB_NAME=onbridge
DB_ROLE=onbridge
DB_PORT=5432

step() { echo; echo "── $* ────────────────────────────────"; }
die()  { echo "중단: $*" >&2; exit 1; }

# ── 0. 사전 점검 ──────────────────────────────────────────
step "0. 사전 점검"

[ "$(id -u)" -eq 0 ] || die "root로 실행해야 한다 (sudo bash $0)"
id "$APP_USER" >/dev/null 2>&1 || die "사용자 '$APP_USER'가 없다"

if dpkg -l 2>/dev/null | grep -qE '^ii +postgresql-[0-9]'; then
    die "PostgreSQL이 이미 설치되어 있다. 이 스크립트는 신규 설치 전용이다.
     기존 클러스터 확인: pg_lsclusters"
fi
if [ -e "$PGDATA" ] && [ -n "$(ls -A "$PGDATA" 2>/dev/null)" ]; then
    die "$PGDATA 가 비어 있지 않다. 데이터를 덮어쓰지 않기 위해 중단한다."
fi
if ss -ltn 2>/dev/null | grep -q ":$DB_PORT "; then
    die "포트 $DB_PORT 가 이미 사용 중이다."
fi
echo "OK — 기존 PostgreSQL 없음, $PGDATA 비어 있음, 포트 $DB_PORT 사용 가능"

# ── 1. 패키지 설치 ────────────────────────────────────────
step "1. postgresql-$PG_VER 설치"

export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y "postgresql-$PG_VER"
echo "설치된 버전: $(/usr/lib/postgresql/$PG_VER/bin/postgres --version)"

# ── 2. 자동 생성된 main 클러스터 제거 (D-4) ───────────────
step "2. 기본 'main' 클러스터 제거"

if pg_lsclusters -h 2>/dev/null | awk '{print $1, $2}' | grep -qx "$PG_VER main"; then
    # 패키지가 방금 만든 빈 클러스터다. 사용자 데이터가 있으면 여기까지 오지 않는다.
    pg_dropcluster --stop "$PG_VER" main
    echo "main 클러스터 제거 완료"
else
    echo "main 클러스터 없음 — 건너뜀"
fi

# ── 3. 디렉토리 구조 (A01 §4) ─────────────────────────────
step "3. /opt/OnBridge 디렉토리 생성"

install -d -o root   -g root   -m 755 "$ROOT/data"
install -d -o "$APP_USER" -g "$APP_USER" -m 755 "$ROOT/conf" "$ROOT/logs" "$ROOT/app"
install -d -o postgres -g postgres -m 750 "$LOGDIR"
# PGDATA 자체는 pg_createcluster가 postgres 소유 0700으로 만든다.
ls -ld "$ROOT"/{conf,data,logs,app} "$LOGDIR"

# ── 4. onbridge 클러스터 생성 (D-2 ~ D-7) ─────────────────
step "4. 클러스터 '$CLUSTER' 생성"

pg_createcluster "$PG_VER" "$CLUSTER" \
    --datadir="$PGDATA" \
    --logfile="$LOGDIR/postgresql-$PG_VER-$CLUSTER.log" \
    --port="$DB_PORT" \
    -- --encoding=UTF8 --locale=C.UTF-8 --data-checksums

# ── 5. 설정 (D-8, D-9) ────────────────────────────────────
step "5. 설정 확인 및 심볼릭 링크"

# 접속 범위를 localhost로 못박는다 (A01 O-5 미결 — 좁은 쪽이 기본).
if grep -qE "^[[:space:]]*#?[[:space:]]*listen_addresses" "$CONFDIR/postgresql.conf"; then
    sed -i "s|^[[:space:]]*#\?[[:space:]]*listen_addresses.*|listen_addresses = 'localhost'|" \
        "$CONFDIR/postgresql.conf"
else
    echo "listen_addresses = 'localhost'" >> "$CONFDIR/postgresql.conf"
fi
grep -E "^listen_addresses" "$CONFDIR/postgresql.conf"

ln -sfn "$CONFDIR" "$ROOT/conf/postgres"
chown -h "$APP_USER:$APP_USER" "$ROOT/conf/postgres"
echo "설정 링크: $ROOT/conf/postgres -> $CONFDIR"

# ── 6. 기동 ───────────────────────────────────────────────
step "6. 클러스터 기동 및 자동 시작 등록"

pg_ctlcluster "$PG_VER" "$CLUSTER" start
systemctl enable postgresql >/dev/null 2>&1 || true
pg_lsclusters

# ── 7. 역할 · 데이터베이스 · 접속 정보 (§6) ───────────────
step "7. 역할 '$DB_ROLE' / DB '$DB_NAME' 생성"

# 파이프를 쓰지 않는다. `tr ... | head -c` 는 head가 파이프를 닫을 때 tr이 SIGPIPE로
# 죽고, set -o pipefail 이 이를 실패로 판정해 스크립트가 중단된다.
DB_PASS="$(python3 -c 'import secrets,string; print("".join(secrets.choice(string.ascii_letters+string.digits) for _ in range(32)))')"

sudo -u postgres psql -p "$DB_PORT" -v ON_ERROR_STOP=1 <<SQL
CREATE ROLE $DB_ROLE LOGIN PASSWORD '$DB_PASS';
CREATE DATABASE $DB_NAME OWNER $DB_ROLE;
SQL

umask 077
cat > "$ENVFILE" <<ENV
# OnBridge DB 접속 정보 — A04 §6
# 생성: $(date '+%Y-%m-%d %H:%M:%S')
# 이 파일은 저장소·문서에 커밋하지 않는다.
ONBRIDGE_DB_HOST=127.0.0.1
ONBRIDGE_DB_PORT=$DB_PORT
ONBRIDGE_DB_NAME=$DB_NAME
ONBRIDGE_DB_USER=$DB_ROLE
ONBRIDGE_DB_PASSWORD=$DB_PASS
ENV
chown "$APP_USER:$APP_USER" "$ENVFILE"
chmod 600 "$ENVFILE"
echo "접속 정보 기록: $ENVFILE (0600, $APP_USER 소유)"

# ── 8. 검증 (§8) ──────────────────────────────────────────
step "8. 검증"

fail=0
check() { if [ "$2" = "$3" ]; then echo "  [OK]   $1: $2"; else echo "  [FAIL] $1: '$2' (기대값 '$3')"; fail=1; fi; }

check "클러스터 상태" \
      "$(pg_lsclusters -h | awk -v v="$PG_VER" -v c="$CLUSTER" '$1==v && $2==c {print $4}')" \
      "online"
check "main 클러스터 부재" \
      "$(pg_lsclusters -h | awk -v v="$PG_VER" '$1==v && $2=="main"' | wc -l)" \
      "0"
check "데이터 경로" \
      "$(sudo -u postgres psql -p "$DB_PORT" -tAc 'SHOW data_directory')" \
      "$PGDATA"
check "데이터 체크섬" \
      "$(sudo -u postgres psql -p "$DB_PORT" -tAc 'SHOW data_checksums')" \
      "on"
check "서버 인코딩" \
      "$(sudo -u postgres psql -p "$DB_PORT" -tAc 'SHOW server_encoding')" \
      "UTF8"
check "접속 범위" \
      "$(sudo -u postgres psql -p "$DB_PORT" -tAc 'SHOW listen_addresses')" \
      "localhost"
check "앱 계정 접속" \
      "$(PGPASSWORD="$DB_PASS" psql -h 127.0.0.1 -p "$DB_PORT" -U "$DB_ROLE" -d "$DB_NAME" -tAc 'SELECT 1')" \
      "1"
check "env 파일 권한" \
      "$(stat -c '%a %U' "$ENVFILE")" \
      "600 $APP_USER"

echo
if [ "$fail" -eq 0 ]; then
    echo "완료 — 8개 검증 항목 모두 통과."
    echo "접속: psql -h 127.0.0.1 -U $DB_ROLE -d $DB_NAME   (비밀번호는 $ENVFILE)"
else
    echo "일부 검증 실패. 위 [FAIL] 항목을 확인할 것." >&2
    exit 1
fi
