#!/usr/bin/env bash
set -euo pipefail

# =========================
# Lineage2 Docker Full Setup (Ubuntu)
# - installs deps (optionally)
# - (optionally) configures firewall/ports
# - unpacks server from .rar (optionally)
# - prepares SQL dump for first DB init (optionally)
# - patches L2 configs (optionally)
# - patches docker-compose.yml (optionally)
# - docker compose up (optionally fresh)
# - applies GRANTS for stored procedures/inventory (optionally)
# - restarts services (optionally)
# - health checks + logs (optionally)
# =========================

BASE="/srv/l2server"

# Helper scripts that may exist already (optional)
UNPACK_SCRIPT="${BASE}/unpack_l2server_rar.sh"
POST_SCRIPT="${BASE}/post_unpack_setup.sh"

# Defaults
DB_NAME="interlude"
DB_USER="l2juser"
DB_PASS="143700"
DB_ROOT_PASS="rootpass"

# L2 ports
PORT_LOGIN="2106"
PORT_GAME="7777"
PORT_STATUS="9014"
PORT_MYSQL="3306"

# Memory tuning
GS_XMX="1024m"
AUTH_MEM_LIMIT="384m"
GS_MEM_LIMIT="1600m"

# AuthServer config paths (if present)
AUTH_PROPS="${BASE}/authserver/config/authserver.properties"
AUTH_DEF_RU="${BASE}/authserver/config/default_russian/authserver.properties"
AUTH_DEF_EN="${BASE}/authserver/config/default_english/authserver.properties"

# Stable dump filename for compose mount
DUMP_STABLE="${BASE}/interlude_backup.sql"

# -------------------------
# Logging helpers
# -------------------------
log(){ echo "[+] $*"; }
warn(){ echo "[!] $*" >&2; }
die(){ echo "[x] $*" >&2; exit 1; }

need_root(){
  if [[ "${EUID}" -ne 0 ]]; then
    die "Запусти от root: sudo $0 --help"
  fi
}

have_cmd(){ command -v "$1" >/dev/null 2>&1; }

ensure_base_dirs(){
  mkdir -p "${BASE}"
  cd "${BASE}"
}

# -------------------------
# User detection (for chown + docker group)
# -------------------------
detect_primary_user(){
  # prefer real sudo user, else best-effort:
  if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
    echo "${SUDO_USER}"
    return 0
  fi

  # pick first human user with home under /home
  local u=""
  u="$(awk -F: '$3>=1000 && $3<65534 {print $1}' /etc/passwd | head -n 1 || true)"
  if [[ -n "$u" ]]; then
    echo "$u"
  else
    echo "root"
  fi
}

# -------------------------
# Docker group management
# -------------------------
ensure_docker_group(){
  local u="$1"
  
  if [[ "$u" == "root" ]]; then
    return 0  # root уже имеет доступ
  fi
  
  if getent group docker >/dev/null 2>&1; then
    # Проверяем, состоит ли пользователь уже в группе docker
    if id -nG "$u" | grep -qw docker; then
      log "Пользователь '$u' уже в группе docker"
      return 0
    else
      # Добавляем пользователя в группу docker
      if usermod -aG docker "$u" 2>/dev/null; then
        log "Добавлен '$u' в группу docker"
        return 0
      else
        warn "Не удалось добавить '$u' в группу docker (команда: usermod -aG docker $u)"
        return 1
      fi
    fi
  else
    warn "Группа docker не существует"
    return 1
  fi
}

# -------------------------
# APT / Docker installation (robust)
# -------------------------
ubuntu_codename(){
  . /etc/os-release
  echo "${VERSION_CODENAME:-}"
}

