#!/usr/bin/env php
<?php
/**
 * contact-report — summary of everything posted to the Contact form.
 *
 *   contact-report              last 7 days: totals, per-day table, recent items
 *   contact-report 30           last 30 days
 *   contact-report 30 --all     also list every blocked (bot) item, not just real ones
 *   contact-report --csv 30 > contact.csv   export for a spreadsheet
 *
 * Reads /var/log/contact-relay-submissions/YYYY-MM.jsonl, written by send.php.
 */

const SUBMIT_DIR = '/var/log/contact-relay-submissions';
const REAL_OUTCOMES = ['sent', 'spooled', 'send-failed'];   // outcomes where a message got through the bot checks

$args = array_slice($argv, 1);
$csv  = in_array('--csv', $args, true);
$all  = in_array('--all', $args, true);
$nums = array_values(array_filter($args, 'ctype_digit'));
$days = (int) ($nums[0] ?? 7);
$from = time() - $days * 86400;

$rows = [];
foreach (glob(SUBMIT_DIR . '/*.jsonl') ?: [] as $file) {
    if (substr(basename($file), 0, 7) < gmdate('Y-m', $from)) continue;
    foreach (file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
        $r = json_decode($line, true);
        if (is_array($r) && strtotime($r['time']) >= $from) $rows[] = $r;
    }
}
usort($rows, fn($a, $b) => strcmp($a['time'], $b['time']));

if ($csv) {
    $out  = fopen('php://stdout', 'w');
    $cols = ['time', 'outcome', 'fill_secs', 'ip', 'name', 'email', 'reason', 'company', 'message', 'honeypot', 'origin', 'ua', 'error'];
    fputcsv($out, $cols, ',', '"', '');
    foreach ($rows as $r) fputcsv($out, array_map(fn($c) => $r[$c] ?? '', $cols), ',', '"', '');
    exit;
}

$line = str_repeat('─', 72);
$cut  = fn(string $s, int $n): string => mb_strlen($s) > $n ? mb_substr($s, 0, $n - 1) . '…' : $s;
$flat = fn(string $s): string => trim(preg_replace('/\s+/u', ' ', $s));

echo "\nContact form: last $days day(s)  (" . gmdate('Y-m-d', $from) . ' → ' . gmdate('Y-m-d') . " UTC)\n$line\n";
if (!$rows) { echo "No submissions.\n\n"; exit; }

// Totals by outcome
$by = array_count_values(array_column($rows, 'outcome'));
arsort($by);
$real = array_sum(array_intersect_key($by, array_flip(REAL_OUTCOMES)));
printf("Total posts: %d   Real messages: %d   Blocked as bots/invalid: %d\n\n", count($rows), $real, count($rows) - $real);
foreach ($by as $o => $n) printf("  %-14s %5d\n", $o, $n);

// Per-day table
echo "\nPer day (real / blocked):\n";
$perDay = [];
foreach ($rows as $r) {
    $d = substr($r['time'], 0, 10);
    $perDay[$d] ??= [0, 0];
    $perDay[$d][in_array($r['outcome'], REAL_OUTCOMES, true) ? 0 : 1]++;
}
foreach ($perDay as $d => [$ok, $bad]) printf("  %s   %3d / %-3d %s\n", $d, $ok, $bad, str_repeat('▇', min($bad, 40)));

// Top IPs among blocked posts
$blockedIps = array_count_values(array_column(array_filter($rows, fn($r) => !in_array($r['outcome'], REAL_OUTCOMES, true)), 'ip'));
if ($blockedIps) {
    arsort($blockedIps);
    echo "\nTop blocked IPs:\n";
    foreach (array_slice($blockedIps, 0, 5, true) as $ip => $n) printf("  %-40s %4d\n", $ip, $n);
}

// Item list
$list = $all ? $rows : array_filter($rows, fn($r) => in_array($r['outcome'], REAL_OUTCOMES, true));
echo "\n" . ($all ? 'All posts' : 'Real messages') . ' (newest first' . ($all ? '' : '; add --all to include blocked') . "):\n$line\n";
foreach (array_reverse($list) as $r) {
    printf("%s  %-12s %s  [%s]\n", str_replace(['T', '+00:00'], [' ', 'Z'], $r['time']), $r['outcome'],
        isset($r['fill_secs']) ? sprintf('%4ds', $r['fill_secs']) : '   -', $r['ip']);
    printf("  %s <%s>  · %s%s\n", $cut($flat($r['name']), 40) ?: '(no name)', $cut($r['email'], 60) ?: '-',
        $r['reason'] ?: '-', $r['company'] !== '' ? ' · ' . $cut($flat($r['company']), 40) : '');
    echo '  "' . $cut($flat($r['message']), 110) . "\"\n";
    if (($r['honeypot'] ?? '') !== '') echo '  honeypot: "' . $cut($flat($r['honeypot']), 60) . "\"\n";
    if (!empty($r['error'])) echo '  error: ' . $cut($r['error'], 110) . "\n";
    echo "\n";
}
