#!/usr/bin/env bash
#
# install-loganalyzer-agent.sh
# ------------------------------------------------------------------------
# Установочный (и до-настроечный) скрипт агента сбора логов на базе
# Vector (vectordotdev), ныне поддерживаемого Datadog:
# https://github.com/vectordotdev/vector
#
# Скрипт можно запускать ПОВТОРНО — он идемпотентен:
#   - если Vector уже установлен нужной версии, повторное скачивание
#     пропускается;
#   - если конфигурация уже существует, скрипт читает из неё список уже
#     подключённых лог-файлов и не предлагает их заново;
#   - при повторном поиске новые (ещё не подключённые) лог-файлы
#     показываются отдельно, из них можно выбрать файл для добавления;
#   - выбранный файл добавляется в СУЩЕСТВУЮЩУЮ конфигурацию (все
#     остальные источники логов остаются как были), после чего сервис
#     перезапускается.
#   - если новых файлов не найдено (и вы не указали путь вручную),
#     скрипт просто проверяет/перезапускает сервис и завершает работу —
#     это позволяет безопасно запускать его повторно, например по cron.
#
# Что делает скрипт (по шагам):
#    1. Определяет ОС, архитектуру и тип libc
#    2. Определяет пользователя (root / обычный) и повышает права при необходимости
#    3. Проверяет права, необходимые для создания systemd-сервиса
#    4. Создаёт (при необходимости) папки установки/конфигурации/данных
#    5. Проверяет наличие существующей конфигурации Vector и читает из неё
#       список уже подключённых лог-файлов (и сохранённый API-ключ)
#    6. Ищет access.log от Nginx / Apache / прочих reverse-proxy,
#       исключая уже подключённые файлы из шага 5
#    7. Даёт выбрать файл для добавления из новых найденных
#       (или ввести путь вручную, или пропустить, если нечего добавлять)
#    8. Запрашивает API-ключ (по умолчанию — уже сохранённый, если есть)
#    9. Скачивает нужную сборку Vector v0.57.0 с GitHub и проверяет
#       checksum — но только если версия ещё не установлена
#   10. Добавляет выбранный файл логов в конфигурацию (или создаёт
#       конфигурацию с нуля при самом первом запуске)
#   11. Создаёт/обновляет unit-файл systemd-сервиса
#   12. Перезапускает сервис и выводит его статус
#
# Запуск:
#   sudo ./install-loganalyzer-agent.sh
#   -- или --
#   curl -fsSL https://.../install-loganalyzer-agent.sh | sudo bash
#
# Необязательные переменные окружения для неинтерактивного запуска:
#   LOG_FILE_PATH   - путь к access.log, чтобы пропустить шаг выбора.
#                     Если файл уже подключён в конфигурации, скрипт
#                     просто перезапустит сервис без дублирования записи.
#   API_KEY         - API-ключ, чтобы пропустить интерактивный ввод
#   INGEST_URL      - URL приёма логов (см. ЗАМЕНИТЕ ниже)
#   VECTOR_CONFIG_TEMPLATE_URL - URL заготовки конфигурации, если она
#                                хранится у вас на сервере/CDN, а не берётся
#                                из встроенного шаблона ниже (используется
#                                только при самом первом запуске, когда
#                                конфигурации ещё не существует)
#
# ------------------------------------------------------------------------

set -Eeuo pipefail
IFS=$'\n\t'

# ============================== НАСТРОЙКИ ================================

VECTOR_VERSION="0.57.0"
GITHUB_RELEASE_BASE="https://github.com/vectordotdev/vector/releases/download/v${VECTOR_VERSION}"

INSTALL_DIR="/usr/local/bin"
VECTOR_BIN="${INSTALL_DIR}/loganalyzer"
CONFIG_DIR="/etc/loganalyzer"
CONFIG_FILE="${CONFIG_DIR}/config.yaml"
DATA_DIR="/var/lib/loganalyzer"
SYSTEM_USER="loganalyzer"
SYSTEM_GROUP="loganalyzer"

SERVICE_NAME="loganalyzer-agent"
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"

# ЗАМЕНИТЕ на реальный адрес приёма логов вашего сервиса.
# Можно также передать снаружи: INGEST_URL="https://..." ./install-loganalyzer-agent.sh
INGEST_URL="${INGEST_URL:-https://api.loganalyzer.ru/functions/v1/ingest-logs}"

