#!/usr/bin/env bash
# Скрипт первичной подготовки dev-ВМ для проекта Bridge and Join's
# (сервис M2M-перевода ЦБ через НРД).
#
# Поддерживаемые ОС:
#   - РЕД ОС 7.x      — целевая прод-ОС (вариант с бюджетом на лицензии)
#   - Ubuntu 22.04+   — бюджетная альтернатива для разработки и для
#                       компаний, у которых ещё нет лицензий на РЕД ОС
#                       (для прод-сертификации в финсекторе всё равно
#                       потребуется РЕД ОС / Astra SE с сертификатом ФСТЭК)
#
# Что делает:
#   - определяет ОС и пакетный менеджер;
#   - устанавливает базовый dev-стек (Git, Make, jq, xmlstarlet, ...);
#   - устанавливает Go 1.23, Liberica JDK 21, Node.js 20 LTS, Podman;
#   - устанавливает Claude Code CLI;
#   - создаёт пользователя dev и каталог /srv/dev;
#   - готовит безопасный shell-history с timestamp.
#
# Запуск (под root или через sudo):
#   sudo bash scripts/setup-dev-vm.sh
#
# Переменные окружения для тонкой настройки:
#   GO_VERSION       — версия Go (по умолчанию 1.23.4)
#   JDK_VERSION      — версия Liberica JDK (по умолчанию 21)
#   NODE_VERSION     — версия Node (по умолчанию 20)
#   DEV_USER         — имя пользователя для разработки (по умолчанию dev)
#   DEV_HOME         — домашняя директория dev (по умолчанию /home/dev)
#   WORKSPACE_ROOT   — корень рабочих репо (по умолчанию /srv/dev)
#   SKIP_USER        — 1, чтобы не создавать пользователя
#   SKIP_CLAUDE      — 1, чтобы не ставить Claude Code CLI
#
# Скрипт идемпотентен — повторный запуск не ломает то, что уже сделано.

set -euo pipefail

# --------------------------------------------------------------------- #
# Параметры
# --------------------------------------------------------------------- #
GO_VERSION="${GO_VERSION:-1.23.4}"
JDK_VERSION="${JDK_VERSION:-21}"
NODE_VERSION="${NODE_VERSION:-20}"
DEV_USER="${DEV_USER:-dev}"
DEV_HOME="${DEV_HOME:-/home/${DEV_USER}}"
WORKSPACE_ROOT="${WORKSPACE_ROOT:-/srv/dev}"
SKIP_USER="${SKIP_USER:-0}"
SKIP_CLAUDE="${SKIP_CLAUDE:-0}"

REPO_URL="https://git.zetit.ru/zuevav/Bridge-and-Join-s.git"

# --------------------------------------------------------------------- #
# Хелперы
# --------------------------------------------------------------------- #
log()  { printf '[%s] %s\n' "$(date +'%Y-%m-%d %H:%M:%S')" "$*"; }
die()  { printf '[ОШИБКА] %s\n' "$*" >&2; exit 1; }
need_root() { [[ "$EUID" -eq 0 ]] || die "Нужны права root. Запусти через sudo."; }

# --------------------------------------------------------------------- #
# 0. Предусловия — определение ОС и менеджера пакетов
# --------------------------------------------------------------------- #
need_root

if [[ ! -f /etc/os-release ]]; then
  die "Файл /etc/os-release не найден. Проверь, что ОС поддерживает FHS."
fi

# shellcheck disable=SC1091
. /etc/os-release

OS_FAMILY=""    # rpm | deb
OS_HUMAN=""

