#!/bin/bash
# scripts/pm2-monitor.sh
# ═══════════════════════════════════════════════════════════
# 🔍 PM2 Monitor with Telegram Alerts
# - هر 2 دقیقه توسط cron اجرا می‌شود
# - وضعیت PM2 process را چک می‌کند
# - اگر متوقف شده بود → ری‌استارت + هشدار تلگرام
# - اگر PM2 خودش از کار افتاده → هشدار ویژه
# ═══════════════════════════════════════════════════════════

set -euo pipefail

# ─── بارگذاری تنظیمات ───
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
ENV_FILE="$PROJECT_DIR/config/telegram.env"

if [ ! -f "$ENV_FILE" ]; then
    echo "❌ Config file not found: $ENV_FILE"
    exit 1
fi

source "$ENV_FILE"

# ─── متغیرها ───
LOG_FILE="$PROJECT_DIR/logs/monitor.log"
STATE_FILE="/tmp/pm2_monitor_state_${PM2_APP_NAME}"
ALERT_COOLDOWN=300  # حداقل 5 دقیقه بین هشدارها (جلوگیری از spam)

# ایجاد پوشه لاگ
mkdir -p "$(dirname "$LOG_FILE")"

# ─── تابع لاگ ───
log() {
    local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
    echo "$msg" >> "$LOG_FILE"
    echo "$msg"
}

# ─── تابع ارسال پیام تلگرام ───
send_telegram() {
    local message="$1"
    local parse_mode="${2:-HTML}"

    local response
    response=$(curl -s -w "\n%{http_code}" \
        --max-time 10 \
        -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
        -d "chat_id=${TELEGRAM_CHAT_ID}" \
        -d "text=${message}" \
        -d "parse_mode=${parse_mode}" \
        2>&1)

    local http_code
    http_code=$(echo "$response" | tail -1)
    local body
    body=$(echo "$response" | head -n -1)

    if [ "$http_code" = "200" ]; then
        log "✅ Telegram message sent successfully"
        return 0
    else
        log "❌ Telegram send failed (HTTP $http_code): $body"
        return 1
    fi
}

# ─── تابع چک cooldown (جلوگیری از ارسال مکرر) ───
should_alert() {
    local alert_type="$1"
    local state_key="${STATE_FILE}_${alert_type}"

    if [ ! -f "$state_key" ]; then
        echo "$(date +%s)" > "$state_key"
        return 0  # باید هشدار بدهد
    fi

    local last_alert
    last_alert=$(cat "$state_key" 2>/dev/null || echo "0")
    local now
    now=$(date +%s)
    local diff=$((now - last_alert))

    if [ "$diff" -ge "$ALERT_COOLDOWN" ]; then
        echo "$now" > "$state_key"
        return 0  # باید هشدار بدهد
    fi

    return 1  # هنوز زود است
}

# ─── تابع ریست cooldown (وقتی همه چیز درست شد) ───
reset_alert_state() {
    rm -f "${STATE_FILE}_"* 2>/dev/null
}

# ─── تابع دریافت اطلاعات سیستم ───
get_system_info() {
    local cpu_usage
    cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' 2>/dev/null || echo "N/A")

    local mem_info
    mem_info=$(free -m | awk '/^Mem:/{printf "%dMB / %dMB (%.0f%%)", $3, $2, ($3/$2)*100}' 2>/dev/null || echo "N/A")

    local disk_info
    disk_info=$(df -h / | awk 'NR==2{printf "%s / %s (%s)", $3, $2, $5}' 2>/dev/null || echo "N/A")

    local uptime_info
    uptime_info=$(uptime -p 2>/dev/null || echo "N/A")

    echo "CPU: ${cpu_usage}%
RAM: ${mem_info}
Disk: ${disk_info}
Uptime: ${uptime_info}"
}

# ═══════════════════════════════════════════════════════════
# 🔍 شروع بررسی
# ═══════════════════════════════════════════════════════════

log "─── Monitor check started ───"

# ─── چک ۱: آیا PM2 خودش نصب و در دسترس است؟ ───
if ! command -v pm2 &> /dev/null; then
    log "🔴 PM2 command not found!"

    if should_alert "pm2_missing"; then
        HOSTNAME=$(hostname 2>/dev/null || echo "unknown")

        send_telegram "🔴 <b>بحرانی: PM2 پیدا نشد!</b>