# Необязательный URL заготовки конфигурации (если храните её у себя, а не
# используете встроенный шаблон из этого скрипта). Используется только
# при первичном создании конфигурации.
CONFIG_TEMPLATE_URL="${VECTOR_CONFIG_TEMPLATE_URL:-}"

TMP_DIR="$(mktemp -d /tmp/vector-install.XXXXXX)"
trap 'rm -rf "${TMP_DIR}"' EXIT

# ============================== ВЫВОД/ЛОГИ ================================

if [ -t 1 ]; then
  C_RESET=$'\033[0m'; C_INFO=$'\033[1;36m'; C_OK=$'\033[1;32m'
  C_WARN=$'\033[1;33m'; C_ERR=$'\033[1;31m'; C_STEP=$'\033[1;35m'
else
  C_RESET=""; C_INFO=""; C_OK=""; C_WARN=""; C_ERR=""; C_STEP=""
fi

step()  { echo -e "\n${C_STEP}==== $* ====${C_RESET}"; }
info()  { echo -e "${C_INFO}[i]${C_RESET} $*"; }
ok()    { echo -e "${C_OK}[ok]${C_RESET} $*"; }
warn()  { echo -e "${C_WARN}[!]${C_RESET} $*"; }
err()   { echo -e "${C_ERR}[ошибка]${C_RESET} $*" >&2; }

trap 'err "Выполнение прервано на строке ${LINENO}."' ERR

# ============================ ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ======================

# Достаёт список путей из блока `include:` источника типа file в
# конфигурации Vector, сгенерированной этим же скриптом. Рассчитан именно
# на наш собственный формат (список с отступом и элементы вида
# `      - "путь"`), поэтому не требует внешних утилит вроде yq.
parse_included_log_paths() {
  local file="$1"
  [ -f "${file}" ] || return 0
  awk '
    /^[[:space:]]*include:/ { flag=1; next }
    flag && /^[[:space:]]*-[[:space:]]*"/ { print; next }
    flag { flag = 0 }
  ' "${file}" | sed -E 's/^[[:space:]]*-[[:space:]]*"(.*)"[[:space:]]*$/\1/'
}

# Проверяет, есть ли указанный путь уже среди EXISTING_LOG_PATHS.
is_already_configured() {
  local candidate="$1"
  local existing
  for existing in "${EXISTING_LOG_PATHS[@]:-}"; do
    [ -n "${existing}" ] && [ "${existing}" = "${candidate}" ] && return 0
  done
  return 1
}

# ============================ ШАГ 2: ПОЛЬЗОВАТЕЛЬ =========================

step "[1/12] Определение пользователя"

CURRENT_USER="$(id -un)"

if [ "${EUID}" -ne 0 ]; then
  warn "Скрипт запущен от имени '${CURRENT_USER}', требуются права root (создаётся systemd-сервис)."
  if command -v sudo >/dev/null 2>&1; then
    info "Повторный запуск через sudo..."
    exec sudo --preserve-env=INGEST_URL,VECTOR_CONFIG_TEMPLATE_URL,LOG_FILE_PATH,API_KEY \
      "$0" "$@"
  else
    err "Команда sudo не найдена. Запустите скрипт от имени root: su -c './$(basename "$0")'"
    exit 1
  fi
else
  ok "Скрипт выполняется от имени root."
fi

# ============================== ШАГ 1: ОС ================================

step "[2/12] Определение операционной системы"

OS_ID="unknown"
OS_NAME="unknown"
OS_VERSION="unknown"

if [ -r /etc/os-release ]; then
  # shellcheck disable=SC1091
  . /etc/os-release
  OS_ID="${ID:-unknown}"
  OS_NAME="${PRETTY_NAME:-${NAME:-unknown}}"
  OS_VERSION="${VERSION_ID:-unknown}"
else
  warn "Не удалось найти /etc/os-release, продолжаем на общих правах."
fi

KERNEL="$(uname -s)"
if [ "${KERNEL}" != "Linux" ]; then
  err "Скрипт поддерживает только Linux (обнаружено: ${KERNEL})."
  exit 1
fi

RAW_ARCH="$(uname -m)"
case "${RAW_ARCH}" in
  x86_64|amd64)        ARCH="x86_64"  ;;
  aarch64|arm64)       ARCH="aarch64" ;;
  armv7l|armv7)        ARCH="armv7"   ;;
  *)
    err "Неподдерживаемая архитектура процессора: ${RAW_ARCH}"
    exit 1
    ;;
