#!/usr/bin/env bash
# Автономность сбора: расписание сбора сырых JSON не должно исчезать НИКОГДА.
#
# Требование владельца, повторённое трижды за три дня: сбор идёт сам по расписанию и
# наша остальная работа его не касается. До этого сторожа связь была прямая и злая:
# выкладка приложения заменяет root crontab, и при её провале расписание оставалось
# ПУСТЫМ — сбор вставал целиком и молча. 08 и 09.08.2026 так вышло трижды.
#
# Что делает: раз в пять минут смотрит, есть ли у root расписание. Если его нет вовсе —
# возвращает из канонического файла выложенного дерева и пишет в журнал.
#
# Чего не делает: не трогает расписание, если оно есть (отличие от канона бывает
# законным), и не вмешивается, пока идёт выкладка — у неё свой откат.
set -uo pipefail

CANON=/opt/gwptd-analytics/repo/marketplace-collector-v3/scripts/crontab.s3-next.txt
TRANSACTION=/etc/gwptd/state/s3-next-deploy-transaction.env
LOG=/var/log/gwptd-collector-watchdog.log

say() { printf "%s %s\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$1" >> "$LOG"; }

lines="$(crontab -l 2>/dev/null | grep -c . || true)"
[ -n "$lines" ] || lines=0
[ "$lines" -gt 0 ] && exit 0

# Расписка о выкладке отключает сторожа ТОЛЬКО пока выкладка действительно идёт.
# 09.08.2026 застрявшая расписка обесточила сторожа насовсем: сбор стоял, а он молчал,
# потому что видел файл от оборванной час назад попытки. Признак «идёт» — живой процесс
# выкладки ИЛИ расписка не старше пятнадцати минут.
# Признак «выкладка идёт» — процесс, который ЗАПУЩЕН обёрткой, а не любой
# процесс, где её имя просто упомянуто.
#
# 11.08.2026 сторож промолчал при пустом расписании ровно из-за этой разницы.
# `pgrep -f gwptd-next-deploy` сработал на постороннюю диагностическую команду
# `ls -la /var/lib/gwptd-next-deploy/` — путь упомянут, выкладки нет. Сторож
# решил «выкладка идёт», не вмешался, и расписание пролежало пустым, пока его
# не подняли руками. То есть сторожа глушит ЛЮБОЙ, кто напишет это имя в своей
# командной строке, — включая grep по журналу.
#
# Проверяем позицию: у настоящего прогона путь стоит нулевым или первым словом
# командной строки (`/bin/bash /usr/local/sbin/gwptd-next-deploy --expected-sha …`).
# У упоминания он оказывается дальше — в аргументе ls, в теле `sh -c`, в шаблоне
# grep. Этого различения достаточно и оно не зависит от того, кто и как назвал
# свой юнит.
deploy_running=0
for p in /proc/[0-9]*/cmdline; do
  [ -r "$p" ] || continue
  # первые два слова командной строки, разделитель — нулевой байт
  first_two="$(tr '\0' '\n' < "$p" 2>/dev/null | head -2)"
  case "$first_two" in
    */usr/local/sbin/gwptd-next-deploy|/usr/local/sbin/gwptd-next-deploy*)
      deploy_running=1
      break
      ;;
  esac
done
if [ -e "$TRANSACTION" ] && [ "$deploy_running" = 1 ]; then
  say "расписание пусто, но выкладка ИДЁТ (живой процесс) — не вмешиваюсь"
  exit 0
fi
if [ -e "$TRANSACTION" ] && [ "$deploy_running" = 0 ]; then
  age=$(( $(date -u +%s) - $(stat -c %Y "$TRANSACTION" 2>/dev/null || echo 0) ))
  if [ "$age" -lt 900 ]; then
    say "расписание пусто, расписка свежая (${age}с) — жду завершения выкладки"
    exit 0
  fi
  say "расписка ЗАСТРЯЛА (${age}с, процесса выкладки нет) — восстанавливаю расписание"
fi
if [ ! -s "$CANON" ]; then
  say "ТРЕВОГА: расписание пусто, канонический файл недоступен: $CANON"
  exit 1
fi
if crontab "$CANON"; then
  say "расписание было ПУСТО и восстановлено из канона: $(crontab -l 2>/dev/null | grep -c .) строк"
else
  say "ТРЕВОГА: расписание пусто, восстановить НЕ УДАЛОСЬ"
  exit 1
fi
