#!/bin/bash
set -euo pipefail

SSH_OPTS=(-o StrictHostKeyChecking=no)

# Parse --mode flag
while [[ "${1:-}" == --* ]]; do
    case "$1" in
        --mode) MERGE_MODE="$2"; shift 2 ;;
        *) echo "Unknown option: $1" >&2; exit 1 ;;
    esac
done

usage() {
  echo "用法: $0 [--mode single|multiple] <new_merge_filename> <old_merge_filename> <restore_dbbackup_date (YYYYMMDD)> [localdb]" >&2
  exit 1
}

# 檢查參數數量
[[ $# -eq 3 || $# -eq 4 ]] || usage

new_merge_filename="$1"
old_merge_filename="$2"
restore_dbbackup_date="$3"

localdb_mode=0
if [[ $# -eq 4 ]]; then
  [[ "$4" == "localdb" ]] || { echo "ERROR: unknown 4th parameter: $4 (only allow: localdb)" >&2; exit 1; }
  localdb_mode=1
fi

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
new_file="${SCRIPT_DIR}/merge_n1/test/${new_merge_filename}"
old_file="${SCRIPT_DIR}/merge_n1/test/${old_merge_filename}"

# 檢查檔案是否存在
[[ -f "$new_file" ]] || { echo "找不到檔案: $new_file" >&2; exit 1; }
[[ -f "$old_file" ]] || { echo "找不到檔案: $old_file" >&2; exit 1; }

is_leap_year() {
  local y="$1"
  (( (y % 400 == 0) || (y % 4 == 0 && y % 100 != 0) ))
}

is_valid_yyyymmdd() {
  local s="$1"
  [[ "$s" =~ ^[0-9]{8}$ ]] || return 1

  local y="${s:0:4}"
  local m="${s:4:2}"
  local d="${s:6:2}"

  local yi=$((10#$y))
  local mi=$((10#$m))
  local di=$((10#$d))

  (( yi >= 1 )) || return 1
  (( mi >= 1 && mi <= 12 )) || return 1
  (( di >= 1 && di <= 31 )) || return 1

  local maxd
  case "$mi" in
    1|3|5|7|8|10|12) maxd=31 ;;
    4|6|9|11)        maxd=30 ;;
    2)
      if is_leap_year "$yi"; then maxd=29; else maxd=28; fi
      ;;
    *) return 1 ;;
  esac

  (( di <= maxd )) || return 1
  return 0
}

if ! is_valid_yyyymmdd "$restore_dbbackup_date"; then
  echo "ERROR: invalid date (expect YYYYMMDD) on the 3rd Parameter: $restore_dbbackup_date" >&2
  exit 1
fi

# MERGE_MODE 模式
_MODE="${MERGE_MODE:-single}"

# 依 SET_ID 取得 test 機器名
_get_test_machine() {
  local sid="$1"
  case "$_MODE" in
    single) echo "MERGE" ;;
    multiple)
      local area_id=$((10#$sid / 10))
      case "$area_id" in
        1) echo "MERGE_ASIA" ;;
        2) echo "MERGE_US" ;;
        3) echo "MERGE_EU" ;;
        *) echo "Error: unsupported SET_ID '$sid'" >&2; return 1 ;;
      esac
      ;;
  esac
}

# 取得 common 機器
_get_common_machine() {
  case "$_MODE" in
    single)   echo "MERGE" ;;
    multiple) echo "MERGE_COMMON" ;;
  esac
}

COMMON_MACHINE="$(_get_common_machine)"

########################################
# 前置檢查：確認環境準備就緒
########################################
echo "========================================"
echo "前置檢查開始"
echo "========================================"

# 取得所有需要檢查的機器
if [[ "$_MODE" == "single" ]]; then
  _ALL_MACHINES="MERGE"
else
  _ALL_MACHINES="MERGE_COMMON MERGE_ASIA MERGE_US MERGE_EU"
fi

_check_failed=0

# 1. SSH 連通測試
echo ">>> [1/3] SSH 連通測試..."
for machine in ${_ALL_MACHINES}; do
  if ssh "${SSH_OPTS[@]}" -o ConnectTimeout=5 "${machine}" "echo ok" &>/dev/null; then
    echo "  ✓ ${machine} 連通"
  else
    echo "  ✗ ${machine} 連線失敗" >&2
    _check_failed=1
  fi
done