esac

if [ -f /etc/alpine-release ]; then
  LIBC="musl"
elif ldd --version 2>&1 | head -n1 | grep -qi musl; then
  LIBC="musl"
else
  LIBC="gnu"
fi

info "ОС:            ${OS_NAME} (${OS_ID} ${OS_VERSION})"
info "Архитектура:   ${RAW_ARCH} -> ${ARCH}"
info "libc:          ${LIBC}"
ok "ОС определена."

# ===================== ШАГ 3: ПРОВЕРКА ПРАВ / SYSTEMD =====================

step "[3/12] Проверка прав для создания сервиса"

if ! command -v systemctl >/dev/null 2>&1 || [ ! -d /run/systemd/system ]; then
  err "В системе не обнаружен systemd — создание сервиса из этого скрипта не поддерживается."
  exit 1
fi
ok "systemd обнаружен."

for path in /etc/systemd/system /usr/local/bin /etc /var/lib; do
  if [ ! -w "${path}" ]; then
    err "Нет прав на запись в ${path}. Требуется root."
    exit 1
  fi
done
ok "Права на запись в системные каталоги подтверждены."

for cmd in curl tar sha256sum systemctl useradd getent stat awk sed; do
  if ! command -v "${cmd}" >/dev/null 2>&1; then
    err "Не найдена необходимая утилита: ${cmd}. Установите её и повторите запуск."
    exit 1
  fi
done
ok "Все необходимые утилиты (curl, tar, sha256sum, systemctl, useradd...) найдены."

# ===================== ШАГ 4: ПОДГОТОВКА КАТАЛОГОВ =========================

step "[4/12] Проверка и создание необходимых каталогов"

for d in "${INSTALL_DIR}" "${CONFIG_DIR}" "${DATA_DIR}"; do
  if [ -d "${d}" ]; then
    info "Каталог уже существует: ${d}"
  else
    mkdir -p "${d}"
    ok "Создан каталог: ${d}"
  fi
done

if ! getent group "${SYSTEM_GROUP}" >/dev/null 2>&1; then
  groupadd --system "${SYSTEM_GROUP}"
  ok "Создана системная группа: ${SYSTEM_GROUP}"
else
  info "Системная группа ${SYSTEM_GROUP} уже существует."
fi

if ! getent passwd "${SYSTEM_USER}" >/dev/null 2>&1; then
  useradd --system --no-create-home --shell /usr/sbin/nologin \
    --gid "${SYSTEM_GROUP}" --home-dir "${DATA_DIR}" "${SYSTEM_USER}"
  ok "Создан системный пользователь: ${SYSTEM_USER}"
else
  info "Системный пользователь ${SYSTEM_USER} уже существует."
fi

chown -R "${SYSTEM_USER}:${SYSTEM_GROUP}" "${DATA_DIR}"
chmod 750 "${DATA_DIR}"
ok "Каталоги готовы (${INSTALL_DIR}, ${CONFIG_DIR}, ${DATA_DIR})."

# ================ ШАГ 5: ПРОВЕРКА СУЩЕСТВУЮЩЕЙ КОНФИГУРАЦИИ ================

step "[5/12] Проверка существующей конфигурации Vector"

declare -a EXISTING_LOG_PATHS=()
EXISTING_API_KEY=""
CONFIG_EXISTS=false

if [ -f "${CONFIG_FILE}" ]; then
  CONFIG_EXISTS=true
  ok "Найдена существующая конфигурация: ${CONFIG_FILE}"

  mapfile -t EXISTING_LOG_PATHS < <(parse_included_log_paths "${CONFIG_FILE}")

  if [ "${#EXISTING_LOG_PATHS[@]}" -gt 0 ]; then
    info "Уже отправляются лог-файлы (${#EXISTING_LOG_PATHS[@]}):"
    for p in "${EXISTING_LOG_PATHS[@]}"; do
      info "  - ${p}"
    done
  else
    info "В конфигурации не найдено ни одного источника логов."
  fi

  EXISTING_API_KEY="$(grep -m1 -oE 'Bearer [^"[:space:]]+' "${CONFIG_FILE}" 2>/dev/null | awk '{print $2}')" || true
  if [ -n "${EXISTING_API_KEY}" ]; then
    info "В конфигурации уже сохранён API-ключ (${#EXISTING_API_KEY} символов)."
  fi
