<?php

function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

function redirect($path) {
    $url = BASE_URL . $path;
    if (!headers_sent()) {
        header("Location: " . $url);
    } else {
        echo '<script>window.location.href = ' . json_encode($url) . ';</script>';
        echo '<noscript><meta http-equiv="refresh" content="0;url=' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '"></noscript>';
    }
    exit;
}

// Busca plano atual do usuário (simples: último registro)
function get_user_plan($user_id) {
    $pdo = db();
    $stmt = $pdo->prepare("
        SELECT p.*
        FROM subscriptions s
        JOIN plans p ON p.id = s.plan_id
        WHERE s.user_id = ?
        ORDER BY s.created_at DESC
        LIMIT 1
    ");
    $stmt->execute([$user_id]);
    $plan = $stmt->fetch();
    if (!$plan) {
        // Plano Free padrão
        $stmt = $pdo->prepare("SELECT * FROM plans WHERE name = 'Free' LIMIT 1");
        $stmt->execute();
        $plan = $stmt->fetch();
        if ($plan) {
            $stmt2 = $pdo->prepare("INSERT INTO subscriptions (user_id, plan_id) VALUES (?, ?)");
            $stmt2->execute([$user_id, $plan['id']]);
        }
    }
    return $plan;
}

function count_user_ips($user_id) {
    $pdo = db();
    $stmt = $pdo->prepare("SELECT COUNT(*) FROM monitored_ips WHERE user_id = ?");
    $stmt->execute([$user_id]);
    return (int)$stmt->fetchColumn();
}

function get_user_name(PDO $pdo, int $userId): ?string {
    static $cache = [];
    if (array_key_exists($userId, $cache)) {
        return $cache[$userId];
    }
    $stmt = $pdo->prepare("SELECT name FROM users WHERE id = ? LIMIT 1");
    $stmt->execute([$userId]);
    $name = $stmt->fetchColumn() ?: null;
    $cache[$userId] = $name;
    return $name;
}

function system_settings_cache(bool $reset = false): array {
    static $cache = null;
    if ($reset) {
        $cache = null;
    }
    if ($cache !== null) {
        return $cache;
    }
    $pdo = db();
    $stmt = $pdo->query("SELECT `key`, `value` FROM system_settings");
    $cache = [];
    foreach ($stmt->fetchAll() as $row) {
        $cache[$row['key']] = $row['value'];
    }
    return $cache;
}

function get_system_setting(string $key, $default = null) {
    $settings = system_settings_cache();
    return array_key_exists($key, $settings) ? $settings[$key] : $default;
}

function set_system_setting(string $key, $value): void {
    $pdo = db();
    $stmt = $pdo->prepare("
        INSERT INTO system_settings (`key`, `value`)
        VALUES (?, ?)
        ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)
    ");
    $stmt->execute([$key, $value]);
    system_settings_cache(true);
}

function get_smtp_settings(): array {
    static $config = null;
    if ($config !== null) {
        return $config;
    }
    $config = [
        'host' => get_system_setting('smtp_host', SMTP_HOST),
        'port' => (int)get_system_setting('smtp_port', SMTP_PORT),
        'user' => get_system_setting('smtp_user', SMTP_USER),
        'pass' => get_system_setting('smtp_pass', SMTP_PASS),
        'secure' => strtolower(get_system_setting('smtp_secure', SMTP_SECURE ?: 'tls')),
        'from' => get_system_setting('smtp_from', SMTP_FROM),
        'from_name' => get_system_setting('smtp_from_name', SMTP_FROM_NAME),
        'timeout' => (int)get_system_setting('smtp_timeout', SMTP_TIMEOUT ?: 10),
    ];
    return $config;
}

function get_whatsapp_settings(): array {
    static $config = null;
    if ($config !== null) {
        return $config;
    }
    $config = [
        'webhook_url' => get_system_setting('whatsapp_webhook_url', WHATSAPP_WEBHOOK_URL),
        'token' => get_system_setting('whatsapp_webhook_token', WHATSAPP_WEBHOOK_TOKEN),
    ];
    return $config;
}

function db_column_exists(PDO $pdo, string $table, string $column): bool {
    static $cache = [];
    $key = $table . ':' . $column;
    if (array_key_exists($key, $cache)) {
        return $cache[$key];
    }
    try {
        $stmt = $pdo->prepare("SHOW COLUMNS FROM `$table` LIKE ?");
        $stmt->execute([$column]);
        $exists = (bool)$stmt->fetch();
    } catch (Throwable $e) {
        $exists = false;
    }
    $cache[$key] = $exists;
    return $exists;
}

function ensure_monitored_ips_check_method(PDO $pdo): bool {
    if (db_column_exists($pdo, 'monitored_ips', 'check_method')) {
        return true;
    }
    try {
        $pdo->exec("ALTER TABLE monitored_ips ADD COLUMN check_method ENUM('auto','icmp','tcp') NOT NULL DEFAULT 'auto'");
    } catch (Throwable $e) {
        return false;
    }
    return db_column_exists($pdo, 'monitored_ips', 'check_method');
}

function split_host_and_port(string $host): array {
    $host = trim($host);
    $port = null;
    if ($host !== '' && preg_match('/^(.+):(\d+)$/', $host, $matches) && strpos($host, ']') === false) {
        $host = $matches[1];
        $port = (int)$matches[2];
    }
    return ['host' => $host, 'port' => $port];
}

function ping_host($host, $timeout_ms = 1000, string $method = 'auto'): array {
    $host = trim($host);
    if ($host === '') {
        return ['status' => 'offline', 'latency_ms' => null];
    }

    $parts = split_host_and_port($host);
    $host = $parts['host'];
    $port = $parts['port'];
    $method = strtolower(trim($method));
    if (!in_array($method, ['auto', 'icmp', 'tcp'], true)) {
        $method = 'auto';
    }

    if ($method === 'tcp') {
        return tcp_ping_fallback($host, $timeout_ms, $port);
    }

    $execAvailable = function_exists('exec') && !in_array('exec', array_map('trim', explode(',', (string)ini_get('disable_functions'))));
    $isWindows = stripos(PHP_OS, 'WIN') === 0;

    if ($execAvailable) {
        $timeout_s = max(1, (int)ceil($timeout_ms / 1000));
        if ($isWindows) {
            $cmd = sprintf('ping -n 1 -w %d %s', $timeout_ms, escapeshellarg($host));
        } else {
            $cmd = sprintf('ping -c 1 -W %d %s', $timeout_s, escapeshellarg($host));
        }
        $output = [];
        $return_var = 0;
        @exec($cmd, $output, $return_var);

        if ($return_var === 0) {
            $text = implode("\n", $output);
            if (preg_match('/time[=<]\s*([\d\.]+)\s*ms/i', $text, $m)) {
                $latency = (int)round((float)$m[1]);
                return ['status' => 'online', 'latency_ms' => $latency];
            }
            return ['status' => 'online', 'latency_ms' => null];
        }
        if ($method === 'icmp') {
            return ['status' => 'offline', 'latency_ms' => null];
        }
    } elseif ($method === 'icmp') {
        return ['status' => 'offline', 'latency_ms' => null];
    }

    return tcp_ping_fallback($host, $timeout_ms, $port);
}

function tcp_ping_fallback(string $host, int $timeout_ms = 1000, ?int $forcePort = null): array {
    $parts = split_host_and_port($host);
    $host = $parts['host'];
    $port = $forcePort;
    if ($port === null) {
        $port = $parts['port'];
    }

    $portsToTry = $port ? [$port] : [80, 443, 22, 53];
    $timeout_s = max(0.2, $timeout_ms / 1000);

    foreach ($portsToTry as $currentPort) {
        $start = microtime(true);
        $connection = @fsockopen($host, $currentPort, $errno, $errstr, $timeout_s);
        if ($connection) {
            $latency = (int)round((microtime(true) - $start) * 1000);
            fclose($connection);
            return ['status' => 'online', 'latency_ms' => $latency];
        }
    }

    return ['status' => 'offline', 'latency_ms' => null];
}

function get_alert_channels(int $ipId): array {
    $pdo = db();
    $stmt = $pdo->prepare("SELECT * FROM alert_channels WHERE monitored_ip_id = ? ORDER BY id ASC");
    $stmt->execute([$ipId]);
    return $stmt->fetchAll() ?: [];
}

function generate_slug(int $length = 12): string {
    $alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789';
    $max = strlen($alphabet) - 1;
    $slug = '';
    for ($i = 0; $i < $length; $i++) {
        $slug .= $alphabet[random_int(0, $max)];
    }
    return $slug;
}

function csrf_token(): string {
    if (session_status() !== PHP_SESSION_ACTIVE) {
        session_start();
    }
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(16));
    }
    return $_SESSION['csrf_token'];
}

