#!/bin/bash
source ~/.gamerc

# 以獨立 process group 執行, 讓逾時砍掉舊任務時能連同 rsync 子程序一起清掉
if [ -z "${SYNC_LEVELLOG_SETSID}" ]; then
        export SYNC_LEVELLOG_SETSID=1
        exec setsid "$0" "$@"
fi

XGA_LOG_IP="104.199.226.218"
RSYNC_APP_KEY="${SDK_APP_KEY}"           # 預設用 SDK_APP_KEY; 改用遊戲+地區組合: RSYNC_APP_KEY="${GAME_TYPE}${COUNTRY_CODE}"
LOCK_FILE="/tmp/sync_levellog.lock"
PID_FILE="/tmp/sync_levellog.pid"
MAX_RUNTIME=3600        # 前一次執行超過 60 分鐘未結束就強制終止
GCHAT_WEBHOOK_URL="https://chat.googleapis.com/v1/spaces/AAAAv2lQfRw/messages?key=AIzaSyDdI0hCZtE6vySjMm-WEfRq3CPzqKqqsHI&token=52LEIe0m5K9VosQx1T-BJwfLWeqSESkOPeAvvk0sFX4"

GATEKEEPER_URL="http://${XGA_LOG_IP}:8472"
QUEUE_WAIT_MAX=600      # 排隊最多等 10 分鐘, 排不到就放棄本輪, 留給下次 cron
QUEUE_POLL_INTERVAL=30  # 閘道沒回 retry_after 時的預設輪詢間隔 (秒)

notify_gchat() {
        local MESSAGE="$1"
        curl -s -X POST "${GCHAT_WEBHOOK_URL}" \
                -H "Content-Type: application/json" \
                -d "{\"text\": \"${MESSAGE}\"}" > /dev/null
}

# 結束時釋放閘道名額 (含被逾時機制 TERM 砍掉); 被 KILL 來不及釋放的由閘道 token TTL 兜底
GATE_TOKEN=""
release_gate() {
        if [ -n "${GATE_TOKEN}" ]; then
                curl -s --connect-timeout 10 -X POST "${GATEKEEPER_URL}/release" \
                        -H "Content-Type: application/json" \
                        -d "{\"token\":\"${GATE_TOKEN}\"}" > /dev/null
                GATE_TOKEN=""
        fi
}
trap release_gate EXIT
trap 'exit 143' TERM INT

# ===== 防止重複執行 =====
exec 9>"${LOCK_FILE}"
if ! flock -n 9; then
        OLD_PID=$(cat "${PID_FILE}" 2>/dev/null)
        if [ -n "${OLD_PID}" ] && kill -0 "${OLD_PID}" 2>/dev/null; then
                ELAPSED=$(ps -o etimes= -p "${OLD_PID}" 2>/dev/null | tr -d ' ')
                if [ "${ELAPSED:-0}" -ge "${MAX_RUNTIME}" ]; then
                        # 前一次執行已超過 60 分鐘, 砍掉整個 process group 後接手
                        kill -TERM -- -"${OLD_PID}" 2>/dev/null
                        sleep 5
                        kill -KILL -- -"${OLD_PID}" 2>/dev/null
                        if ! flock -w 30 9; then
                                notify_gchat "[sync_levellog] $(hostname) 無法取得 lock, 砍掉逾時舊任務 (PID ${OLD_PID}) 後仍失敗"
                                exit 1
                        fi
                        notify_gchat "[sync_levellog] $(hostname) 前次同步執行超過 60 分鐘 (PID ${OLD_PID}) 已強制終止, 重新開始同步"
                else
                        # 前一次還在執行且未逾時, 直接結束避免重複執行
                        exit 0
                fi
        else
                # lock 殘留但程序已不在, 短暫等待後接手
                flock -w 10 9 || exit 0
        fi
fi
echo $$ > "${PID_FILE}"

# ===== 申請 firewall ACL =====
game="${GAME_TYPE}-${COUNTRY_CODE_DISPLAY}"
servername="$(hostname)"
curl -s -X POST "https://app.x-legend.com/firewall-acl/api/apply" \
        -H "Content-Type: application/json" \
        -d "{\"service\":\"xlog\",\"game\":\"${game}\",\"servername\":\"${servername}\"}"