else
  info "Конфигурация ${CONFIG_FILE} не найдена — будет создана новая при первичной настройке."
fi

# ================ ШАГ 6: ПОИСК НОВЫХ ЛОГ-ФАЙЛОВ ============================

step "[6/12] Поиск новых лог-файлов Nginx / Apache / других reverse-proxy"

SEARCH_DIRS=(/var /opt /srv /usr/local/var/log /home)
declare -a FOUND_LOGS=()

for dir in "${SEARCH_DIRS[@]}"; do
  [ -d "${dir}" ] || continue
  while IFS= read -r -d '' f; do
    FOUND_LOGS+=("${f}")
  done < <(find "${dir}" -xdev -maxdepth 6 -type f \
              \( -iname '*access*.log' -o -iname '*access_log*' \) \
              ! -iname '*.gz' ! -iname '*.bz2' ! -iname '*.zip' \
              -print0 2>/dev/null)
done

# Дедупликация с сортировкой по времени изменения (сначала самые свежие)
declare -a UNIQUE_LOGS=()
if [ "${#FOUND_LOGS[@]}" -gt 0 ]; then
  while IFS= read -r line; do
    UNIQUE_LOGS+=("${line#* }")
  done < <(
    for f in "${FOUND_LOGS[@]}"; do
      mtime=$(stat -c '%Y' "${f}" 2>/dev/null || echo 0)
      echo "${mtime} ${f}"
    done | sort -rn -k1,1 | awk '!seen[$2]++'
  )
fi

# Исключаем то, что уже подключено в конфигурации (шаг 5).
declare -a NEW_LOGS=()
for f in "${UNIQUE_LOGS[@]:-}"; do
  [ -n "${f}" ] || continue
  if ! is_already_configured "${f}"; then
    NEW_LOGS+=("${f}")
  fi
done

if [ "${#UNIQUE_LOGS[@]}" -eq 0 ]; then
  warn "Автоматический поиск не нашёл access.log в стандартных местах."
elif [ "${#NEW_LOGS[@]}" -eq 0 ]; then
  info "Найдено файлов: ${#UNIQUE_LOGS[@]}, но все они уже подключены в конфигурации."
else
  ok "Найдено новых файлов (ещё не подключённых): ${#NEW_LOGS[@]}"
  for f in "${NEW_LOGS[@]}"; do
    size_h=$(du -h "${f}" 2>/dev/null | cut -f1)
    mtime_h=$(stat -c '%y' "${f}" 2>/dev/null | cut -d'.' -f1)
    info "  - ${f}  (размер: ${size_h:-?}, изменён: ${mtime_h:-?})"
  done
fi

# ================ ШАГ 7: ВЫБОР ФАЙЛА ЛОГОВ ДЛЯ ДОБАВЛЕНИЯ ===================

step "[7/12] Выбор файла логов для добавления"

LOG_FILE_PATH="${LOG_FILE_PATH:-}"
ADD_NEW_LOG=true

if [ -n "${LOG_FILE_PATH}" ]; then
  ok "Путь к логу задан переменной окружения: ${LOG_FILE_PATH}"
  if [ ! -f "${LOG_FILE_PATH}" ]; then
    err "Файл '${LOG_FILE_PATH}' не существует."
    exit 1
  fi
  if is_already_configured "${LOG_FILE_PATH}"; then
    warn "Файл '${LOG_FILE_PATH}' уже подключён в конфигурации, повторное добавление не требуется."
    ADD_NEW_LOG=false
  fi
elif [ ! -e /dev/tty ]; then
  if [ "${#NEW_LOGS[@]}" -eq 0 ]; then
    info "Нет интерактивного терминала и новых файлов не найдено — пропускаем добавление и переходим к перезапуску сервиса."
    ADD_NEW_LOG=false
  else
    err "Нет интерактивного терминала и переменная LOG_FILE_PATH не задана."
    err "Задайте LOG_FILE_PATH=/path/to/access.log перед запуском скрипта."
    exit 1
  fi