function csrf_check(?string $token): bool {
    if (!$token || session_status() !== PHP_SESSION_ACTIVE || empty($_SESSION['csrf_token'])) {
        return false;
    }
    return hash_equals($_SESSION['csrf_token'], $token);
}

function rate_limit_check(string $key, int $seconds): bool {
    if (session_status() !== PHP_SESSION_ACTIVE) {
        session_start();
    }
    $now = time();
    $last = $_SESSION['rate_limit'][$key] ?? 0;
    if ($now - $last < $seconds) {
        return false;
    }
    $_SESSION['rate_limit'][$key] = $now;
    return true;
}

function validate_monitor_host(string $host, ?string &$error = null): bool {
    $error = null;
    $parts = split_host_and_port($host);
    $host = trim($parts['host']);
    $port = $parts['port'];
    if ($host === '') {
        $error = 'Host vazio.';
        return false;
    }
    if (stripos($host, '://') !== false) {
        $error = 'Informe apenas host ou host:porta (sem http://).';
        return false;
    }
    if ($port !== null && ($port < 1 || $port > 65535)) {
        $error = 'Porta invalida.';
        return false;
    }
    $isIp = filter_var($host, FILTER_VALIDATE_IP);
    if ($isIp !== false) {
        if (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
            $error = 'Endereco IP privado nao permitido.';
            return false;
        }
        return true;
    }
    if (!preg_match('/^(?=.{3,255}$)([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/', $host)) {
        $error = 'Dominio invalido.';
        return false;
    }
    return true;
}

