<?php
require_once __DIR__ . '/db.php';
require_once __DIR__ . '/includes/functions.php';

// Verificação simples de segurança: apenas aceita requests internos ou com token
$token = $_GET['token'] ?? '';
$settingsToken = get_system_setting('cron_http_token', CRON_HTTP_TOKEN);

$remoteIp = $_SERVER['REMOTE_ADDR'] ?? '';
$isPrivateIp = !filter_var($remoteIp, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
if ($token !== $settingsToken && !$isPrivateIp) {
    http_response_code(403);
    exit('Acesso Negado.');
}

$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if (!$id) {
    http_response_code(400);
    exit('ID invalido.');
}

$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM monitored_ips WHERE id = ? LIMIT 1");
$stmt->execute([$id]);
$ip = $stmt->fetch();

if (!$ip || !$ip['active']) {
    exit('Inativo ou Nao Encontrado.');
}

// Verifica se já não foi checado recentemente (evita double-run)
if ($ip['last_check_at']) {
    $last = strtotime($ip['last_check_at']);
    $interval = (int)$ip['interval_seconds'];
    if (time() - $last < $interval - 2) {
        // Menos do que o intervalo, pula
        exit('Skipped (Too soon)');
    }
}

// 1. Faz o Ping
$result = ping_host($ip['host'], (int)$ip['timeout_ms'], $ip['check_method'] ?? 'auto');
$status = $result['status'];
$latency = $result['latency_ms'];
$now = new DateTimeImmutable('now');

// 2. Grava log
$stmtLog = $pdo->prepare("
    INSERT INTO ping_logs (monitored_ip_id, status, latency_ms, checked_at)
    VALUES (?, ?, ?, NOW())
");
$stmtLog->execute([$ip['id'], $status, $latency]);

// 3. Atualiza IP
$previousStatus = $ip['last_status'];

// Rastreia início da queda (last_offline_at)
if ($status === 'offline' && $previousStatus !== 'offline') {
    // Primeira vez offline: registra o momento
    $pdo->prepare("
        UPDATE monitored_ips
        SET last_status = ?, last_latency_ms = ?, last_check_at = NOW(), last_offline_at = NOW()
        WHERE id = ?
    ")->execute([$status, $latency, $ip['id']]);
} elseif ($status === 'online' && $previousStatus === 'offline') {
    // Voltou online: mantém last_offline_at para calcular duração, limpa depois
    $pdo->prepare("
        UPDATE monitored_ips
        SET last_status = ?, last_latency_ms = ?, last_check_at = NOW()
        WHERE id = ?
    ")->execute([$status, $latency, $ip['id']]);
} else {
    $pdo->prepare("
        UPDATE monitored_ips
        SET last_status = ?, last_latency_ms = ?, last_check_at = NOW()
        WHERE id = ?
    ")->execute([$status, $latency, $ip['id']]);
}

// 4. Verifica janela de manutenção — suprime alertas se ativo em manutenção
if (ip_in_maintenance($pdo, (int)$ip['id'], (int)$ip['user_id'])) {
    echo "OK (Em Manutenção — alertas suprimidos)";
    exit;
}

// 5. Processa alertas
$channels = get_alert_channels((int)$ip['id']);
if (!$channels && !empty($ip['alert_email'])) {
    if ($previousStatus !== 'unknown' && $previousStatus !== $status) {
        $subject = "[MonitorPing] " . $ip['label'] . " (" . $ip['host'] . ") está agora " . strtoupper($status);
        $message = "O host " . $ip['label'] . " (" . $ip['host'] . ") mudou de status para: " . $status .
                    "\nLatência: " . ($latency !== null ? $latency . " ms" : "N/A") .
                    "\nData/hora: " . date('Y-m-d H:i:s');
        @mail($ip['alert_email'], $subject, $message);

        $stmtAlert = $pdo->prepare("
            INSERT INTO alerts_sent (monitored_ip_id, channel_id, type, message, sent_at)
            VALUES (?, NULL, 'email', ?, NOW())
        ");
        $stmtAlert->execute([$ip['id'], $message]);
    }
    echo "OK (Basic Email)";
    exit;
}

foreach ($channels as $channel) {
    if (empty($channel['active'])) {
        continue;
    }
    $channelId   = $channel['id'] ?? null;
    $hadFailures = (int)($channel['consecutive_failures'] ?? 0);

    if ($status === 'online') {
        if ($channelId) {
            $pdo->prepare("UPDATE alert_channels SET consecutive_failures = 0 WHERE id = ?")->execute([$channelId]);
        }
        // Só envia recuperação se o canal havia atingido o limiar de falhas
        if ($previousStatus === 'offline'
            && $hadFailures >= max(1, (int)($channel['min_failures'] ?? 1))
            && ($channel['notify_recovery'] ?? true)
        ) {
            // Calcula duração da queda
            $outageDuration = !empty($ip['last_offline_at'])
                ? format_outage_duration($ip['last_offline_at'])
                : null;
            // Limpa last_offline_at após recuperação
            $pdo->prepare("UPDATE monitored_ips SET last_offline_at = NULL WHERE id = ?")->execute([$ip['id']]);
            $channel['consecutive_failures'] = 0;
            $channel['last_sent_at']         = $now->format('Y-m-d H:i:s');
            send_channel_alert($pdo, $channel, $ip, $status, $latency, null, $outageDuration);
        }
        continue;
    }

    // Status = offline
    $newFailures = $hadFailures + 1;
    if ($channelId) {
        $pdo->prepare("UPDATE alert_channels SET consecutive_failures = ? WHERE id = ?")->execute([$newFailures, $channelId]);
    }

    if ($newFailures < max(1, (int)($channel['min_failures'] ?? 1))) {
        continue;
    }
    if (channel_in_cooldown($channel, $now)) {
        continue;
    }
    if (channel_in_silence_window($channel, $now)) {
        continue;
    }

    $channel['consecutive_failures'] = $newFailures;
    $channel['last_sent_at']         = $now->format('Y-m-d H:i:s');
    send_channel_alert($pdo, $channel, $ip, $status, $latency, $newFailures);
}

// #45 Alerta de latência alta — gerencia incidentes de período (open/close)
if (!empty($ip['latency_alert_ms'])) {
    $threshold = (int)$ip['latency_alert_ms'];

    // Busca incidente de latência aberto para este IP
    $openIncStmt = $pdo->prepare("
        SELECT id, peak_latency_ms, checks_above_threshold, alert_sent
        FROM latency_incidents
        WHERE monitored_ip_id = ? AND recovered_at IS NULL
        LIMIT 1
    ");
    $openIncStmt->execute([$ip['id']]);
    $openIncident = $openIncStmt->fetch();

    if ($status === 'online' && $latency !== null && $latency >= $threshold) {
        // Registra ponto individual (histórico granular)
        try {
            $pdo->prepare("
                INSERT INTO latency_events (monitored_ip_id, latency_ms, threshold_ms, occurred_at)
                VALUES (?, ?, ?, NOW())
            ")->execute([$ip['id'], $latency, $threshold]);
        } catch (Throwable) {}

        if (!$openIncident) {
            // Abre novo incidente
            $pdo->prepare("
                INSERT INTO latency_incidents
                    (monitored_ip_id, threshold_ms, started_at, peak_latency_ms, checks_above_threshold, alert_sent)
                VALUES (?, ?, NOW(), ?, 1, FALSE)
            ")->execute([$ip['id'], $threshold, $latency]);

            // Envia alerta apenas na primeira ocorrência
            foreach ($channels as $channel) {
                if (empty($channel['active'])) continue;
                if (channel_in_silence_window($channel, $now)) continue;
                send_channel_alert($pdo, $channel, $ip, 'latency_high', $latency);
            }

            $pdo->prepare("
                UPDATE latency_incidents SET alert_sent = TRUE
                WHERE monitored_ip_id = ? AND recovered_at IS NULL
            ")->execute([$ip['id']]);
        } else {
            // Atualiza pico e contagem do incidente existente
            $newPeak   = max((int)$openIncident['peak_latency_ms'], $latency);
            $newChecks = (int)$openIncident['checks_above_threshold'] + 1;
            $pdo->prepare("
                UPDATE latency_incidents
                SET peak_latency_ms = ?, checks_above_threshold = ?
                WHERE id = ?
            ")->execute([$newPeak, $newChecks, $openIncident['id']]);
        }
    } elseif ($openIncident) {
        // Latência voltou ao normal ou dispositivo offline — fecha incidente
        $pdo->prepare("
            UPDATE latency_incidents
            SET recovered_at     = NOW(),
                duration_seconds = EXTRACT(EPOCH FROM (NOW() - started_at))::INT
            WHERE id = ?
        ")->execute([$openIncident['id']]);
    }
}

echo "OK (Channels Processed)";