else
  if [ "${#NEW_LOGS[@]}" -gt 0 ]; then
    echo "Выберите файл логов для добавления (номер), 0 — указать путь вручную, s — пропустить:" > /dev/tty
    i=1
    for f in "${NEW_LOGS[@]}"; do
      echo "  ${i}) ${f}" > /dev/tty
      i=$((i + 1))
    done
    echo "  0) Указать путь вручную" > /dev/tty
    echo "  s) Пропустить (не добавлять новый файл)" > /dev/tty

    while true; do
      read -r -p "Ваш выбор: " choice < /dev/tty
      case "${choice}" in
        s|S)
          LOG_FILE_PATH=""
          ADD_NEW_LOG=false
          break
          ;;
        *)
          if [[ "${choice}" =~ ^[0-9]+$ ]] && [ "${choice}" -ge 0 ] && [ "${choice}" -le "${#NEW_LOGS[@]}" ]; then
            if [ "${choice}" -eq 0 ]; then
              LOG_FILE_PATH=""
            else
              LOG_FILE_PATH="${NEW_LOGS[$((choice - 1))]}"
            fi
            break
          fi
          warn "Некорректный ввод, повторите."
          ;;
      esac
    done
  else
    read -r -p "Новых файлов не найдено. Указать путь вручную? (y/N): " manual_choice < /dev/tty
    if [[ "${manual_choice}" =~ ^[YyДд]$ ]]; then
      LOG_FILE_PATH=""
    else
      ADD_NEW_LOG=false
      info "Пропускаем добавление нового файла."
    fi
  fi

  if [ "${ADD_NEW_LOG}" = true ] && [ -z "${LOG_FILE_PATH}" ]; then
    while [ -z "${LOG_FILE_PATH}" ]; do
      read -r -p "Введите полный путь к access.log: " LOG_FILE_PATH < /dev/tty
      if [ ! -f "${LOG_FILE_PATH}" ]; then
        warn "Файл '${LOG_FILE_PATH}' не найден, попробуйте ещё раз."
        LOG_FILE_PATH=""
      elif is_already_configured "${LOG_FILE_PATH}"; then
        warn "Файл '${LOG_FILE_PATH}' уже подключён в конфигурации."
        LOG_FILE_PATH=""
      fi
    done
  fi
fi

if [ "${ADD_NEW_LOG}" = true ]; then
  ok "Выбран файл логов для добавления: ${LOG_FILE_PATH}"
else
  info "Новый файл логов в этом запуске не добавляется."
fi

LOG_FILE_NAME=$(basename "$LOG_FILE_PATH")
CONFIG_SOURCE_NAME=$(echo "$LOG_FILE_NAME" | sed -e 's/access//g' -e 's/[-.]/_/g' -e 's/__*/_/g' -e 's/$/_source/')
CONFIG_SINK_NAME=$(echo "$LOG_FILE_NAME" | sed -e 's/access//g' -e 's/[-.]/_/g' -e 's/__*/_/g' -e 's/$/_sink/')

# ==================== ШАГ 8: API-КЛЮЧ =======================================

step "[8/12] Ввод API-ключа"

API_KEY="${API_KEY:-}"

if [ -n "${API_KEY}" ]; then
  ok "API-ключ получен из переменной окружения."
elif [ ! -e /dev/tty ]; then
  if [ -n "${EXISTING_API_KEY}" ]; then
    API_KEY="${EXISTING_API_KEY}"
    info "Нет интерактивного терминала — используется ранее сохранённый API-ключ."
  else
    err "Нет интерактивного терминала и переменная API_KEY не задана."
    exit 1
  fi
else
  prompt_suffix=""
  [ -n "${EXISTING_API_KEY}" ] && prompt_suffix=" (Enter — оставить прежний)"
  while true; do
    read -r -s -p "Введите API-ключ${prompt_suffix}: " API_KEY_INPUT < /dev/tty
    echo "" > /dev/tty
    if [ -z "${API_KEY_INPUT}" ]; then
      if [ -n "${EXISTING_API_KEY}" ]; then
        API_KEY="${EXISTING_API_KEY}"
        ok "Оставлен прежний API-ключ."
        break
      else
        warn "API-ключ не может быть пустым."
        continue
      fi
    elif [ "${#API_KEY_INPUT}" -lt 8 ]; then
      warn "API-ключ выглядит слишком коротким (меньше 8 символов), проверьте ввод."
      continue
    else
      API_KEY="${API_KEY_INPUT}"
      break
    fi
  done
fi
ok "API-ключ принят (${#API_KEY} символов)."

# ================ ШАГ 9: УСТАНОВКА/ПРОВЕРКА VECTOR ==========================

step "[9/12] Установка/проверка Vector v${VECTOR_VERSION}"