function send_email_message(string $to, string $subject, string $message, ?string &$error = null, ?string $htmlMessage = null): bool {
    $smtp = get_smtp_settings();
    $to = trim($to);
    if ($to === '') {
        $error = 'Destinatário vazio.';
        return false;
    }

    if (empty($smtp['host'])) {
        $headers = "From: " . $smtp['from_name'] . " <" . $smtp['from'] . ">\r\n";
        if ($htmlMessage !== null) {
            $headers .= "MIME-Version: 1.0\r\n";
            $headers .= "Content-Type: text/html; charset=UTF-8\r\n";
            $headers .= "Content-Transfer-Encoding: 8bit\r\n";
            $body = $htmlMessage;
        } else {
            $headers .= "MIME-Version: 1.0\r\n";
            $headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
            $headers .= "Content-Transfer-Encoding: 8bit\r\n";
            $body = $message;
        }
        if (@mail($to, $subject, $body, $headers)) {
            return true;
        }
        $error = 'mail() retornou false';
        return false;
    }

    try {
        smtp_send_mail($to, $subject, $message, $smtp, $htmlMessage);
        return true;
    } catch (Exception $e) {
        $error = $e->getMessage();
        return false;
    }
}

function smtp_send_mail(string $to, string $subject, string $message, array $smtpConfig, ?string $htmlMessage = null): void {
    $host = $smtpConfig['host'];
    $port = (int)($smtpConfig['port'] ?: 587);
    $secure = in_array($smtpConfig['secure'], ['tls', 'ssl', 'none'], true) ? $smtpConfig['secure'] : 'tls';
    $timeout = max(5, (int)($smtpConfig['timeout'] ?: 10));
    $remote = ($secure === 'ssl' ? 'ssl://' : '') . $host . ':' . $port;
    $socket = @stream_socket_client($remote, $errno, $errstr, $timeout);
    if (!$socket) {
        throw new Exception("Falha ao conectar ao SMTP: {$errstr} ({$errno})");
    }
    stream_set_timeout($socket, $timeout);

    $read = function () use ($socket) {
        $data = '';
        while ($line = fgets($socket, 515)) {
            $data .= $line;
            if (isset($line[3]) && $line[3] === ' ') {
                break;
            }
        }
        return $data;
    };

    $expect = function (array $codes) use ($read) {
        $response = $read();
        $code = substr($response, 0, 3);
        if (!in_array($code, $codes, true)) {
            throw new Exception("Resposta SMTP inesperada: " . trim($response));
        }
        return $response;
    };

    $write = function (string $cmd) use ($socket) {
        fwrite($socket, $cmd . "\r\n");
    };

    $expect(['220']);
    $write('EHLO ' . gethostname());
    $expect(['250']);

    if ($secure === 'tls') {
        $write('STARTTLS');
        $expect(['220']);
        if (!stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
            throw new Exception('Falha ao iniciar TLS no SMTP.');
        }
        $write('EHLO ' . gethostname());
        $expect(['250']);
    }

    if (!empty($smtpConfig['user'])) {
        $write('AUTH LOGIN');
        $expect(['334']);
        $write(base64_encode($smtpConfig['user']));
        $expect(['334']);
        $write(base64_encode($smtpConfig['pass']));
        $expect(['235']);
    }

    $from = $smtpConfig['from'] ?: $smtpConfig['user'];
    $write('MAIL FROM:<' . $from . '>');
    $expect(['250']);

    $recipients = preg_split('/[,;]/', $to);
    $validRcpt = false;
    foreach ($recipients as $recipient) {
        $recipient = trim($recipient);
        if ($recipient === '') continue;
        $write('RCPT TO:<' . $recipient . '>');
        $expect(['250', '251']);
        $validRcpt = true;
    }

    if (!$validRcpt) {
        throw new Exception('Nenhum destinatário válido informado.');
    }

    $write('DATA');
    $expect(['354']);

    $headers = [
        "From: " . $smtpConfig['from_name'] . " <" . $from . ">",
        "To: " . $to,
        "Subject: " . $subject,
        "MIME-Version: 1.0"
    ];

    if ($htmlMessage !== null) {
        $headers[] = "Content-Type: text/html; charset=UTF-8";
        $headers[] = "Content-Transfer-Encoding: 8bit";
        $body = $htmlMessage;
    } else {
        $headers[] = "Content-Type: text/plain; charset=UTF-8";
        $headers[] = "Content-Transfer-Encoding: 8bit";
        $body = $message;
    }

    $normalizedBody = preg_replace('/^\./m', '..', str_replace(["\r\n", "\n"], "\r\n", $body));
    $write(implode("\r\n", $headers) . "\r\n\r\n" . $normalizedBody . "\r\n.");
    $expect(['250']);

    $write('QUIT');
    $expect(['221']);
    fclose($socket);
}