case "${ID:-}" in
  redos)
    OS_FAMILY="rpm"
    OS_HUMAN="РЕД ОС ${VERSION_ID:-?}"
    ;;
  rhel|centos|rocky|almalinux|fedora)
    OS_FAMILY="rpm"
    OS_HUMAN="${PRETTY_NAME:-RHEL-совместимая}"
    log "ВНИМАНИЕ: ОС ${OS_HUMAN} — для разработки подходит, для прод-сертификации в финсекторе нужна РЕД ОС / Astra SE."
    ;;
  ubuntu|debian)
    OS_FAMILY="deb"
    OS_HUMAN="${PRETTY_NAME:-${ID} ${VERSION_ID:-?}}"
    log "ОС ${OS_HUMAN} — бюджетный вариант для разработки. Для прода в финсекторе нужна РЕД ОС / Astra SE с сертификатом ФСТЭК."
    ;;
  astra)
    OS_FAMILY="deb"
    OS_HUMAN="Astra Linux ${VERSION_ID:-?}"
    ;;
  *)
    log "ВНИМАНИЕ: ОС определена как ${ID:-неизвестно} ${VERSION_ID:-?}."
    if   command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
      OS_FAMILY="rpm"
    elif command -v apt-get >/dev/null 2>&1; then
      OS_FAMILY="deb"
    else
      die "Не удалось определить пакетный менеджер. Поддерживаются dnf/yum (РЕД ОС, RHEL) и apt (Ubuntu, Debian, Astra)."
    fi
    log "Пробуем как ${OS_FAMILY}-совместимую."
    ;;
esac

log "ОС: ${OS_HUMAN}, семейство: ${OS_FAMILY}"

PKG_INSTALL=""
PKG_UPDATE=""
case "${OS_FAMILY}" in
  rpm)
    if   command -v dnf >/dev/null 2>&1; then PKG_INSTALL="dnf install -y";  PKG_UPDATE="dnf -y makecache"
    elif command -v yum >/dev/null 2>&1; then PKG_INSTALL="yum install -y";  PKG_UPDATE="yum -y makecache"
    else die "Не найден dnf/yum"
    fi
    ;;
  deb)
    export DEBIAN_FRONTEND=noninteractive
    PKG_INSTALL="apt-get install -y --no-install-recommends"
    PKG_UPDATE="apt-get update -y"
    ;;
esac

log "Обновление кэша пакетов"
${PKG_UPDATE}

# --------------------------------------------------------------------- #
# 1. Базовый системный стек
# --------------------------------------------------------------------- #
log "Шаг 1/8: установка базовых пакетов"
case "${OS_FAMILY}" in
  rpm)
    ${PKG_INSTALL} \
      git make curl wget tar gzip unzip ca-certificates \
      jq xmlstarlet libxml2 \
      gcc gcc-c++ binutils \
      python3 python3-pip \
      postgresql-libs \
      openssl-libs
    ;;
  deb)
    ${PKG_INSTALL} \
      git make curl wget tar gzip unzip ca-certificates \
      jq xmlstarlet libxml2-utils \
      build-essential \
      python3 python3-pip python3-venv \
      libpq5 \
      libssl3 \
      gnupg lsb-release software-properties-common
    ;;
esac

# --------------------------------------------------------------------- #
# 2. Podman + podman-compose
# --------------------------------------------------------------------- #
log "Шаг 2/8: установка Podman и podman-compose"
case "${OS_FAMILY}" in
  rpm)
    ${PKG_INSTALL} podman podman-docker || true
    ;;
  deb)
    ${PKG_INSTALL} podman || true
    # podman-docker может отсутствовать — не критично
    apt-get install -y --no-install-recommends podman-docker 2>/dev/null || true
    ;;
esac

if ! command -v podman-compose >/dev/null 2>&1; then
  pip3 install --quiet podman-compose || \
    log "ВНИМАНИЕ: не удалось установить podman-compose через pip3. Поставь вручную."
fi

# Включаем сокет podman, чтобы клиенты, ожидающие docker.sock, работали без правок
systemctl enable --now podman.socket 2>/dev/null || true

# --------------------------------------------------------------------- #
# 3. Go
# --------------------------------------------------------------------- #
log "Шаг 3/8: установка Go ${GO_VERSION}"
GO_TARBALL="go${GO_VERSION}.linux-amd64.tar.gz"
GO_URL="https://go.dev/dl/${GO_TARBALL}"

if [[ -d /usr/local/go && -x /usr/local/go/bin/go ]]; then
  CURRENT_GO_VER="$(/usr/local/go/bin/go version | awk '{print $3}' | sed 's/go//')"
  if [[ "${CURRENT_GO_VER}" == "${GO_VERSION}" ]]; then
    log "Go ${GO_VERSION} уже установлен"
  else
    log "Найден Go ${CURRENT_GO_VER}, переустанавливаем на ${GO_VERSION}"
    rm -rf /usr/local/go
  fi
