#!/usr/bin/env bash
# =====================================================================
# 04_promote_failover.sh
# ---------------------------------------------------------------------
# 演示「主库挂了 → 从库提升为主库 → 业务继续写入」的 Failover 流程。
#
# 前置：已运行 02_setup_streaming_replication.sh up 起好集群。
# 用法：
#   ./04_promote_failover.sh demo       # 跑完整剧本
#   ./04_promote_failover.sh promote    # 只做提升
#   ./04_promote_failover.sh check      # 检查从库当前角色
#   ./04_promote_failover.sh rewind     # 用 pg_rewind 让旧主重新入集群
# =====================================================================

set -euo pipefail

MASTER="pg_master"
REPLICA="pg_replica"

step() {
    echo
    echo "========================================================"
    echo "  $*"
    echo "========================================================"
}

cmd_check() {
    echo "--- 主库角色 ---"
    docker exec "$MASTER" psql -U postgres -d learn_pg \
        -c "SELECT pg_is_in_recovery() AS in_recovery;" 2>/dev/null || echo "(主库不可达)"
    echo "--- 从库角色 ---"
    docker exec "$REPLICA" psql -U postgres -d learn_pg \
        -c "SELECT pg_is_in_recovery() AS in_recovery;"
}

cmd_promote() {
    step "在从库上调用 pg_promote()"
    docker exec "$REPLICA" psql -U postgres -d learn_pg -c \
        "SELECT pg_promote(wait => true, wait_seconds => 30);"
    sleep 2
    cmd_check
}

cmd_demo() {
    step "Step 1: 主库写一条标记数据"
    docker exec "$MASTER" psql -U postgres -d learn_pg -c \
        "INSERT INTO ch15_demo(msg) VALUES('failover-marker-A') RETURNING *;" || true

    sleep 1

    step "Step 2: 验证从库已经收到（流复制）"
    docker exec "$REPLICA" psql -U postgres -d learn_pg -c \
        "SELECT * FROM ch15_demo WHERE msg LIKE 'failover-marker%' ORDER BY id;"

    step "Step 3: 模拟主库故障 —— docker stop pg_master"
    docker stop "$MASTER"

    step "Step 4: 此时业务尝试连主库（应失败）"
    docker exec "$REPLICA" psql -h "$MASTER" -U postgres -d learn_pg \
        -c "SELECT 1;" 2>&1 | head -3 || echo "(预期失败)"

    step "Step 5: 把从库 promote 成新主库"
    docker exec "$REPLICA" psql -U postgres -d learn_pg -c \
        "SELECT pg_promote(wait => true, wait_seconds => 30);"
    sleep 2

    step "Step 6: 检查从库是否已经变主"
    docker exec "$REPLICA" psql -U postgres -d learn_pg -c \
        "SELECT pg_is_in_recovery() AS still_replica;"

    step "Step 7: 在新主库写入"
    docker exec "$REPLICA" psql -U postgres -d learn_pg -c \
        "INSERT INTO ch15_demo(msg) VALUES('failover-marker-B-on-new-master') RETURNING *;"

    step "Step 8: 看 timeline 切换"
    docker exec "$REPLICA" bash -c "ls /var/lib/postgresql/data/pg_wal/ | head -10"

    echo
    echo "✅ Failover 演示完毕。新主在 pg_replica（端口 5433）。"
    echo "   要让旧主重新加入，运行: ./04_promote_failover.sh rewind"
}

cmd_rewind() {
    step "用 pg_rewind 让旧主 pg_master 变成新主 pg_replica 的从库"
    echo "[1/4] 先启动旧主（启动后会以 standalone 模式起来）"
    docker start "$MASTER"
    sleep 3

    echo "[2/4] 立即 stop（避免它写入分叉数据）"
    docker exec "$MASTER" su postgres -c "pg_ctl -D /var/lib/postgresql/data stop -m fast" || true
    sleep 2

    echo "[3/4] 在旧主容器内执行 pg_rewind（从新主拉取差异块）"
    docker exec -e PGPASSWORD=postgres "$MASTER" su postgres -c \
        "pg_rewind --target-pgdata=/var/lib/postgresql/data \
                   --source-server='host=pg_replica port=5432 user=postgres dbname=postgres password=postgres' \
                   --progress" || {
            echo "⚠️  pg_rewind 失败。常见原因：旧主未开 wal_log_hints / data_checksums。"
            echo "    回退方案：删 data 目录，重新 pg_basebackup。"
            return 1
        }

    echo "[4/4] 写 standby.signal + primary_conninfo，启动旧主作为从库"
    docker exec "$MASTER" bash -c "
        touch /var/lib/postgresql/data/standby.signal
        cat >> /var/lib/postgresql/data/postgresql.auto.conf <<EOF
primary_conninfo = 'host=pg_replica port=5432 user=repl_user password=r3pl_pwd application_name=oldmaster'
EOF
        chown postgres:postgres /var/lib/postgresql/data/standby.signal
    "
    docker exec -d "$MASTER" su postgres -c "postgres -D /var/lib/postgresql/data"

    sleep 3
    echo
    echo "✅ rewind 完成。新主视角："
    docker exec "$REPLICA" psql -U postgres -d learn_pg -c \
        "SELECT application_name, client_addr, state FROM pg_stat_replication;"
}

case "${1:-}" in
    demo)    cmd_demo ;;
    promote) cmd_promote ;;
    check)   cmd_check ;;
    rewind)  cmd_rewind ;;
    *)
        echo "Usage: $0 {demo|promote|check|rewind}"
        exit 1
        ;;
esac