function sync_alert_channels(int $ipId, array $channels, ?string $fallbackEmail = null): void {
    $pdo = db();
    ensure_whatsapp_enum($pdo);
    $inserted = 0;
    $stmt = $pdo->prepare("
        INSERT INTO alert_channels
        (monitored_ip_id, type, target, min_failures, cooldown_minutes, silence_start, silence_end, active)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?)
    ");
    $pdo->prepare("DELETE FROM alert_channels WHERE monitored_ip_id = ?")->execute([$ipId]);

    foreach ($channels as $channel) {
        $type = isset($channel['type']) && in_array($channel['type'], ['email','webhook','sms','whatsapp'], true)
            ? $channel['type']
            : null;
        $target = trim($channel['target'] ?? '');
        if (!$type || $target === '') {
            continue;
        }

        $minFailures = max(1, (int)($channel['min_failures'] ?? 1));
        $cooldown = max(0, (int)($channel['cooldown_minutes'] ?? 0));
        $silenceStart = sanitize_channel_time($channel['silence_start'] ?? null);
        $silenceEnd = sanitize_channel_time($channel['silence_end'] ?? null);
        $active = isset($channel['active']) ? 1 : 0;

        $stmt->execute([
            $ipId,
            $type,
            $target,
            $minFailures,
            $cooldown,
            $silenceStart,
            $silenceEnd,
            $active
        ]);
        $inserted++;
    }

    if ($inserted === 0 && $fallbackEmail) {
        $stmt->execute([$ipId, 'email', trim($fallbackEmail), 1, 0, null, null, 1]);
    }
}

function sanitize_channel_time(?string $value): ?string {
    if (!$value) return null;
    $value = trim($value);
    if (preg_match('/^\d{2}:\d{2}$/', $value)) {
        return $value . ':00';
    }
    if (preg_match('/^\d{2}:\d{2}:\d{2}$/', $value)) {
        return $value;
    }
    return null;
}