fi

if [[ ! -d /usr/local/go ]]; then
  TMP_TAR="/tmp/${GO_TARBALL}"
  wget -q --show-progress -O "${TMP_TAR}" "${GO_URL}"
  tar -C /usr/local -xzf "${TMP_TAR}"
  rm -f "${TMP_TAR}"
fi

cat > /etc/profile.d/go.sh <<'EOF'
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
EOF
chmod 0644 /etc/profile.d/go.sh

# --------------------------------------------------------------------- #
# 4. Liberica JDK
# --------------------------------------------------------------------- #
log "Шаг 4/8: установка Liberica JDK ${JDK_VERSION}"
case "${OS_FAMILY}" in
  rpm)
    if [[ ! -f /etc/yum.repos.d/bellsoft.repo ]]; then
      cat > /etc/yum.repos.d/bellsoft.repo <<'EOF'
[bellsoft]
name=BellSoft Repository
baseurl=https://yum.bell-sw.com/
enabled=1
gpgcheck=1
gpgkey=https://download.bell-sw.com/pki/GPG-KEY-bellsoft
EOF
    fi
    ${PKG_INSTALL} "bellsoft-java${JDK_VERSION}" || \
      log "ВНИМАНИЕ: не удалось установить bellsoft-java${JDK_VERSION}. Альтернатива — скачать tar.gz с https://bell-sw.com/pages/downloads/."
    ;;
  deb)
    if [[ ! -f /etc/apt/sources.list.d/bellsoft.list ]]; then
      curl -fsSL https://download.bell-sw.com/pki/GPG-KEY-bellsoft \
        | gpg --dearmor -o /usr/share/keyrings/bellsoft.gpg
      echo "deb [signed-by=/usr/share/keyrings/bellsoft.gpg] https://apt.bell-sw.com/ stable main" \
        > /etc/apt/sources.list.d/bellsoft.list
      apt-get update -y
    fi
    ${PKG_INSTALL} "bellsoft-java${JDK_VERSION}-full" || \
    ${PKG_INSTALL} "bellsoft-java${JDK_VERSION}"      || \
      log "ВНИМАНИЕ: не удалось установить bellsoft-java${JDK_VERSION}. Альтернатива — скачать tar.gz с https://bell-sw.com/pages/downloads/."
    ;;
esac

# --------------------------------------------------------------------- #
# 5. Node.js LTS (для admin-ui и Claude Code CLI)
# --------------------------------------------------------------------- #
log "Шаг 5/8: установка Node.js ${NODE_VERSION} LTS"
need_install_node=1
if command -v node >/dev/null 2>&1; then
  CUR_NODE_MAJOR="$(node --version | sed 's/v//;s/\..*//')"
  if [[ "${CUR_NODE_MAJOR}" == "${NODE_VERSION}" ]]; then
    log "Node.js ${NODE_VERSION} уже установлен"
    need_install_node=0
  fi
fi

if [[ "${need_install_node}" == "1" ]]; then
  case "${OS_FAMILY}" in
    rpm)
      curl -fsSL "https://rpm.nodesource.com/setup_${NODE_VERSION}.x" | bash -
      ${PKG_INSTALL} nodejs
      ;;
    deb)
      curl -fsSL "https://deb.nodesource.com/setup_${NODE_VERSION}.x" | bash -
      ${PKG_INSTALL} nodejs
      ;;
  esac
fi

# --------------------------------------------------------------------- #
# 6. Claude Code CLI
# --------------------------------------------------------------------- #
if [[ "${SKIP_CLAUDE}" != "1" ]]; then
  log "Шаг 6/8: установка Claude Code CLI"
  npm install -g @anthropic-ai/claude-code
else
  log "Шаг 6/8: пропуск установки Claude Code CLI (SKIP_CLAUDE=1)"
fi

