#!/bin/bash
# diag_staging.command — диагностика staging-установки Dolphin Manager
# Безопасно: только чтение. Не меняет файлы, не публикует секреты, не трогает prod.
# Запуск: двойной клик, или в Terminal: bash ~/dolphin-manager-staging/diag_staging.command

set +e

SITE="https://staging.kabanwork.82-221-101-9.sslip.io"
DM_DIR="$HOME/dolphin-manager-staging"
PROD_DIR="$HOME/dolphin-manager"
UID_=$(id -u)
PORT=8766
LABEL="com.chig1r.dolphin-manager-staging"

cyan()  { printf "\033[1;36m%s\033[0m\n" "$*"; }
green() { printf "\033[0;32m%s\033[0m\n" "$*"; }
red()   { printf "\033[0;31m%s\033[0m\n" "$*"; }
yellow(){ printf "\033[0;33m%s\033[0m\n" "$*"; }

pass_count=0
fail_count=0
notchk_count=0

check() {
  local name="$1"; local status="$2"; local detail="$3"
  case "$status" in
    PASS) green  "  ✓ $name"; pass_count=$((pass_count+1)) ;;
    FAIL) red    "  ✗ $name — $detail"; fail_count=$((fail_count+1)) ;;
    NC)   yellow "  ? $name — $detail"; notchk_count=$((notchk_count+1)) ;;
  esac
  [ -n "$detail" ] && [ "$status" = "PASS" ] && echo "       $detail"
}

echo
cyan "═══════════════════════════════════════════════════════════════════"
cyan "  Диагностика staging Dolphin Manager (read-only)"
cyan "═══════════════════════════════════════════════════════════════════"
echo

cyan "[A] Установка"

# A-1: наличие staging-каталога
if [ -d "$DM_DIR" ]; then
  check "staging-каталог существует ($DM_DIR)" PASS
else
  check "staging-каталог существует ($DM_DIR)" FAIL "каталог не создан — повтори one-liner установки"
fi

# A-2: ключевые файлы на диске
for f in app.py dm/server.py dm/__init__.py dm/staging_guard.py dm/updater.py install_autostart_staging.command uninstall_autostart_staging.command requirements.txt; do
  if [ -f "$DM_DIR/$f" ]; then
    check "файл $f" PASS
  else
    check "файл $f" FAIL "отсутствует в $DM_DIR/"
  fi
done

# A-3: staging_guard импортируется
if command -v python3 >/dev/null 2>&1; then
  if python3 -I -c "import sys; sys.path.insert(0, '$DM_DIR'); from dm import staging_guard; print(staging_guard._is_staging)" 2>/dev/null | grep -q "True\|False"; then
    is_staging=$(python3 -I -c "import sys; sys.path.insert(0, '$DM_DIR'); from dm import staging_guard; print(staging_guard._is_staging)" 2>/dev/null)
    check "dm.staging_guard импортируется (_is_staging=$is_staging)" PASS
  else
    check "dm.staging_guard импортируется" FAIL "ошибка импорта (неполный zip? повтори установку)"
  fi
else
  check "python3 найден" FAIL "python3 не установлен — поставь с python.org"
fi

echo
cyan "[B] Конфигурация"

