#!/bin/bash
# One validated request, one supervisor restart. Never execute panel input.
set -uo pipefail
VAR_DIR="${M2_VAR_DIR:-/opt/metin2/var}"
SPOOL="${M2_RATES_SPOOL:-/opt/m2spool}"
STATE="$VAR_DIR/server-settings"
REQUEST="$SPOOL/server-settings.request"
READY="$SPOOL/server-settings.ready"
mkdir -p "$STATE" || exit 1
kv() { sed -n "s/^$2=//p" "$1" 2>/dev/null | head -1; }
publish() {
  local tmp="$SPOOL/server-settings.status.new.$$"
  { printf 'state=%s\npercent=%s\nmessage=%s\ntime=%s\n' "$1" "$2" "$3" "$(date +%s)"
    [[ ! -f "$STATE/history" ]] || cat "$STATE/history"
    printf 'id=%s\n' "$(cat "$STATE/last-id" 2>/dev/null)"
  } >"$tmp" && chmod 0660 "$tmp" && mv "$tmp" "$SPOOL/server-settings.status"
}
heartbeat() {
  local tmp="$READY.new.$$"
  { printf 'capability=server-settings\n'
    printf 'time=%s\n' "$(date +%s)"
  } >"$tmp" && chmod 0660 "$tmp" && mv "$tmp" "$READY"
}
finish() {
  local id
  id=$(cat "$STATE/last-id" 2>/dev/null)
  [[ "$(kv "$REQUEST" id)" != "$id" ]] || rm -f "$REQUEST"
}
record() {
  local source="${1:-external_unknown}" scope="${2:-server}" tmp="$STATE/history.new"
  if [[ "$scope" == server ]]; then
    printf 'last_restart_time=%s\nlast_restart_source=%s\n' "$(date +%s)" "$source" >"$tmp"
    [[ ! -f "$STATE/history" ]] || sed -n '/^last_core_/p' "$STATE/history" >>"$tmp"
  else
    { [[ ! -f "$STATE/history" ]] || sed -n '/^last_restart_/p' "$STATE/history"
      printf 'last_core_time=%s\nlast_core_name=%s\n' "$(date +%s)" "$scope"
    } >"$tmp"
  fi
  mv "$tmp" "$STATE/history"
  publish ok 100 'Serwer działa'
}
poll() {
  local id action value key rc work="$STATE/work" source rates_seen maps_seen
  [[ -f "$REQUEST" ]] || return 0
  id=$(kv "$REQUEST" id)
  [[ "$id" =~ ^[a-zA-Z0-9_-]{1,100}$ ]] || { publish failed 0 'Nieprawidłowe zlecenie'; rm -f "$REQUEST"; return 1; }
  [[ "$id" != "$(cat "$STATE/last-id" 2>/dev/null)" ]] || { finish; return 0; }
  printf '%s\n' "$id" >"$STATE/last-id" || return 1
  action=$(kv "$REQUEST" action); source=$(kv "$REQUEST" source)
  [[ "$source" == panel || "$source" == deployment ]] || source=external_unknown
  printf '%s\n' "$source" >"$STATE/source"
  if [[ "$action" == restart ]]; then publish running 10 'Zlecono restart'; return 10; fi
  [[ "$action" == apply ]] || { publish failed 0 'Nieprawidłowa akcja'; finish; return 1; }
  for key in exp drop yang; do
    value=$(kv "$REQUEST" "$key")
    [[ "$value" =~ ^[0-9]{1,5}$ ]] && ((10#$value>=1 && 10#$value<=10000)) || {
      publish failed 0 'Nieprawidłowe mnożniki'; finish; return 1;
    }
  done
  for key in 1 3 21 23 41 43 61 63 64 104 stone_1 stone_3 stone_21 stone_23 stone_41 stone_43 stone_61 stone_63 stone_64; do
    value=$(kv "$REQUEST" "map_$key")
    [[ -z "$value" || "$value" == reset ]] && continue
    [[ "$value" =~ ^[0-9]{1,4}$ ]] && ((10#$value>=1 && 10#$value<=3600)) || {
      publish failed 0 'Nieprawidłowy respawn'; finish; return 1;
    }
  done
  mkdir -p "$work" "$VAR_DIR/rates" "$VAR_DIR/map-regens" || return 1
  # Native helpers work in a private spool; the watch loop cannot pick up
  # either half separately. Save the previous intent for rollback on failure.
  if [[ -f "$VAR_DIR/rates/wanted" ]]; then cp "$VAR_DIR/rates/wanted" "$work/rates.old"; else printf 'exp=100\ndrop=100\nyang=100\n' >"$work/rates.old"; fi
  if [[ -f "$VAR_DIR/map-regens/wanted" ]]; then cp "$VAR_DIR/map-regens/wanted" "$work/maps.old"; else : >"$work/maps.old"; fi
  cp "$REQUEST" "$work/request" && cp "$REQUEST" "$work/map-regens.request" || return 1
  rates_seen=$(cat "$VAR_DIR/rates/last-id" 2>/dev/null)
  maps_seen=$(cat "$VAR_DIR/map-regens/last-id" 2>/dev/null)
  publish running 15 'Zapisywanie ustawień serwera'
  M2_RATES_SPOOL="$work" m2-rates poll; rc=$?
  if [[ "$rc" == 10 ]]; then
    if grep -q '^map_' "$REQUEST"; then M2_RATES_SPOOL="$work" m2-map-regens poll; rc=$?; fi
  fi
  # Keep legacy queue cursors independent, so old requests cannot be replayed.
  printf '%s\n' "$rates_seen" >"$VAR_DIR/rates/last-id"
  printf '%s\n' "$maps_seen" >"$VAR_DIR/map-regens/last-id"
  if [[ "$rc" != 10 ]] || ! m2-map-regens apply || ! m2-rates apply; then
    cp "$work/rates.old" "$VAR_DIR/rates/wanted"
    cp "$work/maps.old" "$VAR_DIR/map-regens/wanted"
    if m2-map-regens apply && m2-rates apply; then
      publish failed 0 'Nie zapisano zestawu. Przywrócono wcześniejsze ustawienia; bez restartu.'
    else publish failed 0 'Błąd zapisu i przywracania ustawień — sprawdź logi serwera'; fi
    m2-map-regens publish; m2-rates publish
    finish; return 1
  fi
  return 10
}
case "${1:-}" in
  heartbeat) heartbeat;;
  poll) heartbeat; poll;;
  publish) shift; publish "$@";;
  record) shift; record "$@";;
  complete) record "$(cat "$STATE/source" 2>/dev/null || echo external_unknown)"; finish;;
  failed) publish failed 0 'Nie wszystkie rdzenie uruchomiły się poprawnie'; finish;;
  *) exit 1;;
esac