ensure_docker_repo(){
  # Adds Docker official repo if missing (Ubuntu).
  # Safe to run multiple times.
  local codename arch keyring listfile
  codename="$(ubuntu_codename)"
  [[ -n "$codename" ]] || die "Не могу определить VERSION_CODENAME из /etc/os-release"

  arch="$(dpkg --print-architecture)"
  keyring="/etc/apt/keyrings/docker.gpg"
  listfile="/etc/apt/sources.list.d/docker.list"

  apt-get update -y
  apt-get install -y ca-certificates curl gnupg lsb-release

  install -m 0755 -d /etc/apt/keyrings

  if [[ ! -f "$keyring" ]]; then
    log "Добавляю GPG ключ Docker..."
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o "$keyring"
    chmod a+r "$keyring"
  fi

  if [[ ! -f "$listfile" ]] || ! grep -q "download.docker.com/linux/ubuntu" "$listfile" 2>/dev/null; then
    log "Добавляю репозиторий Docker..."
    echo \
"deb [arch=${arch} signed-by=${keyring}] https://download.docker.com/linux/ubuntu ${codename} stable" \
      > "$listfile"
  fi

  apt-get update -y
}

install_deps(){
  local want_nginx="${1:-1}"
  local want_tools="${2:-1}"  # vim/unrar/unzip/net-tools
  local want_ufw="${3:-1}"

  log "STEP install: ставлю пакеты..."

  apt-get update -y

  if [[ "$want_tools" -eq 1 ]]; then
    apt-get install -y vim curl wget unzip unrar net-tools
  else
    log "STEP install: tools пропущены (--skip-tools)."
  fi

  if [[ "$want_ufw" -eq 1 ]]; then
    apt-get install -y ufw
  else
    log "STEP install: ufw пропущен (--skip-firewall/--no-firewall)."
  fi

  # Remove Ubuntu-packaged docker bits if present (avoid conflicts)
  log "STEP install: чищу конфликтующие docker пакеты (если есть)..."
  apt-get remove -y docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc >/dev/null 2>&1 || true

  # Ensure Docker repo exists and install Docker CE + plugins
  log "STEP install: проверяю/добавляю docker repo + ставлю docker-ce..."
  ensure_docker_repo

  # Install docker packages (idempotent)
  apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

  systemctl enable --now docker

  if [[ "$want_nginx" -eq 1 ]]; then
    apt-get install -y nginx
    systemctl enable --now nginx
  else
    log "STEP install: nginx пропущен (--skip-nginx/--no-nginx)."
  fi

  # Add primary user to docker group
  local u
  u="$(detect_primary_user)"
  if [[ "$u" != "root" ]] && id "$u" >/dev/null 2>&1; then
    if ensure_docker_group "$u"; then
      log "STEP install: пользователь '$u' добавлен в группу docker"
    else
      warn "STEP install: не удалось добавить '$u' в группу docker"
    fi
  fi
}

ensure_docker_ready(){
  have_cmd docker || die "docker не найден. Либо запусти с --skip-install и поставь docker вручную, либо без --skip-install."
  systemctl is-active --quiet docker || systemctl start docker || true
  docker info >/dev/null 2>&1 || die "docker daemon недоступен. Проверь: systemctl status docker"
  docker compose version >/dev/null 2>&1 || die "docker compose plugin не найден (docker compose)."
}

# -------------------------
# Firewall (ufw) - optional
# -------------------------
configure_firewall(){
  log "STEP firewall: ufw allow SSH + L2 ports + (опц.) MySQL ${PORT_MYSQL}..."

  have_cmd ufw || die "ufw не найден (поставь или запусти без firewall шага)."

  # Safe mode: do not reset existing rules. Just add/ensure.
  ufw default deny incoming >/dev/null 2>&1 || true
  ufw default allow outgoing >/dev/null 2>&1 || true

  ufw allow OpenSSH >/dev/null 2>&1 || ufw allow 22/tcp >/dev/null 2>&1 || true
  ufw allow "${PORT_LOGIN}/tcp" >/dev/null 2>&1 || true
  ufw allow "${PORT_GAME}/tcp" >/dev/null 2>&1 || true
  ufw allow "${PORT_STATUS}/tcp" >/dev/null 2>&1 || true

  # Optional if you want remote DB access (dangerous)
  if [[ "${EXPOSE_MYSQL:-0}" -eq 1 ]]; then
    ufw allow "${PORT_MYSQL}/tcp" >/dev/null 2>&1 || true
  fi

  ufw --force enable >/dev/null 2>&1 || true
  ufw status numbered || true
}