if [ -f "$DM_DIR/config.json" ]; then
  if command -v python3 >/dev/null 2>&1; then
    cfg_out=$(python3 -I -c "
import json
try:
  c = json.load(open('$DM_DIR/config.json'))
  port = c.get('ui',{}).get('port','?')
  url  = c.get('agent',{}).get('url','?')
  en   = c.get('agent',{}).get('enabled', False)
  key  = c.get('agent',{}).get('key','') or ''
  ext_id = c.get('ext',{}).get('id','') or '(не задан)'
  name_match = c.get('ext',{}).get('name_match','') or ''
  api_tok = c.get('api_token','') or ''
  print(f'PORT={port}|URL={url}|EN={en}|KEY_LEN={len(key)}|EXT_ID={ext_id}|NAME_MATCH={name_match}|TOKEN_LEN={len(api_tok)}')
except Exception as e:
  print(f'ERR:{e}')
" 2>&1)
    IFS='|' read -ra parts <<< "$cfg_out"
    port_val=$(echo "${parts[0]}" | cut -d= -f2)
    url_val=$(echo "${parts[1]}" | cut -d= -f2-)
    en_val=$(echo "${parts[2]}" | cut -d= -f2)
    key_len=$(echo "${parts[3]}" | cut -d= -f2)
    ext_id_val=$(echo "${parts[4]}" | cut -d= -f2)
    name_match=$(echo "${parts[5]}" | cut -d= -f2)
    token_len=$(echo "${parts[6]}" | cut -d= -f2)

    [ "$port_val" = "8766" ] && check "config.ui.port = 8766" PASS || check "config.ui.port" FAIL "ожидалось 8766, фактически $port_val"
    echo "$url_val" | grep -q 'staging' && check "config.agent.url содержит 'staging'" PASS "$url_val" || check "config.agent.url" FAIL "ожидался staging-URL, фактически $url_val"
    [ "$en_val" = "True" ] && check "config.agent.enabled = true" PASS || check "config.agent.enabled" FAIL "не включён ($en_val)"
    [ "$key_len" != "0" ] && check "config.agent.key задан (длина $key_len символов)" PASS || check "config.agent.key" FAIL "ПУСТО — впиши api_key работника через UI"
    [ "$ext_id_val" = "lhdmopcibgkjkcjhinfpnlkdpcjgfkkm" ] && check "config.ext.id = staging-ID" PASS || check "config.ext.id" FAIL "ожидался lhdmopcibg... (staging), фактически $ext_id_val"
    [ "$name_match" = "[STAGING]" ] && check "config.ext.name_match = [STAGING]" PASS || check "config.ext.name_match" FAIL "ожидался '[STAGING]', фактически '$name_match'"
    [ "$token_len" = "0" ] && check "config.api_token пустой (ok для staging — реальный Dolphin API мокается)" PASS || check "config.api_token" NC "задан — но на staging guard его игнорирует"
  else
    check "config.json разбор" NC "python3 не найден"
  fi
else
  check "config.json существует" FAIL "файла нет — повтори install_autostart_staging.command"
fi

echo
cyan "[C] launchd"

if launchctl print "gui/$UID_/$LABEL" >/dev/null 2>&1; then
  check "staging-plist загружен ($LABEL)" PASS
  # env guard
  if launchctl print "gui/$UID_/$LABEL" 2>/dev/null | grep -q "DM_STAGING = 1"; then
    check "DM_STAGING=1 в plist environment" PASS
  else
    check "DM_STAGING=1 в plist" FAIL "guard не активируется — переустанови"
  fi
  if launchctl print "gui/$UID_/$LABEL" 2>/dev/null | grep -q "DM_EXT_ID_DEFAULT"; then
    val=$(launchctl print "gui/$UID_/$LABEL" 2>/dev/null | grep "DM_EXT_ID_DEFAULT" | head -1 | awk -F'= ' '{print $2}')
    check "DM_EXT_ID_DEFAULT в plist ($val)" PASS
  else
    check "DM_EXT_ID_DEFAULT в plist" FAIL "fallback уведёт на prod-расшир — переустанови"
  fi
else
  check "staging-plist загружен ($LABEL)" FAIL "сервис не зарегистрирован в launchd — запусти install_autostart_staging.command"
fi

# prod параллельно живёт — information
if launchctl print "gui/$UID_/com.chig1r.dolphin-manager" >/dev/null 2>&1; then
  check "prod-plist параллельно работает" PASS "prod не трогается"
else
  check "prod-plist" NC "prod-локалка не установлена (ок если ты её не ставил)"
fi

echo
cyan "[D] Процесс и порт $PORT"

if command -v lsof >/dev/null 2>&1; then
  pid=$(lsof -ti tcp:$PORT -sTCP:LISTEN 2>/dev/null | head -1)
  if [ -n "$pid" ]; then
    cmd=$(ps -o command= -p $pid 2>/dev/null | head -1)
    check "порт $PORT слушается (pid=$pid)" PASS
    echo "         $cmd"
  else
    check "порт $PORT слушается" FAIL "никто не слушает — локалка не запущена"
  fi
else
  check "lsof доступен" NC "утилита отсутствует (на Mac по умолчанию есть)"
fi

echo
cyan "[E] HTTP локалка"

if curl -s -m 5 "http://127.0.0.1:$PORT/api/ping" | grep -q "dolphin-manager"; then
  resp=$(curl -s -m 5 "http://127.0.0.1:$PORT/api/ping")
  ver=$(echo "$resp" | python3 -I -c 'import json,sys; print(json.load(sys.stdin).get("version","?"))' 2>/dev/null)
  check "http://127.0.0.1:$PORT/api/ping отвечает (version=$ver)" PASS
else
  check "http://127.0.0.1:$PORT/api/ping" FAIL "не отвечает — проверь логи $DM_DIR/logs/manager.err.log"
fi

echo
cyan "[F] Сервер $SITE"

if resp=$(curl -s -m 10 "$SITE/api/ping"); then
  if echo "$resp" | grep -q '"env": "staging"'; then
    ver=$(echo "$resp" | python3 -I -c 'import json,sys; print(json.load(sys.stdin).get("version","?"))' 2>/dev/null)
    check "сервер staging отвечает (version=$ver, env=staging)" PASS
  else
    check "сервер отвечает, но не staging" FAIL "ожидался env=staging, пришло: $resp"
  fi
else
  check "сервер $SITE" FAIL "не достучался — проверь интернет / VPN / файрвол"
fi

echo
cyan "[G] Heartbeat / последние ошибки"

LOG="$DM_DIR/logs/manager.log"
ERR="$DM_DIR/logs/manager.err.log"
if [ -f "$LOG" ]; then
  last_poll=$(tail -500 "$LOG" 2>/dev/null | grep -E 'agent.poll|/api/agent/poll' | tail -1)
  if [ -n "$last_poll" ]; then
    check "последний heartbeat (из manager.log)" PASS
    echo "         $(echo "$last_poll" | head -c 150)"
  else
    check "heartbeat в логах" NC "нет записей agent.poll — локалка не поллит (проверь ключ)"
  fi
else
  check "manager.log существует" NC "лога нет — локалка не запускалась после установки"
fi

if [ -f "$ERR" ]; then
  errcnt=$(tail -200 "$ERR" 2>/dev/null | grep -ciE 'error|exception|traceback')
  if [ "$errcnt" -gt 0 ]; then
    check "последние ошибки в manager.err.log" FAIL "$errcnt строк с ошибками в последних 200"
    echo "         последние 3 строки с error:"
    tail -200 "$ERR" | grep -iE 'error|exception' | tail -3 | sed 's/^/           /'
  else
    check "manager.err.log чистый" PASS
  fi
fi

echo
cyan "[H] Версия и режим"

if [ -f "$DM_DIR/dm/__init__.py" ]; then
  ver=$(grep '__version__' "$DM_DIR/dm/__init__.py" | head -1 | sed 's/.*"\(.*\)"/\1/')
  check "версия локалки на диске: $ver" PASS
  if echo "$ver" | grep -q 'staging'; then
    check "это staging-билд (суффикс -staging в версии)" PASS
  else
    check "это НЕ staging-билд — скачан prod zip?" FAIL "ожидался суффикс -staging"
  fi
fi

echo
cyan "═══════════════════════════════════════════════════════════════════"
cyan "  Итог: $pass_count PASS, $fail_count FAIL, $notchk_count NOT CHECKED"
cyan "═══════════════════════════════════════════════════════════════════"

if [ "$fail_count" = "0" ]; then
  green "  Всё готово к работе."
elif [ "$fail_count" -lt 3 ]; then
  yellow "  Есть FAIL — исправь по подсказкам выше и запусти скрипт ещё раз."
else
  red    "  Много FAIL — покажи этот вывод модератору через ❓ Поддержка на сайте."
fi
echo