# --------------------------------------------------------------------- #
# 7. Пользователь dev и рабочая директория
# --------------------------------------------------------------------- #
if [[ "${SKIP_USER}" != "1" ]]; then
  log "Шаг 7/8: подготовка пользователя ${DEV_USER}"
  if ! id -u "${DEV_USER}" >/dev/null 2>&1; then
    useradd -m -s /bin/bash -d "${DEV_HOME}" "${DEV_USER}"
    log "Создан пользователь ${DEV_USER}"
  else
    log "Пользователь ${DEV_USER} уже существует"
  fi

  mkdir -p "${WORKSPACE_ROOT}"
  chown "${DEV_USER}:${DEV_USER}" "${WORKSPACE_ROOT}"

  HIST_SNIPPET="${DEV_HOME}/.bashrc.d-history"
  cat > "${HIST_SNIPPET}" <<'EOF'
# История с timestamp и общая для всех сессий — для аудита.
export HISTTIMEFORMAT="%F %T  "
export HISTSIZE=10000
export HISTFILESIZE=20000
shopt -s histappend
PROMPT_COMMAND="history -a; ${PROMPT_COMMAND:-}"
EOF
  chown "${DEV_USER}:${DEV_USER}" "${HIST_SNIPPET}"

  if ! grep -q "bashrc.d-history" "${DEV_HOME}/.bashrc" 2>/dev/null; then
    echo "[ -f ${HIST_SNIPPET} ] && . ${HIST_SNIPPET}" >> "${DEV_HOME}/.bashrc"
  fi

  if ! grep -q "/usr/local/go/bin" "${DEV_HOME}/.bashrc" 2>/dev/null; then
    cat >> "${DEV_HOME}/.bashrc" <<'EOF'
[ -f /etc/profile.d/go.sh ] && . /etc/profile.d/go.sh
EOF
  fi
else
  log "Шаг 7/8: пропуск подготовки пользователя (SKIP_USER=1)"
fi

# --------------------------------------------------------------------- #
# 8. Финальные проверки
# --------------------------------------------------------------------- #
log "Шаг 8/8: проверка установленных версий"
{
  printf 'git:           '; git --version
  printf 'make:          '; make --version | head -n1
  printf 'jq:            '; jq --version
  printf 'xmlstarlet:    '; xmlstarlet --version | head -n1
  printf 'podman:        '; podman --version 2>/dev/null || echo "(не установлен)"
  printf 'podman-compose:'; podman-compose --version 2>/dev/null || echo "(не установлен)"
  printf 'go:            '; /usr/local/go/bin/go version
  printf 'java:          '; java -version 2>&1 | head -n1
  printf 'node:          '; node --version 2>/dev/null || echo "(не установлен)"
  printf 'npm:           '; npm --version 2>/dev/null || echo "(не установлен)"
  if [[ "${SKIP_CLAUDE}" != "1" ]]; then
    printf 'claude:        '; claude --version 2>/dev/null || echo "(не установлен или требует первого запуска)"
  fi
} || true

cat <<EOF

──────────────────────────────────────────────────────────────────────
Подготовка dev-ВМ завершена.
ОС: ${OS_HUMAN}

Дальше — под пользователем ${DEV_USER}:

    su - ${DEV_USER}
    cd ${WORKSPACE_ROOT}

    # Положи свой публичный SSH-ключ в ~/.ssh/id_ed25519.pub
    # и добавь его в git.zetit.ru → Settings → SSH Keys

    git clone ${REPO_URL}
    cd Bridge-and-Join-s

    make ci          # локальный прогон линтера, тестов и сборки
    make compose-up  # PostgreSQL + MinIO для разработки

    # Запуск Claude Code из корня репо:
    claude

Дополнительные шаги, которые НЕ автоматизированы (делаются вручную при
получении соответствующих артефактов от заказчика):

    1. Установка КриптоПро CSP и КриптоПро JCP под целевую ОС.
    2. Установка дистрибутива Интеграционного шлюза НРД.
    3. Импорт тестовых сертификатов GUEST/TEST3 (ГОСТ + RSA).
    4. Конфигурирование исходящего прокси/whitelist для git.zetit.ru,
       gost-*.nsd.ru, rsa-*.nsd.ru, registry.npmjs.org, proxy.golang.org,
       api.anthropic.com.

ВАЖНО про прод:
    Эта ВМ годится для разработки и тестирования. Для прод-стенда в
    финсекторе обязательны РЕД ОС / Astra SE с сертификатом ФСТЭК и
    лицензированный КриптоПро класса КС1 — Ubuntu/Debian для прода
    финсектора не подходят.
──────────────────────────────────────────────────────────────────────
EOF
