#!/usr/bin/env php
<?php
// coraza-audit-rollup.php — parse /var/log/coraza/audit.log, populate
// coraza_rule_hits with 24h and 7d windows.
//
// Run from cron every 5 minutes (flock+timeout — see crontab change in Task 5.2).
// Idempotent: full-rebuild on each run (cheap; ~270k events parsed in ~2s on whp01).

require_once '/docker/whp/web/configs/config.php';

const AUDIT_LOG   = '/var/log/coraza/audit.log';
const SECURITY_DB = '/docker/whp/sql/security.db';

if (!file_exists(AUDIT_LOG)) {
    fwrite(STDERR, "audit.log not present yet — skipping\n");
    exit(0);
}

$now = time();
$windows = [
    '24h' => $now - 86400,
    '7d'  => $now - 604800,
];

$counters = ['24h' => [], '7d' => []];

$fh = fopen(AUDIT_LOG, 'r');
if (!$fh) {
    fwrite(STDERR, "cannot open audit.log\n");
    exit(1);
}

while (($line = fgets($fh)) !== false) {
    $line = trim($line);
    if ($line === '') continue;
    $j = json_decode($line, true);
    if (!$j || !isset($j['transaction']['timestamp'], $j['messages'])) continue;
    $ts = strtotime(str_replace('/', '-', $j['transaction']['timestamp']));
    if ($ts === false) continue;
    $host = $j['transaction']['server_id'] ?? '';
    $uri  = $j['transaction']['request']['uri'] ?? '';
    foreach ($j['messages'] as $m) {
        $rid = (int)($m['data']['id'] ?? 0);
        if ($rid === 0) continue;
        foreach ($windows as $win => $cutoff) {
            if ($ts < $cutoff) continue;
            if (!isset($counters[$win][$rid])) {
                $counters[$win][$rid] = ['hits' => 0, 'hosts' => [], 'uris' => []];
            }
            $counters[$win][$rid]['hits']++;
            if ($host !== '') {
                $counters[$win][$rid]['hosts'][$host] =
                    ($counters[$win][$rid]['hosts'][$host] ?? 0) + 1;
            }
            if ($uri !== '') {
                $counters[$win][$rid]['uris'][$uri] =
                    ($counters[$win][$rid]['uris'][$uri] ?? 0) + 1;
            }
        }
    }
}
fclose($fh);

$db = new SQLite3(SECURITY_DB);
$db->busyTimeout(10000);
$db->exec('PRAGMA journal_mode = WAL');
$db->exec('BEGIN');
$db->exec('DELETE FROM coraza_rule_hits');

$ins = $db->prepare('INSERT INTO coraza_rule_hits
    (rule_id, window, hits, top_hosts, top_uris, computed_at)
    VALUES (:r, :w, :h, :th, :tu, :c)');

foreach ($counters as $win => $perRule) {
    foreach ($perRule as $rid => $c) {
        arsort($c['hosts']);
        arsort($c['uris']);
        $topHosts = [];
        foreach (array_slice($c['hosts'], 0, 5, true) as $h => $n) {
            $topHosts[] = ['host' => $h, 'n' => $n];
        }
        $topUris = [];
        foreach (array_slice($c['uris'], 0, 5, true) as $u => $n) {
            $topUris[] = ['uri' => $u, 'n' => $n];
        }
        $ins->bindValue(':r', $rid, SQLITE3_INTEGER);
        $ins->bindValue(':w', $win, SQLITE3_TEXT);
        $ins->bindValue(':h', $c['hits'], SQLITE3_INTEGER);
        $ins->bindValue(':th', json_encode($topHosts), SQLITE3_TEXT);
        $ins->bindValue(':tu', json_encode($topUris), SQLITE3_TEXT);
        $ins->bindValue(':c', $now, SQLITE3_INTEGER);
        $ins->execute();
        $ins->reset();
    }
}

$db->exec('COMMIT');
$db->close();

$tot = array_sum(array_map('count', $counters));
echo "coraza-audit-rollup.php: OK ($tot rule-window rows written at $now)\n";
exit(0);