INSTALLED_VERSION=""
if [ -x "${VECTOR_BIN}" ]; then
  INSTALLED_VERSION="$("${VECTOR_BIN}" --version 2>/dev/null | awk '{print $2}')" || true
fi

if [ "${INSTALLED_VERSION}" = "${VECTOR_VERSION}" ]; then
  ok "Vector v${VECTOR_VERSION} уже установлен (${VECTOR_BIN}), повторное скачивание не требуется."
else
  if [ -n "${INSTALLED_VERSION}" ]; then
    info "Обнаружена версия Vector ${INSTALLED_VERSION}, обновляем до v${VECTOR_VERSION}."
  fi

  ASSET_NAME="vector-${VECTOR_VERSION}-${ARCH}-unknown-linux-${LIBC}"
  case "${ARCH}" in
    armv7)
      ASSET_NAME="vector-${VECTOR_VERSION}-armv7-unknown-linux-${LIBC}eabihf"
      ;;
  esac
  ARCHIVE_NAME="${ASSET_NAME}.tar.gz"
  CHECKSUMS_NAME="vector-${VECTOR_VERSION}-SHA256SUMS"

  info "Файл сборки: ${ARCHIVE_NAME}"

  curl -fsSL --retry 3 --retry-delay 2 \
    -o "${TMP_DIR}/${ARCHIVE_NAME}" \
    "${GITHUB_RELEASE_BASE}/${ARCHIVE_NAME}"

  curl -fsSL --retry 3 --retry-delay 2 \
    -o "${TMP_DIR}/${CHECKSUMS_NAME}" \
    "${GITHUB_RELEASE_BASE}/${CHECKSUMS_NAME}"

  ok "Скачивание завершено, проверяем целостность (SHA256)..."

  ( cd "${TMP_DIR}" && grep "  ${ARCHIVE_NAME}\$" "${CHECKSUMS_NAME}" | sha256sum -c - )

  ok "Контрольная сумма подтверждена."

  info "Распаковка архива..."
  mkdir -p "${TMP_DIR}/extract"
  tar -xzf "${TMP_DIR}/${ARCHIVE_NAME}" -C "${TMP_DIR}/extract"

  VECTOR_EXTRACTED_BIN="$(find "${TMP_DIR}/extract" -type f -name vector -perm -u+x | head -n1)"
  if [ -z "${VECTOR_EXTRACTED_BIN}" ]; then
    err "Не удалось найти исполняемый файл vector внутри архива."
    exit 1
  fi

  install -o root -g root -m 0755 "${VECTOR_EXTRACTED_BIN}" "${VECTOR_BIN}"
  ok "Vector установлен в ${VECTOR_BIN}"
fi
"${VECTOR_BIN}" --version || true

# ================ ШАГ 10: ОБНОВЛЕНИЕ КОНФИГУРАЦИИ VECTOR =====================

step "[10/12] Обновление конфигурации Vector"

if [ "${CONFIG_EXISTS}" = false ]; then
  # -------- Первичная настройка: конфигурации ещё нет, создаём с нуля -------
  if [ "${ADD_NEW_LOG}" != true ] || [ -z "${LOG_FILE_PATH}" ]; then
    err "Конфигурация отсутствует, и не выбран ни один файл логов для первичной настройки."
    err "Запустите скрипт снова и выберите (или укажите через LOG_FILE_PATH) файл логов."
    exit 1
  fi

  info "Конфигурация отсутствует — создаём новую."

  TEMPLATE_FILE="${TMP_DIR}/vector.yaml.tmpl"

  if [ -n "${CONFIG_TEMPLATE_URL}" ]; then
    info "Скачивание заготовки конфигурации: ${CONFIG_TEMPLATE_URL}"
    if ! curl -fsSL --retry 3 --retry-delay 2 -o "${TEMPLATE_FILE}" "${CONFIG_TEMPLATE_URL}"; then
      warn "Не удалось скачать внешнюю заготовку, используем встроенный шаблон."
      CONFIG_TEMPLATE_URL=""
    fi
  fi

  if [ -z "${CONFIG_TEMPLATE_URL}" ]; then
    info "Используется встроенный шаблон конфигурации."
    cat > "${TEMPLATE_FILE}" <<'TEMPLATE_EOF'