🖥 سرور: <code>${HOSTNAME}</code>
⏰ زمان: $(TZ='Europe/Berlin' date '+%Y-%m-%d %H:%M:%S %Z')

❌ دستور <code>pm2</code> در سیستم وجود ندارد یا در PATH نیست.

🔧 <b>اقدام لازم:</b>
<code>npm install -g pm2</code>
یا PATH را بررسی کنید."
    fi
    exit 1
fi

# ─── چک ۲: آیا PM2 daemon در حال اجراست؟ ───
PM2_PING=$(pm2 ping 2>&1 || true)

if echo "$PM2_PING" | grep -qi "error\|not running\|ECONNREFUSED"; then
    log "🔴 PM2 daemon is not running!"

    if should_alert "pm2_daemon"; then
        HOSTNAME=$(hostname 2>/dev/null || echo "unknown")
        SYS_INFO=$(get_system_info)

        send_telegram "🔴 <b>بحرانی: PM2 Daemon متوقف شده!</b>

🖥 سرور: <code>${HOSTNAME}</code>
⏰ زمان: $(TZ='Europe/Berlin' date '+%Y-%m-%d %H:%M:%S %Z')

❌ PM2 daemon پاسخ نمی‌دهد.

📊 <b>وضعیت سیستم:</b>
<pre>${SYS_INFO}</pre>

🔧 <b>اقدام خودکار:</b> تلاش برای ری‌استارت..."

        # تلاش برای ری‌استارت PM2
        log "🔄 Attempting to restart PM2 daemon..."
        pm2 resurrect 2>&1 || true
        sleep 3

        # بررسی مجدد
        if pm2 ping &>/dev/null; then
            log "✅ PM2 daemon restored successfully"
            send_telegram "✅ <b>PM2 Daemon بازیابی شد!</b>

🖥 سرور: <code>${HOSTNAME}</code>
⏰ زمان: $(TZ='Europe/Berlin' date '+%Y-%m-%d %H:%M:%S %Z')

✅ PM2 با موفقیت ری‌استارت شد."
        else
            log "❌ Failed to restore PM2 daemon"
            send_telegram "❌ <b>بازیابی PM2 ناموفق بود!</b>

🖥 سرور: <code>${HOSTNAME}</code>

🚨 نیاز به بررسی دستی دارد.
<code>ssh user@server</code>
<code>pm2 start ecosystem.config.js</code>"
        fi
    fi
    exit 1
fi

# ─── چک ۳: وضعیت Process مورد نظر ───
PROCESS_JSON=$(pm2 jlist 2>/dev/null || echo "[]")