# -------------------------
# External IP detection
# -------------------------
detect_external_ip(){
  local ip=""
  ip="$(timeout 5s curl -fsS ifconfig.me 2>/dev/null || true)"
  if [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
    echo "$ip"
  else
    echo ""
  fi
}

# -------------------------
# Find inputs (RAR / SQL dump)
# -------------------------
find_any_rar(){
  find / -maxdepth 6 -type f -name "*.rar" 2>/dev/null | head -n 1 || true
}

find_any_dump(){
  local best=""
  best="$(ls -1t /home/*/interlude_backup_*.sql 2>/dev/null | head -n 1 || true)"
  if [[ -n "$best" && -f "$best" ]]; then echo "$best"; return 0; fi

  best="$(ls -1t "${BASE}"/interlude_backup_*.sql 2>/dev/null | head -n 1 || true)"
  if [[ -n "$best" && -f "$best" ]]; then echo "$best"; return 0; fi

  echo ""
}

# -------------------------
# Unpack + Post setup
# -------------------------
run_unpack(){
  local rar_path="${1:-}"
  ensure_base_dirs

  if [[ -n "$rar_path" ]]; then
    [[ -f "$rar_path" ]] || die "RAR не найден: $rar_path"
    log "STEP unpack: распаковка RAR (явно): $rar_path"
    unrar x -o+ "$rar_path" /
  else
    if [[ -x "$UNPACK_SCRIPT" ]]; then
      log "STEP unpack: использую helper: $UNPACK_SCRIPT"
      cd /
      "$UNPACK_SCRIPT"
    else
      log "STEP unpack: helper не найден. Ищу *.rar..."
      local found
      found="$(find_any_rar)"
      [[ -n "$found" ]] || die "Не найден *.rar (искал до глубины 6 от /)."
      log "STEP unpack: найден архив: $found"
      unrar x -o+ "$found" /
    fi
  fi

  [[ -d "${BASE}/authserver" ]] || die "После распаковки нет ${BASE}/authserver"
  [[ -d "${BASE}/gameserver" ]] || die "После распаковки нет ${BASE}/gameserver"
}

run_post_unpack(){
  ensure_base_dirs
  log "STEP post: логи/права/chmod..."

  local u
  u="$(detect_primary_user)"

  if [[ -x "$POST_SCRIPT" ]]; then
    "$POST_SCRIPT"
    return 0
  fi

  mkdir -p "${BASE}/authserver/log" "${BASE}/gameserver/log"

  # chown only if user exists; otherwise skip safely
  if [[ "$u" != "root" ]] && id "$u" >/dev/null 2>&1; then
    chown -R "$u:$u" "${BASE}/authserver" "${BASE}/gameserver" || true
  else
    warn "Пользователь '$u' не найден или это root — пропускаю chown."
  fi

  find "${BASE}/authserver" -type f -name "*.sh" -exec chmod +x {} \; || true
  find "${BASE}/gameserver" -type f -name "*.sh" -exec chmod +x {} \; || true
}

# -------------------------
# Dump handling
# -------------------------
ensure_dump_file(){
  local dump_path="${1:-}"
  ensure_base_dirs
  log "STEP dump: подготовка дампа -> ${DUMP_STABLE}"

  if [[ -z "$dump_path" ]]; then
    dump_path="$(find_any_dump)"
  fi

  [[ -n "$dump_path" ]] || die "SQL-дамп не найден. Передай: --dump /path/interlude_backup_*.sql"
  [[ -f "$dump_path" ]] || die "Дамп должен быть файлом: $dump_path"

  cp -f "$dump_path" "$DUMP_STABLE"
  chmod 0644 "$DUMP_STABLE" || true

  local sz
  sz="$(stat -c '%s' "$DUMP_STABLE" 2>/dev/null || echo 0)"
  if [[ "$sz" -lt 1000000 ]]; then
    warn "Дамп подозрительно маленький (${sz} bytes)."
  fi
}

# -------------------------
# DB init script
# -------------------------
ensure_db_init(){
  ensure_base_dirs
  log "STEP db_init: создаю db-init/01-init.sql (если нет)"

  mkdir -p "${BASE}/db-init"
  local init="${BASE}/db-init/01-init.sql"
  if [[ -f "$init" ]]; then
    log "STEP db_init: уже есть ${init} — не трогаю."
    return 0
  fi

  cat >"$init" <<SQL
CREATE DATABASE IF NOT EXISTS ${DB_NAME}
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

CREATE USER IF NOT EXISTS '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%';
FLUSH PRIVILEGES;
SQL
}

# -------------------------
# Patch L2 configs
# -------------------------
patch_configs(){
  ensure_base_dirs
  log "STEP patch: правлю конфиги L2..."

  local gs_props
  gs_props="$(find "${BASE}/gameserver/config" -maxdepth 1 -type f -name "server.properties" | head -n 1 || true)"
  [[ -n "$gs_props" ]] || die "Не найден server.properties в gameserver (depth<=6)."

  local ext_ip
  ext_ip="$(detect_external_ip)"
  if [[ -n "$ext_ip" ]]; then
    log "STEP patch: Внешний IP: $ext_ip"
  else
    warn "STEP patch: внешний IP не определён, ExternalHostname не трогаю."
  fi

  # Force inter-container hostnames
  sed -i \
    -e 's/^\s*DatabaseHost\s*=.*/DatabaseHost = db/' \
    -e 's/^\s*DatabasePort\s*=.*/DatabasePort = 3306/' \
    -e "s/^\s*DatabaseName\s*=.*/DatabaseName = ${DB_NAME}/" \
    -e "s/^\s*DatabaseUser\s*=.*/DatabaseUser = ${DB_USER}/" \
    -e "s/^\s*DatabasePassword\s*=.*/DatabasePassword = ${DB_PASS}/" \
    -e 's/^\s*LoginHost\s*=.*/LoginHost = authserver/' \
    -e "s/^\s*LoginPort\s*=.*/LoginPort = ${PORT_STATUS}/" \
    "$gs_props"

  if [[ -n "$ext_ip" ]]; then
    sed -i -e "s/^\s*ExternalHostname\s*=.*/ExternalHostname = ${ext_ip}/" "$gs_props"
  fi

  # Auth server props
  if [[ -f "$AUTH_PROPS" ]]; then
    sed -i \
      -e 's/^\s*LoginHost\s*=.*/LoginHost = authserver/' \
      -e 's/^\s*DatabaseHost\s*=.*/DatabaseHost = db/' \
      -e "s/^\s*DatabasePort\s*=.*/DatabasePort = 3306/" \
      -e "s/^\s*DatabaseName\s*=.*/DatabaseName = ${DB_NAME}/" \
      -e "s/^\s*DatabaseUser\s*=.*/DatabaseUser = ${DB_USER}/" \
      -e "s/^\s*DatabasePassword\s*=.*/DatabasePassword = ${DB_PASS}/" \
      -e 's#^\s*DatabaseURL\s*=.*#DatabaseURL = jdbc:mysql://db:3306/interlude?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8#' \
      -e 's/^\s*WhiteIpList\s*=.*/WhiteIpList = 127.0.0.1,0.0.0.0/' \
      "$AUTH_PROPS"
  else
    warn "STEP patch: не найден $AUTH_PROPS — пропускаю."
  fi

  [[ -f "$AUTH_DEF_RU" ]] && sed -i \
    -e 's/^\s*LoginHost\s*=.*/LoginHost = authserver/' \
    -e 's/^\s*DatabaseHost\s*=.*/DatabaseHost = db/' \
    "$AUTH_DEF_RU" || true

  [[ -f "$AUTH_DEF_EN" ]] && sed -i \
    -e 's/^\s*LoginHost\s*=.*/LoginHost = authserver/' \
    -e 's/^\s*DatabaseHost\s*=.*/DatabaseHost = db/' \
    "$AUTH_DEF_EN" || true

  # Heap in loop script
  local gs_loop="${BASE}/gameserver/GameServer_loop.sh"
  if [[ -f "$gs_loop" ]]; then
    if grep -qE -- '-Xmx' "$gs_loop"; then
      sed -i -E "s/-Xmx[0-9]+[mMgG]/-Xmx${GS_XMX}/g" "$gs_loop"
      log "STEP patch: GameServer_loop.sh -> -Xmx${GS_XMX}"
    else
      warn "STEP patch: в GameServer_loop.sh не найден -Xmx."
    fi
  else
    warn "STEP patch: не найден ${gs_loop}."
  fi
}

# -------------------------
# Patch docker-compose.yml
# -------------------------
ensure_compose(){
  ensure_base_dirs
  log "STEP compose_fix: патчу docker-compose.yml..."

  local yml="${BASE}/docker-compose.yml"
  [[ -f "$yml" ]] || die "docker-compose.yml не найден: $yml"

  # remove obsolete "version:" (compose v2 ignores, but warning annoying)
  if grep -qE '^\s*version\s*:' "$yml"; then
    sed -i -E '/^\s*version\s*:/d' "$yml" || true
  fi

  # ensure dump mount uses stable filename
  if grep -qE '/docker-entrypoint-initdb\.d/10-interlude\.sql' "$yml"; then
    sed -i -E \
      "s#(^\s*-\s*)(\./|/srv/l2server/)?interlude_backup[^:]*\.sql(:/docker-entrypoint-initdb\.d/10-interlude\.sql:ro)#\1./interlude_backup.sql\3#g" \
      "$yml" || true
    sed -i -E \
      "s#(^\s*-\s*)/srv/l2server/interlude_backup[^:]*\.sql(:/docker-entrypoint-initdb\.d/10-interlude\.sql:ro)#\1./interlude_backup.sql\2#g" \
      "$yml" || true
  fi

  # mem_limit upsert for authserver + gameserver
  perl -0777 -i -pe '
    sub upsert_mem {
      my ($svc, $val, $txt) = @_;
      if ($txt =~ m/(^\s*$svc:\s*\n(?:.*\n)*?)(^\s+mem_limit:\s*)(\S+)/m) {
        $txt =~ s/(^\s*$svc:\s*\n(?:.*\n)*?)(^\s+mem_limit:\s*)\S+/$1$2$val/m;
      } else {
        $txt =~ s/(^\s*$svc:\s*\n)/$1    mem_limit: $val\n/m;
      }
      return $txt;
    }
    $_ = upsert_mem("authserver", "'"$AUTH_MEM_LIMIT"'", $_);
    $_ = upsert_mem("gameserver", "'"$GS_MEM_LIMIT"'", $_);
  ' "$yml"

  # Ensure TZ env var exists in services? (optional; harmless)
  # (we don't strictly enforce)

  log "STEP compose_fix: готово (mem_limit auth=${AUTH_MEM_LIMIT}, game=${GS_MEM_LIMIT}, dump=./interlude_backup.sql)"
}

# -------------------------
# Compose up/down
# -------------------------
compose_fresh_start(){
  ensure_base_dirs
  log "STEP compose_up: Fresh: docker compose down -v"
  docker compose down -v || true
  docker compose up -d
}

compose_normal_start(){
  ensure_base_dirs
  log "STEP compose_up: docker compose up -d"
  docker compose up -d
}

# -------------------------
# Apply GRANTS (stored procedures / inventory)
# -------------------------
apply_grants(){
  ensure_base_dirs
  log "STEP grants: выдаю права ${DB_USER} для процедур/инвентаря..."

  # wait DB ready
  local ok=0
  for i in $(seq 1 90); do
    if docker exec -i l2-db mariadb -uroot -p"${DB_ROOT_PASS}" -e "SELECT 1" >/dev/null 2>&1; then
      ok=1
      break
    fi
    sleep 1
  done

  if [[ "$ok" -ne 1 ]]; then
    warn "STEP grants: DB не отвечает по root за 90 секунд — пропускаю."
    return 0
  fi

  # heredoc to stdin (no bash quoting issues)
  docker exec -i l2-db mariadb -uroot -p"${DB_ROOT_PASS}" <<SQL || true
CREATE DATABASE IF NOT EXISTS ${DB_NAME};

GRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE ON ${DB_NAME}.* TO '${DB_USER}'@'%';
GRANT SHOW DATABASES ON *.* TO '${DB_USER}'@'%';
GRANT SELECT ON mysql.proc TO '${DB_USER}'@'%';
FLUSH PRIVILEGES;
SQL
}

restart_services(){
  ensure_base_dirs
  log "STEP restart: перезапускаю authserver + gameserver..."
  docker compose restart authserver gameserver || true
}

# -------------------------
# Health checks / logs
# -------------------------
health_checks(){
  ensure_base_dirs
  log "STEP health: контейнеры"
  docker compose ps || true

  log "STEP health: порты (${PORT_LOGIN}/${PORT_GAME}/${PORT_STATUS}/${PORT_MYSQL})"
  ss -tulnp | grep -E "${PORT_LOGIN}|${PORT_GAME}|${PORT_STATUS}|${PORT_MYSQL}" || true

  log "STEP health: SHOW DATABASES (root)"
  docker exec -i l2-db mariadb -uroot -p"${DB_ROOT_PASS}" -e "SHOW DATABASES;" 2>/dev/null || true

  log "STEP health: последние строки логов"
  tail -n 5 "${BASE}/authserver/log/stdout.log" 2>/dev/null || true
  tail -n 5 "${BASE}/gameserver/log/stdout.log" 2>/dev/null || true

  log "STEP health: поиск ошибок metadata/procedure"
  (grep -RIn --color=never -E "noAccessToProcedureBodies|metadata required|Procedure|CallableStatement" \
    "${BASE}/authserver/log" "${BASE}/gameserver/log" 2>/dev/null | tail -n 50) || true
}

print_docker_nosudo_hint(){
  local u
  u="$(detect_primary_user)"
  if [[ "$u" != "root" ]]; then
    cat <<TXT

[i] Docker настройки для пользователя '$u':
────────────────────────────────────────────────────
✓ Добавлен в группу docker

Для применения изменений ВЫБЕРИТЕ ОДИН ИЗ ВАРИАНТОВ:

1. Выполните команду для текущей сессии:
   newgrp docker

2. Выйдите и войдите заново:
   logout
   # затем снова войдите в систему

3. Перезагрузите систему:
   sudo reboot

После этого проверьте:
   docker ps -a  # должно работать без sudo

Если всё ещё требует sudo:
   groups $USER  # проверьте, что docker в списке групп
   sudo usermod -aG docker $USER  # передобавьте при необходимости
   newgrp docker

TXT
  fi
}

# -------------------------
# Usage / flags
# -------------------------
usage(){
  cat <<USAGE
Использование:
  sudo ${BASE}/full_setup_ubuntu.sh [опции] [--skip <steps>]

Основные опции:
  --fresh                 (по умолчанию) docker compose down -v перед up (переимпорт БД)
  --no-fresh              не удалять volume БД (просто up -d)
  --dump /path.sql        путь к дампу interlude_backup_*.sql
  --rar  /path.rar        путь к rar архиву сервера
  --db-root-pass PASS     root пароль MariaDB (должен совпадать с docker-compose.yml env MYSQL_ROOT_PASSWORD)

Установка/сервисы:
  --skip-install          не ставить пакеты (docker/ufw/tools/nginx)
  --no-nginx              (если ставим пакеты) не ставить nginx
  --skip-tools            (если ставим пакеты) не ставить vim/unrar/unzip/net-tools
  --no-firewall           не настраивать ufw и не открывать порты
  --expose-mysql          открыть порт 3306 в ufw (ОПАСНО)

Гибкое исключение шагов (можно исключить любые):
  --skip install,firewall,unpack,post,dump,db_init,patch,compose_fix,compose_up,grants,restart,health

Примеры:
  1) Полный прогон с переимпортом БД:
     sudo ./full_setup_ubuntu.sh --fresh --db-root-pass rootpass

  2) Без установки пакетов и без firewall:
     sudo ./full_setup_ubuntu.sh --skip-install --no-firewall

  3) Пропустить распаковку (если файлы уже лежат в /srv/l2server):
     sudo ./full_setup_ubuntu.sh --skip unpack,post

  4) Прогнать только grants + restart:
     sudo ./full_setup_ubuntu.sh --skip install,firewall,unpack,post,dump,db_init,patch,compose_fix,compose_up,health