function channel_in_silence_window(array $channel, DateTimeImmutable $now): bool {
    if (empty($channel['silence_start']) || empty($channel['silence_end'])) {
        return false;
    }
    $start = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $now->format('Y-m-d') . ' ' . $channel['silence_start']);
    $end = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $now->format('Y-m-d') . ' ' . $channel['silence_end']);
    if (!$start || !$end) {
        return false;
    }

    if ($end > $start) {
        return $now >= $start && $now <= $end;
    }

    // Silence wraps past midnight
    return $now >= $start || $now <= $end->modify('+1 day');
}

function channel_in_cooldown(array $channel, DateTimeImmutable $now): bool {
    if (empty($channel['last_sent_at']) || empty($channel['cooldown_minutes'])) {
        return false;
    }
    $cooldown = (int)$channel['cooldown_minutes'];
    if ($cooldown <= 0) {
        return false;
    }
    try {
        $last = new DateTimeImmutable($channel['last_sent_at']);
    } catch (Exception $e) {
        return false;
    }
    return $last->modify("+{$cooldown} minutes") > $now;
}

function build_alert_email_content(array $ip, string $status, ?int $latency, ?string $userName = null): array {
    $label = $ip['label'] ?? 'Canal monitorado';
    $host = $ip['host'] ?? '';
    $statusKey = strtolower(trim($status));
    $statusPretty = ucfirst($statusKey);
    $statusUpper = strtoupper($statusKey);
    $latencyText = $latency !== null ? $latency . ' ms' : 'N/A';
    $timestamp = date('Y-m-d H:i:s');
    $userName = $userName ?: 'MonitorPing';

    $subject = "[MonitorPing] {$label} ({$host}) está {$statusUpper}";
    $textMessage = "Host: {$label} ({$host})\nStatus: {$statusKey}\nLatência: {$latencyText}\nData/hora: {$timestamp}";

    $statusColor = $statusKey === 'online' ? '#22c55e' : ($statusKey === 'offline' ? '#ef4444' : '#64748b');
    $htmlHost = h($host);
    $htmlLabel = h($label);
    $htmlStatus = h($statusPretty);
    $htmlLatency = h($latencyText);
    $htmlTimestamp = h($timestamp);
    $htmlUser = h($userName);

    $htmlBody = <<<HTML
<!DOCTYPE html>
<html lang="pt-BR">
<body style="margin:0;padding:0;background:#f5f7fb;">
  <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f5f7fb;padding:24px 0;">
    <tr>
      <td align="center">
        <table role="presentation" width="480" cellpadding="0" cellspacing="0" style="background:#ffffff;border-radius:18px;padding:32px;font-family:'Inter',Arial,sans-serif;color:#0f172a;border:1px solid #e2e8f0;">
          <tr>
            <td align="center" style="padding-bottom:12px;">
              <div style="width:64px;height:64px;border-radius:16px;background:#eef2ff;display:flex;align-items:center;justify-content:center;">
                <span style="font-size:28px;">📡</span>
              </div>
            </td>
          </tr>
          <tr>
            <td align="center" style="padding-bottom:8px;">
              <h2 style="margin:0;font-size:22px;">Alerta de Canal</h2>
            </td>
          </tr>
          <tr>
            <td align="center" style="padding-bottom:18px;">
              <span style="display:inline-block;padding:6px 16px;border-radius:999px;font-weight:600;font-size:14px;background:{$statusColor};color:#ffffff;">{$htmlStatus}</span>
            </td>
          </tr>
          <tr>
            <td style="font-size:15px;line-height:1.6;color:#334155;padding-bottom:18px;">
              <p style="margin:0 0 12px;">Olá,</p>
              <p style="margin:0;">O canal <strong>{$htmlLabel}</strong> agora está <strong>{$htmlStatus}</strong>.</p>
            </td>
          </tr>
          <tr>
            <td>
              <table width="100%" cellspacing="0" cellpadding="0" style="border:1px solid #e2e8f0;border-radius:12px;border-collapse:collapse;font-size:14px;">
                <tr>
                  <td style="padding:10px 14px;border-bottom:1px solid #e2e8f0;background:#f8fafc;font-weight:600;width:40%;">Host</td>
                  <td style="padding:10px 14px;border-bottom:1px solid #e2e8f0;">{$htmlHost}</td>
                </tr>
                <tr>
                  <td style="padding:10px 14px;border-bottom:1px solid #e2e8f0;background:#f8fafc;font-weight:600;">Latência</td>
                  <td style="padding:10px 14px;border-bottom:1px solid #e2e8f0;">{$htmlLatency}</td>
                </tr>
                <tr>
                  <td style="padding:10px 14px;border-bottom:1px solid #e2e8f0;background:#f8fafc;font-weight:600;">Horário</td>
                  <td style="padding:10px 14px;border-bottom:1px solid #e2e8f0;">{$htmlTimestamp}</td>
                </tr>
                <tr>
                  <td style="padding:10px 14px;background:#f8fafc;font-weight:600;border-bottom:1px solid transparent;">Usuário</td>
                  <td style="padding:10px 14px;border-bottom:1px solid transparent;">{$htmlUser}</td>
                </tr>
              </table>
            </td>
          </tr>
          <tr>
            <td style="padding-top:18px;color:#334155;font-size:15px;">
              <p style="margin:0 0 6px;">Atenciosamente,</p>
              <p style="margin:0;font-weight:600;">{$htmlUser}</p>
            </td>
          </tr>
        </table>
      </td>
    </tr>
  </table>
</body>
</html>
HTML;

    return [
        'subject' => $subject,
        'text' => $textMessage,
        'html' => $htmlBody,
        'timestamp' => $timestamp
    ];
}