# بررسی آیا process وجود دارد
PROCESS_EXISTS=$(echo "$PROCESS_JSON" | node -e "
    const data = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
    const proc = data.find(p => p.name === '${PM2_APP_NAME}');
    if (!proc) { console.log('NOT_FOUND'); process.exit(0); }
    console.log(JSON.stringify({
        status: proc.pm2_env.status,
        restarts: proc.pm2_env.restart_time || 0,
        uptime: proc.pm2_env.pm_uptime || 0,
        memory: proc.monit ? proc.monit.memory : 0,
        cpu: proc.monit ? proc.monit.cpu : 0,
        pid: proc.pid || 'N/A',
        unstable_restarts: proc.pm2_env.unstable_restarts || 0
    }));
" 2>/dev/null || echo "ERROR")

# ─── حالت: Process پیدا نشد ───
if [ "$PROCESS_EXISTS" = "NOT_FOUND" ] || [ "$PROCESS_EXISTS" = "ERROR" ]; then
    log "🔴 Process '${PM2_APP_NAME}' not found in PM2!"

    if should_alert "process_missing"; then
        HOSTNAME=$(hostname 2>/dev/null || echo "unknown")
        ALL_PROCESSES=$(pm2 list 2>/dev/null | head -20 || echo "N/A")

        send_telegram "🟠 <b>Process پیدا نشد: ${PM2_APP_NAME}</b>

🖥 سرور: <code>${HOSTNAME}</code>
⏰ زمان: $(TZ='Europe/Berlin' date '+%Y-%m-%d %H:%M:%S %Z')

❌ Process <code>${PM2_APP_NAME}</code> در لیست PM2 وجود ندارد.

🔧 <b>اقدام لازم:</b>
<code>cd /path/to/project</code>
<code>pm2 start ecosystem.config.js</code>

📋 <b>لیست فعلی PM2:</b>
<pre>$(pm2 list --no-color 2>/dev/null | head -15 || echo 'N/A')</pre>"
    fi
    exit 1
fi

# ─── پارس اطلاعات Process ───
PROC_STATUS=$(echo "$PROCESS_EXISTS" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.status);" 2>/dev/null || echo "unknown")
PROC_RESTARTS=$(echo "$PROCESS_EXISTS" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.restarts);" 2>/dev/null || echo "0")
PROC_MEMORY=$(echo "$PROCESS_EXISTS" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(Math.round(d.memory/1024/1024));" 2>/dev/null || echo "0")
PROC_CPU=$(echo "$PROCESS_EXISTS" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.cpu);" 2>/dev/null || echo "0")
PROC_PID=$(echo "$PROCESS_EXISTS" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.pid);" 2>/dev/null || echo "N/A")
PROC_UPTIME_MS=$(echo "$PROCESS_EXISTS" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.uptime);" 2>/dev/null || echo "0")

# محاسبه uptime خوانا
PROC_UPTIME_READABLE=$(node -e "
    const ms = Date.now() - ${PROC_UPTIME_MS};
    const h = Math.floor(ms/3600000);
    const m = Math.floor((ms%3600000)/60000);
    console.log(h + 'h ' + m + 'm');
" 2>/dev/null || echo "N/A")

log "Process '${PM2_APP_NAME}': status=${PROC_STATUS}, restarts=${PROC_RESTARTS}, memory=${PROC_MEMORY}MB, cpu=${PROC_CPU}%"

# ─── حالت: Process متوقف (stopped/errored) ───
if [ "$PROC_STATUS" = "stopped" ] || [ "$PROC_STATUS" = "errored" ]; then
    log "🔴 Process '${PM2_APP_NAME}' is ${PROC_STATUS}!"

    # خواندن آخرین خطوط لاگ
    LAST_LOGS=$(pm2 logs "${PM2_APP_NAME}" --nostream --lines 15 2>/dev/null | tail -15 || echo "No logs available")
    LAST_ERROR_LOGS=$(pm2 logs "${PM2_APP_NAME}" --err --nostream --lines 10 2>/dev/null | tail -10 || echo "No error logs")

    if should_alert "process_stopped"; then
        HOSTNAME=$(hostname 2>/dev/null || echo "unknown")
        SYS_INFO=$(get_system_info)

        # آیکون بر اساس وضعیت
        if [ "$PROC_STATUS" = "errored" ]; then
            STATUS_ICON="🔴"
            STATUS_TEXT="کرش کرده (Errored)"
        else
            STATUS_ICON="🟠"
            STATUS_TEXT="متوقف شده (Stopped)"
        fi

        send_telegram "${STATUS_ICON} <b>اسکرپر متوقف شد!</b>

🖥 سرور: <code>${HOSTNAME}</code>
⏰ زمان: $(TZ='Europe/Berlin' date '+%Y-%m-%d %H:%M:%S %Z')

📋 <b>جزئیات:</b>
├ نام: <code>${PM2_APP_NAME}</code>
├ وضعیت: <b>${STATUS_TEXT}</b>
├ PID آخر: <code>${PROC_PID}</code>
├ تعداد ری‌استارت: ${PROC_RESTARTS}
└ حافظه آخر: ${PROC_MEMORY}MB

📊 <b>سیستم:</b>
<pre>${SYS_INFO}</pre>

📝 <b>آخرین لاگ‌ها:</b>
<pre>$(echo "$LAST_LOGS" | tail -8 | head -c 800)</pre>

📝 <b>آخرین خطاها:</b>
<pre>$(echo "$LAST_ERROR_LOGS" | tail -5 | head -c 500)</pre>

🔧 <b>اقدام خودکار:</b> تلاش برای ری‌استارت..."

        # ری‌استارت خودکار
        log "🔄 Auto-restarting '${PM2_APP_NAME}'..."
        pm2 restart "${PM2_APP_NAME}" 2>&1 || true
        sleep 5

        # بررسی نتیجه ری‌استارت
        NEW_STATUS=$(pm2 jlist 2>/dev/null | node -e "
            const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
            const p=d.find(x=>x.name==='${PM2_APP_NAME}');
            console.log(p ? p.pm2_env.status : 'unknown');
        " 2>/dev/null || echo "unknown")

        if [ "$NEW_STATUS" = "online" ]; then
            log "✅ Process restarted successfully!"
            send_telegram "✅ <b>اسکرپر با موفقیت ری‌استارت شد!</b>

🖥 سرور: <code>${HOSTNAME}</code>
⏰ $(TZ='Europe/Berlin' date '+%Y-%m-%d %H:%M:%S %Z')
✅ وضعیت: <b>Online</b>
🔄 تعداد کل ری‌استارت: ${PROC_RESTARTS}"

            reset_alert_state
        else
            log "❌ Restart failed! New status: ${NEW_STATUS}"
            send_telegram "❌ <b>ری‌استارت ناموفق!</b>

🖥 سرور: <code>${HOSTNAME}</code>
وضعیت جدید: <code>${NEW_STATUS}</code>

🚨 <b>نیاز به بررسی دستی:</b>
<code>pm2 logs ${PM2_APP_NAME} --lines 50</code>
<code>pm2 restart ${PM2_APP_NAME}</code>"
        fi
    fi

    exit 1
fi

# ─── حالت: Process آنلاین - چک ری‌استارت‌های مشکوک ───
if [ "$PROC_STATUS" = "online" ]; then

    # هشدار اگر تعداد ری‌استارت زیاد باشد (بیش از 10 بار)
    if [ "$PROC_RESTARTS" -gt 10 ]; then
        if should_alert "high_restarts"; then
            HOSTNAME=$(hostname 2>/dev/null || echo "unknown")

            send_telegram "⚠️ <b>تعداد ری‌استارت بالا!</b>

🖥 سرور: <code>${HOSTNAME}</code>
⏰ $(TZ='Europe/Berlin' date '+%Y-%m-%d %H:%M:%S %Z')

📋 <b>جزئیات:</b>
├ نام: <code>${PM2_APP_NAME}</code>
├ وضعیت: ✅ Online
├ ری‌استارت‌ها: <b>${PROC_RESTARTS}</b> ⚠️
├ حافظه: ${PROC_MEMORY}MB
├ CPU: ${PROC_CPU}%
└ Uptime: ${PROC_UPTIME_READABLE}

⚠️ تعداد ری‌استارت بالاست. ممکن است crash loop وجود داشته باشد.

🔧 <b>بررسی کنید:</b>
<code>pm2 logs ${PM2_APP_NAME} --lines 50</code>"
        fi
    fi

    # هشدار مصرف حافظه بالا (بیش از 500MB)
    if [ "$PROC_MEMORY" -gt 500 ]; then
        if should_alert "high_memory"; then
            HOSTNAME=$(hostname 2>/dev/null || echo "unknown")

            send_telegram "⚠️ <b>مصرف حافظه بالا!</b>

🖥 سرور: <code>${HOSTNAME}</code>
├ Process: <code>${PM2_APP_NAME}</code>
├ حافظه: <b>${PROC_MEMORY}MB</b> ⚠️
├ CPU: ${PROC_CPU}%
└ Uptime: ${PROC_UPTIME_READABLE}

🔧 شاید نیاز به ری‌استارت باشد:
<code>pm2 restart ${PM2_APP_NAME}</code>"
        fi
    fi

    # همه چیز OK
    log "✅ Process '${PM2_APP_NAME}' is healthy (memory: ${PROC_MEMORY}MB, cpu: ${PROC_CPU}%, restarts: ${PROC_RESTARTS})"
    # ریست state وقتی همه چیز خوب است
    reset_alert_state
fi

log "─── Monitor check completed ───"
exit 0