# Конфигурация Vector, сгенерирована install-loganalyzer-agent.sh
# Дата генерации: __GENERATED_AT__
# Хост: __HOSTNAME__
#
# Список источников в sources можно расширять
# при повторных запусках install-loganalyzer-agent.sh — новые файлы логов
# добавляются в эту же секцию, остальные настройки не меняются.

data_dir: __DATA_DIR__

sources:
  __SOURCE_NAME__:
    type: file
    include:
      - "__LOG_PATH__"
    read_from: "end"
    fingerprint:
      strategy: "checksum"

sinks:
  __SINK_NAME__:
    type: http
    inputs:
      - __SOURCE_NAME__
    uri: "__INGEST_URL__"
    method: post
    encoding:
      codec: text
    request:
      headers:
        X-Api-Key: "__API_KEY__"
        Content-Type: "text/plain"
    batch:
      max_bytes: 1048576
      timeout_secs: 5
TEMPLATE_EOF
  fi

  GENERATED_AT="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
  HOSTNAME_VAL="$(hostname -f 2>/dev/null || hostname)"

  RENDERED="$(cat "${TEMPLATE_FILE}")"
  RENDERED="${RENDERED//__DATA_DIR__/${DATA_DIR}}"
  RENDERED="${RENDERED//__LOG_PATH__/${LOG_FILE_PATH}}"
  RENDERED="${RENDERED//__SOURCE_NAME__/${CONFIG_SOURCE_NAME}}"
  RENDERED="${RENDERED//__SINK_NAME__/${CONFIG_SINK_NAME}}"
  RENDERED="${RENDERED//__API_KEY__/${API_KEY}}"
  RENDERED="${RENDERED//__HOSTNAME__/${HOSTNAME_VAL}}"
  RENDERED="${RENDERED//__INGEST_URL__/${INGEST_URL}}"
  RENDERED="${RENDERED//__GENERATED_AT__/${GENERATED_AT}}"

  printf '%s\n' "${RENDERED}" > "${CONFIG_FILE}"
  ok "Конфигурация создана: ${CONFIG_FILE}"
else
  # -------- Повторный запуск: аккуратно дополняем существующий файл --------
  info "Обновляем существующую конфигурацию без полной перезаписи."

  if [ "${ADD_NEW_LOG}" = true ] && [ -n "${LOG_FILE_PATH}" ]; then
    # Используем awk для вставки source перед секцией sinks
    awk -v source_name="$CONFIG_SOURCE_NAME" -v log_path="$LOG_FILE_PATH" '
    BEGIN {
      in_sources = 0
      source_inserted = 0
    }

    # Определяем ключи верхнего уровня (без отступа)
    /^[a-z_]+:/ {
      if ($0 ~ /^sources:/) {
        in_sources = 1
      } else if ($0 ~ /^sinks:/) {
        # Перед переходом в sinks вставляем source, если ещё не вставили
        if (!source_inserted) {
          print "  " source_name ":"
          print "    type: file"
          print "    include:"
          print "      - \"" log_path "\""
          print "    read_from: \"end\""
          print "    fingerprint:"
          print "      strategy: \"checksum\""
          print ""
          source_inserted = 1
        }
        in_sources = 0
      } else {
        in_sources = 0
      }
    }

    {
      print $0
    }
    ' "${CONFIG_FILE}" > "${CONFIG_FILE}.tmp"

    # Добавляем sink в конец файла
    cat >> "${CONFIG_FILE}.tmp" <<'CONFIG_FILE_EOF'

  __SINK_NAME__:
    type: http
    inputs:
      - __SOURCE_NAME__
    uri: "__INGEST_URL__"
    method: post
    encoding:
      codec: text
    request:
      headers:
        X-Api-Key: "__API_KEY__"
        Content-Type: "text/plain"
    batch:
      max_bytes: 1048576
      timeout_secs: 5
CONFIG_FILE_EOF

    RENDERED="$(cat ${CONFIG_FILE}.tmp)"
    RENDERED="${RENDERED//__SOURCE_NAME__/${CONFIG_SOURCE_NAME}}"
    RENDERED="${RENDERED//__SINK_NAME__/${CONFIG_SINK_NAME}}"
    RENDERED="${RENDERED//__API_KEY__/${API_KEY}}"
    RENDERED="${RENDERED//__INGEST_URL__/${INGEST_URL}}"
    printf '%s\n' "${RENDERED}" > "${CONFIG_FILE}.tmp"

    cp "${CONFIG_FILE}" "${CONFIG_FILE}.bak"
    mv "${CONFIG_FILE}.tmp" "${CONFIG_FILE}"
    ok "Путь ${LOG_FILE_PATH} добавлен в конфигурацию."
  else
    info "Новый файл логов не добавлялся, список источников не изменился."
  fi