function send_channel_alert(PDO $pdo, array $channel, array $ip, string $status, ?int $latency, ?int $newFailures = null): void {
    $target = $channel['target'] ?? '';
    $type = $channel['type'] ?? 'email';
    ensure_whatsapp_enum($pdo);
    $userName = isset($ip['user_id']) ? get_user_name($pdo, (int)$ip['user_id']) : null;
    $emailContent = build_alert_email_content($ip, $status, $latency, $userName);
    $subject = $emailContent['subject'];
    $message = $emailContent['text'];
    $htmlMessage = $emailContent['html'];

    $statusFlag = 'success';
    $errorMessage = null;

    try {
        if ($type === 'email') {
            if (!send_email_message($target, $subject, $message, $errorMessage, $htmlMessage)) {
                $statusFlag = 'failed';
            }
        } elseif ($type === 'webhook') {
            $payload = json_encode([
                'host' => $ip['host'],
                'label' => $ip['label'],
                'status' => $status,
                'latency' => $latency,
                'timestamp' => date('c')
            ]);
            $context = stream_context_create([
                'http' => [
                    'method' => 'POST',
                    'header' => "Content-Type: application/json\r\n",
                    'content' => $payload,
                    'timeout' => 5
                ]
            ]);
            $result = @file_get_contents($target, false, $context);
            $httpCode = 0;
            if (!empty($http_response_header)) {
                foreach ($http_response_header as $headerLine) {
                    if (preg_match('#HTTP/\d\.\d\s+(\d+)#', $headerLine, $match)) {
                        $httpCode = (int)$match[1];
                        break;
                    }
                }
            }
            if ($result === false || $httpCode >= 400 || $httpCode === 0) {
                $statusFlag = 'failed';
                $errorMessage = 'Webhook erro HTTP ' . ($httpCode ?: 'N/A');
            }
        } elseif ($type === 'sms') {
            error_log("[MonitorPing] SMS para {$target}: {$message}");
        } elseif ($type === 'whatsapp') {
            $whats = get_whatsapp_settings();
            if (empty($whats['webhook_url'])) {
                $statusFlag = 'failed';
                $errorMessage = 'Webhook WhatsApp não configurado.';
            } else {
                $payload = json_encode([
                    'phone' => $target,
                    'host' => $ip['host'],
                    'label' => $ip['label'],
                    'status' => $status,
                    'latency' => $latency,
                    'timestamp' => date('c'),
                    'token' => $whats['token'],
                ]);
                $context = stream_context_create([
                    'http' => [
                        'method' => 'POST',
                        'header' => "Content-Type: application/json\r\n",
                        'content' => $payload,
                        'timeout' => 5
                    ]
                ]);
                $result = @file_get_contents($whats['webhook_url'], false, $context);
                $httpCode = 0;
                if (!empty($http_response_header)) {
                    foreach ($http_response_header as $headerLine) {
                        if (preg_match('#HTTP/\d\.\d\s+(\d+)#', $headerLine, $match)) {
                            $httpCode = (int)$match[1];
                            break;
                        }
                    }
                }
                if ($result === false || $httpCode >= 400 || $httpCode === 0) {
                    $statusFlag = 'failed';
                    $errorMessage = 'WhatsApp webhook erro HTTP ' . ($httpCode ?: 'N/A');
                }
            }
        }
    } catch (Throwable $e) {
        $statusFlag = 'failed';
        $errorMessage = $e->getMessage();
    }

    // Atribui severidade básica para observabilidade
    $severity = 'info';
    if ($status === 'offline') {
        $severity = 'critical';
    } elseif ($status === 'online') {
        $severity = 'info';
    } elseif ($latency !== null && $latency >= 200) {
        $severity = 'warning';
    }

    $stmtAlert = $pdo->prepare("
        INSERT INTO alerts_sent (monitored_ip_id, channel_id, type, status, error_message, message, sent_at, severity)
        VALUES (?, ?, ?, ?, ?, ?, NOW(), ?)
    ");
    $stmtAlert->execute([$ip['id'], $channel['id'] ?? null, $type, $statusFlag, $errorMessage, $message, $severity]);

    if (!empty($channel['id']) && $statusFlag === 'success') {
        $pdo->prepare("
            UPDATE alert_channels
            SET last_sent_at = NOW(), consecutive_failures = ?
            WHERE id = ?
        ")->execute([$status === 'online' ? 0 : ($newFailures ?? ($channel['consecutive_failures'] ?? 0)), $channel['id']]);
    }
}

function ensure_whatsapp_enum(PDO $pdo): void {}

function get_client_ip(): string {
    if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        return $_SERVER['HTTP_CF_CONNECTING_IP'];
    }
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim($ips[0]);
    }
    return $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
}