USAGE
}

# -------------------------
# Step runner
# -------------------------
SKIP_STEPS=""

skip_has(){
  local s=",$(echo "$SKIP_STEPS" | tr -d ' '),"
  [[ "$s" == *",$1,"* ]]
}

run_step(){
  local name="$1"; shift
  if skip_has "$name"; then
    log "SKIP $name"
    return 0
  fi
  "$@"
}

# -------------------------
# Main
# -------------------------
main(){
  need_root

  # defaults
  local do_fresh=1
  local do_install=1
  local want_nginx=1
  local want_tools=1
  local do_firewall=1

  local dump_path=""
  local rar_path=""

  # optional MySQL exposure (ufw)
  EXPOSE_MYSQL=0

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --fresh) do_fresh=1; shift ;;
      --no-fresh) do_fresh=0; shift ;;

      --dump) dump_path="${2:-}"; shift 2 ;;
      --rar)  rar_path="${2:-}"; shift 2 ;;
      --db-root-pass) DB_ROOT_PASS="${2:-}"; shift 2 ;;

      --skip-install) do_install=0; shift ;;
      --no-nginx) want_nginx=0; shift ;;
      --skip-tools) want_tools=0; shift ;;
      --no-firewall) do_firewall=0; shift ;;
      --expose-mysql) EXPOSE_MYSQL=1; shift ;;

      --skip)
        SKIP_STEPS="${2:-}"
        shift 2
        ;;

      -h|--help) usage; exit 0 ;;
      *) die "Неизвестный аргумент: $1 (см. --help)" ;;
    esac
  done

  [[ -d "$BASE" ]] || mkdir -p "$BASE"

  if [[ "$do_install" -eq 0 ]]; then
    SKIP_STEPS="$(echo "${SKIP_STEPS},install" | sed 's/^,//')"
  fi
  if [[ "$do_firewall" -eq 0 ]]; then
    SKIP_STEPS="$(echo "${SKIP_STEPS},firewall" | sed 's/^,//')"
  fi
  if [[ "$want_nginx" -eq 0 ]]; then
    # no separate step, handled inside install
    :
  fi
  if [[ "$want_tools" -eq 0 ]]; then
    # no separate step, handled inside install
    :
  fi

  # Steps
  run_step install      install_deps "$want_nginx" "$want_tools" "$do_firewall"
  run_step docker_ready ensure_docker_ready
  run_step firewall     configure_firewall
  run_step unpack       run_unpack "$rar_path"
  run_step post         run_post_unpack
  run_step dump         ensure_dump_file "$dump_path"
  run_step db_init      ensure_db_init
  run_step patch        patch_configs
  run_step compose_fix  ensure_compose

  if [[ "$do_fresh" -eq 1 ]]; then
    run_step compose_up compose_fresh_start
  else
    run_step compose_up compose_normal_start
  fi

  run_step grants       apply_grants
  run_step restart      restart_services
  run_step health       health_checks

  print_docker_nosudo_hint
  log "Готово."
}

main "$@"