# 2. 遠端 run_merge_n1_clean 目錄存在
echo ">>> [2/3] 遠端腳本目錄檢查..."
for machine in ${_ALL_MACHINES}; do
  if ssh "${SSH_OPTS[@]}" "${machine}" "test -d \$HOME/bin/run_merge_n1_clean" &>/dev/null; then
    echo "  ✓ ${machine} run_merge_n1_clean/ 存在"
  else
    echo "  ✗ ${machine} run_merge_n1_clean/ 不存在（請先執行 sync）" >&2
    _check_failed=1
  fi
done

# 3. 遠端合併清單檔案存在
echo ">>> [3/3] 遠端合併清單檔案檢查..."
for machine in ${_ALL_MACHINES}; do
  if ssh "${SSH_OPTS[@]}" "${machine}" "test -s \$HOME/bin/run_merge_n1_clean/merge_n1/test/${new_merge_filename}" &>/dev/null; then
    echo "  ✓ ${machine} merge_n1/test/${new_merge_filename} 存在"
  else
    echo "  ✗ ${machine} merge_n1/test/${new_merge_filename} 不存在或為空（請先執行 sync）" >&2
    _check_failed=1
  fi
done

if [[ "$_check_failed" -ne 0 ]]; then
  echo ""
  echo "========================================"
  echo "前置檢查失敗，請先解決上述問題再執行 prepare"
  echo "========================================"
  exit 1
fi