# ===== 向 xlog-gatekeeper 排隊取得傳輸許可 =====
QUEUE_WAITED=0
while true; do
        ACQUIRE_RESPONSE=$(curl -s --connect-timeout 10 -X POST "${GATEKEEPER_URL}/acquire" \
                -H "Content-Type: application/json" \
                -d "{\"service\":\"xlog\",\"game\":\"${game}\",\"servername\":\"${servername}\"}")
        if [ $? -ne 0 ] || [ -z "${ACQUIRE_RESPONSE}" ]; then
                # 閘道連不上 → fail-open 直接同步, 避免閘道故障導致所有 log 停擺
                notify_gchat "[sync_levellog] ${servername} 連不上 xlog-gatekeeper (${GATEKEEPER_URL}), 跳過排隊直接同步"
                break
        fi

        if echo "${ACQUIRE_RESPONSE}" | grep -q '"granted":true'; then
                GATE_TOKEN=$(echo "${ACQUIRE_RESPONSE}" | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
                break
        fi

        # 閘道明確拒絕排隊 (例如佇列已滿): 不是錯誤也不用等, 放棄本輪, 下次 cron 再試
        if echo "${ACQUIRE_RESPONSE}" | grep -q '"rejected":true'; then
                REJECT_REASON=$(echo "${ACQUIRE_RESPONSE}" | sed -n 's/.*"reason":"\([^"]*\)".*/\1/p')
                echo "閘道拒絕排隊 (${REJECT_REASON:-unknown}), 放棄本輪同步"
                exit 0
        fi

        RETRY_AFTER=$(echo "${ACQUIRE_RESPONSE}" | sed -n 's/.*"retry_after":\([0-9]*\).*/\1/p')
        RETRY_AFTER=${RETRY_AFTER:-${QUEUE_POLL_INTERVAL}}
        if [ $((QUEUE_WAITED + RETRY_AFTER)) -gt "${QUEUE_WAIT_MAX}" ]; then
                # 正常 backpressure, 不算錯誤; 放棄本輪, 下次 cron 再試
                echo "排隊等待超過 ${QUEUE_WAIT_MAX} 秒, 放棄本輪同步"
                exit 0
        fi
        sleep "${RETRY_AFTER}"
        QUEUE_WAITED=$((QUEUE_WAITED + RETRY_AFTER))
done

# ===== 同步 log =====
RSYNC_FAILURES=""

# 遠端 XLOG 遊戲名稱接收目錄不存在, 協助新增目錄.
rsync /dev/null ${XGA_LOG_IP}::UNCLEAN/${RSYNC_APP_KEY}/

for FULL_PATH_DIRECTORY in $(ls -hld ~/servers*/*Server* | egrep 'Mission|World|Zone' | grep -v core | awk -F" " '{print $NF}'); do
        #SET_NUMBER=$(basename ${FULL_PATH_DIRECTORY} | sed 's/.*Server//' | cut -c 1);
        #echo "${FULL_PATH_DIRECTORY} => ${SET_NUMBER}";

        DIRECTORY=$(basename ${FULL_PATH_DIRECTORY})
        # 遠端 XLOG 遊戲 Server 接收目錄不存在, 協助新增目錄.
        rsync /dev/null ${XGA_LOG_IP}::UNCLEAN/${RSYNC_APP_KEY}/${DIRECTORY}/

        # 同步各 Server 的 onelevel / twolevel 檔案到 XLOG
        rsync -avz ${FULL_PATH_DIRECTORY}/*level.log.* ${XGA_LOG_IP}::UNCLEAN/${RSYNC_APP_KEY}/${DIRECTORY}/
        RSYNC_EXIT_CODE=$?
        if [ ${RSYNC_EXIT_CODE} -ne 0 ]; then
                RSYNC_FAILURES="${RSYNC_FAILURES}\n- ${DIRECTORY} (exit code: ${RSYNC_EXIT_CODE})"
        fi
done

# ===== rsync 失敗發 Google Chat 通知 =====
if [ -n "${RSYNC_FAILURES}" ]; then
        notify_gchat "[sync_levellog] ${servername} (${game}) rsync 同步失敗:${RSYNC_FAILURES}"
fi

release_gate
rm -f "${PID_FILE}"