fi

chown "${SYSTEM_USER}:${SYSTEM_GROUP}" "${CONFIG_FILE}"
chmod 640 "${CONFIG_FILE}"

info "Проверка конфигурации..."
"${VECTOR_BIN}" validate --no-environment "${CONFIG_FILE}"
ok "Конфигурация прошла проверку."

# Доступ к новому лог-файлу для системного пользователя loganalyzer.
if [ "${ADD_NEW_LOG}" = true ] && [ -n "${LOG_FILE_PATH}" ]; then
  LOG_FILE_GROUP="$(stat -c '%G' "${LOG_FILE_PATH}" 2>/dev/null || echo "")"
  if [ -n "${LOG_FILE_GROUP}" ] && [ "${LOG_FILE_GROUP}" != "${SYSTEM_GROUP}" ] && getent group "${LOG_FILE_GROUP}" >/dev/null 2>&1; then
    usermod -aG "${LOG_FILE_GROUP}" "${SYSTEM_USER}"
    ok "Пользователь ${SYSTEM_USER} добавлен в группу ${LOG_FILE_GROUP} (владелец ${LOG_FILE_PATH})."
  fi
fi
if getent group adm >/dev/null 2>&1; then
  usermod -aG adm "${SYSTEM_USER}" 2>/dev/null || true
fi

# ================ ШАГ 11: ОБНОВЛЕНИЕ SYSTEMD-СЕРВИСА =========================

step "[11/12] Обновление systemd-сервиса"

cat > "${SERVICE_FILE}" <<EOF
[Unit]
Description=Vector log-shipping agent (${SERVICE_NAME})
Documentation=https://vector.dev/docs/
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=5

[Service]
Type=exec
User=${SYSTEM_USER}
Group=${SYSTEM_GROUP}
ExecStartPre=${VECTOR_BIN} validate --no-environment ${CONFIG_FILE}
ExecStart=${VECTOR_BIN} --config ${CONFIG_FILE}
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
AmbientCapabilities=CAP_DAC_READ_SEARCH
WorkingDirectory=${DATA_DIR}
StandardOutput=journal
StandardError=journal
SyslogIdentifier=${SERVICE_NAME}

[Install]
WantedBy=multi-user.target
EOF

chmod 644 "${SERVICE_FILE}"
ok "Unit-файл обновлён: ${SERVICE_FILE}"

# ================ ШАГ 12: ПЕРЕЗАПУСК СЕРВИСА И СТАТУС ========================

step "[12/12] Перезапуск сервиса и вывод статуса"

systemctl daemon-reload
systemctl enable "${SERVICE_NAME}"
systemctl restart "${SERVICE_NAME}"

sleep 2
systemctl --no-pager -l status "${SERVICE_NAME}" || true

echo ""
if systemctl is-active --quiet "${SERVICE_NAME}"; then
  ok "Сервис ${SERVICE_NAME} запущен и активен."
else
  err "Сервис ${SERVICE_NAME} не запустился. Проверьте журнал:"
  echo "  journalctl -u ${SERVICE_NAME} -n 50 --no-pager"
  exit 1
fi

mapfile -t FINAL_LOG_PATHS < <(parse_included_log_paths "${CONFIG_FILE}")

echo ""
echo "========================================================================"
echo " Установка/обновление завершено."
echo "   Исполняемый файл : ${VECTOR_BIN}"
echo "   Конфигурация     : ${CONFIG_FILE}"
echo "   Сервис           : ${SERVICE_NAME}"
echo "   Источники логов (${#FINAL_LOG_PATHS[@]}):"
for p in "${FINAL_LOG_PATHS[@]}"; do
  echo "     - ${p}"
done
echo ""
echo " Полезные команды:"
echo "   systemctl status  ${SERVICE_NAME}"
echo "   systemctl restart ${SERVICE_NAME}"
echo "   journalctl -u ${SERVICE_NAME} -f"
echo ""
echo " Чтобы добавить ещё один лог-файл, запустите скрипт повторно —"
echo " уже подключённые файлы будут исключены из поиска автоматически."
echo "========================================================================"