# 4. 從合併清單取得涉及的 SET_ID，檢查遠端 servers 目錄是否存在，不存在則自動建置
echo ">>> [4/4] 遠端 servers 目錄檢查 + 自動建置..."
declare -A _pre_set_seen=()
while IFS= read -r _pre_line || [[ -n "$_pre_line" ]]; do
  _pre_line="${_pre_line//$'\r'/}"
  [[ -z "$_pre_line" ]] && continue
  for _pre_wid in $_pre_line; do
    [[ "$_pre_wid" =~ ^[0-9]+$ ]] || continue
    _pre_sid=$((10#$_pre_wid / 100))
    _pre_set_seen["$_pre_sid"]=1
  done
done < "$new_file"

_pre_uniq_set_ids="$(printf '%s\n' "${!_pre_set_seen[@]}" | sort -n | xargs)"
echo "  涉及的 SET_IDs: ${_pre_uniq_set_ids}"

# 4a. 按合併機分組（multiple 模式下，不同 SET_ID 對應不同機器）
declare -A _pre_machine_sids=()
for _pre_sid in ${_pre_uniq_set_ids}; do
  _pre_machine="$(_get_test_machine "$_pre_sid")" || { echo "  ✗ 無法取得 SET_ID=${_pre_sid} 對應的機器" >&2; _check_failed=1; continue; }
  _pre_machine_sids["$_pre_machine"]+="${_pre_sid} "
done

# 4b. 對每台合併機處理：先把多餘的 servers→zervers，再補缺的
for _pre_machine in "${!_pre_machine_sids[@]}"; do
  _pre_expected_sids="${_pre_machine_sids[$_pre_machine]}"
  echo "  --- ${_pre_machine} (預期: ${_pre_expected_sids}) ---"

  # 列出該機器上所有 servers?? 目錄
  _pre_existing_sids=$(ssh "${SSH_OPTS[@]}" "${_pre_machine}" "ls -d \$HOME/servers* 2>/dev/null | xargs -I{} basename {} | sed 's/^servers//' | grep -E '^[0-9]+$' | sort -n" 2>/dev/null)

  # 將多餘的 servers??（不在合併清單）改名為 zervers??
  # servers1 為特殊目錄，存在就保留不動
  for _ex_sid in ${_pre_existing_sids}; do
    if [[ "${_ex_sid}" == "1" ]]; then
      continue
    fi
    if ! echo " ${_pre_expected_sids} " | grep -q " ${_ex_sid} "; then
      echo "  → 多餘 servers${_ex_sid}/ 改名為 zervers${_ex_sid}/"
      ssh "${SSH_OPTS[@]}" "${_pre_machine}" "mv \$HOME/servers${_ex_sid} \$HOME/zervers${_ex_sid}" || { echo "  ✗ ${_pre_machine} servers${_ex_sid} → zervers${_ex_sid} 改名失敗" >&2; _check_failed=1; }
    fi
  done

  # 對合併清單需要的 SET_ID 處理
  for _pre_sid in ${_pre_expected_sids}; do
    if ssh "${SSH_OPTS[@]}" "${_pre_machine}" "test -d \$HOME/servers${_pre_sid}" &>/dev/null; then
      echo "  ✓ ${_pre_machine} servers${_pre_sid}/ 存在"
    elif ssh "${SSH_OPTS[@]}" "${_pre_machine}" "test -d \$HOME/zervers${_pre_sid}" &>/dev/null; then
      echo "  → ${_pre_machine} zervers${_pre_sid}/ 改名回 servers${_pre_sid}/"
      ssh "${SSH_OPTS[@]}" "${_pre_machine}" "mv \$HOME/zervers${_pre_sid} \$HOME/servers${_pre_sid}" || { echo "  ✗ ${_pre_machine} zervers${_pre_sid} → servers${_pre_sid} 改名失敗" >&2; _check_failed=1; continue; }
    else
      echo "  ✗ ${_pre_machine} servers${_pre_sid}/ 與 zervers${_pre_sid}/ 都不存在，開始自動建置..."
      ssh "${SSH_OPTS[@]}" "${_pre_machine}" "source \$HOME/.gamerc; cd \$HOME && \
        echo yes | SKServer BuildIfNotExist SetDir ${_pre_sid} && \
        if [ \"\$ENABLE_SOCIETY\" == \"1\" ]; then \
          echo yes | SKServer BuildIfNotExist SOCIETYDB ${_pre_sid} && \
          echo yes | SKServer BuildIfNotExist SocietyS ${_pre_sid}; \
        fi && \
        echo yes | SKServer BuildIfNotExist GAMEDB ${_pre_sid} && \
        echo yes | SKServer BuildIfNotExist GameDBS ${_pre_sid} && \
        echo yes | SKServer BuildIfNotExist Mission ${_pre_sid} && \
        echo yes | SKServer BuildIfNotExist RankS ${_pre_sid} && \
        echo yes | SKServer BuildIfNotExist ChatS ${_pre_sid}" || { echo "  ✗ ${_pre_machine} SET_ID=${_pre_sid} 建置失敗" >&2; _check_failed=1; continue; }

      echo "  更新 setup.ini 設定..."
      ssh "${SSH_OPTS[@]}" "${_pre_machine}" "source \$HOME/.gamerc; cd \$HOME && \
        if [ \"\$ENABLE_SOCIETY\" == \"1\" ]; then \
          update_society_ip EXT && \
          update_society_ip INT && \
          update_empty_society_port EXT && \
          update_empty_society_port INT; \
        fi && \
        update_empty_gds_port && \
        update_empty_ms_ip && \
        update_empty_ranks_port && \
        update_empty_chats_port" || { echo "  ✗ ${_pre_machine} setup.ini 更新失敗" >&2; _check_failed=1; continue; }

      # 建置後再確認
      if ssh "${SSH_OPTS[@]}" "${_pre_machine}" "test -d \$HOME/servers${_pre_sid}" &>/dev/null; then
        echo "  ✓ ${_pre_machine} servers${_pre_sid}/ 建置完成"
      else
        echo "  ✗ ${_pre_machine} servers${_pre_sid}/ 建置後仍不存在" >&2
        _check_failed=1
      fi
    fi
  done
done

if [[ "$_check_failed" -ne 0 ]]; then
  echo ""
  echo "========================================"
  echo "前置檢查失敗，請先解決上述問題再執行 prepare"
  echo "========================================"
  exit 1
fi

echo ""
echo "========================================"
echo "前置檢查全部通過"
echo "========================================"
echo ""

# 一次讀檔：每行可能有多個 world_id，必須逐欄位處理
declare -a total_arr=()
declare -A set_seen=()

while IFS= read -r line || [[ -n "$line" ]]; do
  line="${line//$'\r'/}"  # 去除 DOS \r
  [[ -n "$line" ]] || continue

  for wid in $line; do
    [[ "$wid" =~ ^[0-9]+$ ]] || continue

    total_arr+=("$wid")
    sid=$((10#$wid / 100))
    set_seen["$sid"]=1
  done
done < "$new_file"

sorted_total="$(printf '%s\n' "${total_arr[@]}" | sort -n | xargs)"
uniq_set_id="$(printf '%s\n' "${!set_seen[@]}" | sort -n | xargs)"

echo "sorted_total=$sorted_total"
echo "uniq_set_id=$uniq_set_id"

# 分組與排序：sorted_s[$set_id] = 該 set 的 world_id 清單（升冪）
declare -A s
declare -A sorted_s

for set_id in $uniq_set_id; do
  s["$set_id"]=""
  sorted_s["$set_id"]=""
done

for wid in "${total_arr[@]}"; do
  sid=$((10#$wid / 100))
  [[ -n "${s[$sid]+x}" ]] || continue
  s["$sid"]+="$wid "
done

for set_id in $uniq_set_id; do
  sorted_s["$set_id"]="$(printf '%s\n' ${s[$set_id]} | sort -n | xargs)"
  echo "sorted_s${set_id}=${sorted_s[$set_id]}"
done

# MACHINE_AREA = 各 SET_ID 對應的合併機（去重）
# MACHINE_ALL  = COMMON_MACHINE ∪ MACHINE_AREA（去重）
# 注意：single mode 下 COMMON 跟 AREA 是同一台（MERGE），ALL 也只有一台
declare -a MACHINE_AREA_ARR=()
declare -A machine_seen=()

for set_id in $uniq_set_id; do
  MERGE_MACHINE="$(_get_test_machine "$set_id")" || continue
  if [[ -z "${machine_seen[$MERGE_MACHINE]+x}" ]]; then
    MACHINE_AREA_ARR+=("$MERGE_MACHINE")
    machine_seen["$MERGE_MACHINE"]=1
  fi
done

declare -a MACHINE_ALL_ARR=()
if [[ -z "${machine_seen[$COMMON_MACHINE]+x}" ]]; then
  MACHINE_ALL_ARR=("$COMMON_MACHINE" "${MACHINE_AREA_ARR[@]}")
else
  MACHINE_ALL_ARR=("${MACHINE_AREA_ARR[@]}")
fi

echo "MACHINE_ALL=${MACHINE_ALL_ARR[*]}"
echo "MACHINE_AREA=${MACHINE_AREA_ARR[*]}"

if (( localdb_mode == 1 )); then
  echo "INFO: localdb mode enabled; skip remote DB backup fetch block."
else
  # 合併機建立 DB 還原目錄
  ssh "${SSH_OPTS[@]}" "$COMMON_MACHINE" "mkdir -p /tmp/merge_${new_merge_filename}_test/accountdb"
  for machine in "${MACHINE_AREA_ARR[@]}"; do
    ssh "${SSH_OPTS[@]}" "$machine" "mkdir -p /tmp/merge_${new_merge_filename}_test/{gamedb,worlddb}"
  done

  # 同步需要合併日期的 DB 檔案到合併機
  # AccountDB
  sendscript accountdb <<< "cd DBbackup; rsync -e 'ssh -o StrictHostKeyChecking=no' -avz --exclude='*Post*' --exclude='*Pre*' *${restore_dbbackup_date}*.sql ${COMMON_MACHINE}:/tmp/merge_${new_merge_filename}_test/accountdb/." || true

  # GameDB
  for set_id in $uniq_set_id; do
    MERGE_MACHINE="$(_get_test_machine "$set_id")" || continue

    sendscript "ms${set_id}" <<< "cd DBbackup; rsync -e 'ssh -o StrictHostKeyChecking=no' -avz --exclude='*Post*' --exclude='*Pre*' *${restore_dbbackup_date}*.sql ${MERGE_MACHINE}:/tmp/merge_${new_merge_filename}_test/gamedb/." || true
  done

  # WorldDB
  for set_id in $uniq_set_id; do
    MERGE_MACHINE="$(_get_test_machine "$set_id")" || continue

    [[ -n "${sorted_s[$set_id]}" ]] || continue

    for world_id in ${sorted_s[$set_id]}; do
      ssh "${SSH_OPTS[@]}" "ws${world_id}" \
        "cd DBbackup && rsync -e 'ssh -o StrictHostKeyChecking=no' -avz \
        --exclude='*Post*' --exclude='*Pre*' \
        N1World${world_id}-${restore_dbbackup_date}*.sql \
        ${MERGE_MACHINE}:/tmp/merge_${new_merge_filename}_test/worlddb/."
    done
  done
fi

# 刪除 DB 前先停 Game Servers
echo ">>> 停止合併機 Game Servers..."
if [[ "$_MODE" == "single" ]]; then
  _STOP_MACHINES="MERGE"
else
  _STOP_MACHINES="MERGE_EU MERGE_US MERGE_ASIA MERGE_COMMON"
fi
for machine in ${_STOP_MACHINES}; do
  echo "  Stopping ${machine}..."
  ssh "${SSH_OPTS[@]}" "${machine}" "\$HOME/bin/SKServer stop all" || echo "  WARNING: ${machine} stop failed (may not be running)"
done

# 刪除合併機 舊的 DB
for machine in "${MACHINE_ALL_ARR[@]}"; do
  ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/drop_database_for_current_machine_by_pattern postfix _${old_merge_filename}_Pre 0"
  ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/drop_database_for_current_machine_by_pattern postfix _${old_merge_filename}_Post 0"
done

ssh "${SSH_OPTS[@]}" "$COMMON_MACHINE" "psql -U postgres -c 'DROP DATABASE IF EXISTS \"N1Account\";'"
ssh "${SSH_OPTS[@]}" "$COMMON_MACHINE" "psql -U postgres -c 'DROP DATABASE IF EXISTS \"N1Member\";'"

for machine in "${MACHINE_AREA_ARR[@]}"; do
  case "$_MODE" in
    single)
      ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/drop_database_for_current_machine_by_pattern prefix N1DB 0"
      ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/drop_database_for_current_machine_by_pattern prefix N1World 0"
      ;;
    multiple)
      ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/drop_database_for_current_machine_by_pattern prefix N1 0"
      ;;
  esac
done

# 還原新的合併 DB
case "$_MODE" in
  single)
    for machine in "${MACHINE_ALL_ARR[@]}"; do
      ssh "${SSH_OPTS[@]}" "$machine" "cd /tmp/merge_${new_merge_filename}_test/accountdb/; $HOME/bin/create_database_on_local_machine_from_backup_file"
      ssh "${SSH_OPTS[@]}" "$machine" "cd /tmp/merge_${new_merge_filename}_test/gamedb/; $HOME/bin/create_database_on_local_machine_from_backup_file"
      ssh "${SSH_OPTS[@]}" "$machine" "cd /tmp/merge_${new_merge_filename}_test/worlddb/; $HOME/bin/create_database_on_local_machine_from_backup_file"
    done
    ;;
  multiple)
    for machine in "${MACHINE_ALL_ARR[@]}"; do
      if [[ "$machine" == "MERGE_COMMON" ]]; then
        ssh "${SSH_OPTS[@]}" "$machine" "cd /tmp/merge_${new_merge_filename}_test/accountdb/; $HOME/bin/create_database_on_local_machine_from_backup_file"
      else
        ssh "${SSH_OPTS[@]}" "$machine" "cd /tmp/merge_${new_merge_filename}_test/gamedb/; $HOME/bin/create_database_on_local_machine_from_backup_file"
        ssh "${SSH_OPTS[@]}" "$machine" "cd /tmp/merge_${new_merge_filename}_test/worlddb/; $HOME/bin/create_database_on_local_machine_from_backup_file"
      fi
    done
    ;;
esac

# 新的合併 DB, Schema 更新 ( AccountDB / GameDB )
case "$_MODE" in
  single)
    for machine in "${MACHINE_ALL_ARR[@]}"; do
      ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/do_dbpatch_for_current_machine_n1 ACCOUNTDB"
      ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/do_dbpatch_for_current_machine_n1 GAMEDB"
    done
    ;;
  multiple)
    for machine in "${MACHINE_ALL_ARR[@]}"; do
      if [[ "$machine" == "MERGE_COMMON" ]]; then
        ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/do_dbpatch_for_current_machine_n1 ACCOUNTDB"
      else
        ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/do_dbpatch_for_current_machine_n1 GAMEDB"
      fi
    done
    ;;
esac

# 測試合併機 AccountDB 還原測試機最新版本的所有資料表
ssh "${SSH_OPTS[@]}" TEST "$HOME/bin/dump_all_promotion_tables $COMMON_MACHINE"

# 新的合併, World 相關目錄建立
for machine in "${MACHINE_AREA_ARR[@]}"; do
  ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/test_merge_create_worldserver_directory_n1 ${new_merge_filename}"
done

# 新的合併 DB, Schema 更新 ( WorldDB )
for machine in "${MACHINE_AREA_ARR[@]}"; do
  ssh "${SSH_OPTS[@]}" "$machine" "\$HOME/bin/run_merge_n1_clean/do_dbpatch_for_current_machine_n1 WORLDDB"
done

# 合併 worlds 資料處理
ssh "${SSH_OPTS[@]}" "$COMMON_MACHINE" "MERGE_MODE=${_MODE} \$HOME/bin/run_merge_n1_clean/test_merge_update_worlds_n1 ${new_merge_filename}"