function check_rate_limit_ip(PDO $pdo, string $action, int $max_attempts, int $window_seconds): bool {
    $ip = get_client_ip();
    
    // Garante que a tabela existe sem precisar refazer scripts
    static $tableChecked = false;
    if (!$tableChecked) {
        $pdo->exec("
            CREATE TABLE IF NOT EXISTS rate_limits (
                id INT AUTO_INCREMENT PRIMARY KEY,
                ip VARCHAR(64) NOT NULL,
                action VARCHAR(50) NOT NULL,
                attempts INT NOT NULL DEFAULT 1,
                last_attempt DATETIME NOT NULL,
                INDEX idx_ip_act (ip, action)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
        ");
        $tableChecked = true;
    }
    
    // Limpeza de lixo antigo de 10% das vezes
    if (random_int(1, 10) === 1) {
        $pdo->exec("DELETE FROM rate_limits WHERE last_attempt < (NOW() - INTERVAL 1 DAY)");
    }

    $stmt = $pdo->prepare("SELECT attempts, last_attempt FROM rate_limits WHERE ip = ? AND action = ?");
    $stmt->execute([$ip, $action]);
    $row = $stmt->fetch();
    
    if ($row) {
        $last = strtotime($row['last_attempt']);
        if (time() - $last > $window_seconds) {
            // Período de bloqueio passou, reseta a contagem
            $pdo->prepare("UPDATE rate_limits SET attempts = 1, last_attempt = NOW() WHERE ip = ? AND action = ?")->execute([$ip, $action]);
            return true;
        }
        
        if ($row['attempts'] >= $max_attempts) {
            // Estourou o limite! Atualiza só a data pra manter a punição de tempo
            $pdo->prepare("UPDATE rate_limits SET last_attempt = NOW() WHERE ip = ? AND action = ?")->execute([$ip, $action]);
            return false;
        }
        
        $pdo->prepare("UPDATE rate_limits SET attempts = attempts + 1, last_attempt = NOW() WHERE ip = ? AND action = ?")->execute([$ip, $action]);
        return true;
    } else {
        $pdo->prepare("INSERT INTO rate_limits (ip, action, attempts, last_attempt) VALUES (?, ?, 1, NOW())")->execute([$ip, $action]);
        return true;
    }
}

function clear_rate_limit_ip(PDO $pdo, string $action): void {
    $ip = get_client_ip();
    $pdo->prepare("DELETE FROM rate_limits WHERE ip = ? AND action = ?")->execute([$ip, $action]);
}
