#!/usr/bin/env php
<?php
/**
 * CLI smoke-test for TrafficAnalytics.
 * Exercises every public method against a temp directory and prints PASS/FAIL.
 * Intended for deploy verification — exits non-zero on any failure.
 */

if (file_exists('/docker/whp/web/libs/traffic_analytics.php')) {
    require_once('/docker/whp/web/libs/traffic_analytics.php');
} else {
    require_once(dirname(__DIR__) . '/libs/traffic_analytics.php');
}

$failures = 0;
$total = 0;

function assert_eq($expected, $actual, string $label): void
{
    global $failures, $total;
    $total++;
    if ($expected === $actual) {
        echo "PASS  $label\n";
    } else {
        $failures++;
        echo "FAIL  $label\n";
        echo "      expected: " . var_export($expected, true) . "\n";
        echo "      actual:   " . var_export($actual, true) . "\n";
    }
}

function assert_true(bool $cond, string $label): void
{
    global $failures, $total;
    $total++;
    if ($cond) {
        echo "PASS  $label\n";
    } else {
        $failures++;
        echo "FAIL  $label\n";
    }
}

$tmp = sys_get_temp_dir() . '/whp-ta-test-' . getmypid() . '-' . bin2hex(random_bytes(4));
mkdir($tmp, 0755, true);
$user = 'testuser';
mkdir($tmp . '/' . $user, 0755, true);

try {
    $ta = new TrafficAnalytics($tmp);

    // --- getOrCreateMonthDb: creates schema and the analytics dir
    $pdo = $ta->getOrCreateMonthDb($user, '2026-05');
    assert_true($pdo instanceof PDO, 'getOrCreateMonthDb returns PDO');
    assert_true(is_dir("$tmp/$user/whp-analytics"), 'whp-analytics dir created');
    assert_true(file_exists("$tmp/$user/whp-analytics/2026-05.sqlite3"), 'monthly sqlite file created');

    $tables = $pdo->query("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
        ->fetchAll(PDO::FETCH_COLUMN);
    assert_eq(['daily_top_bandwidth', 'daily_top_urls', 'daily_totals'], $tables, 'schema tables present');

    // --- recordDailySnapshot: write
    $ta->recordDailySnapshot(
        $user, 42, 'example.com', '2026-05-01',
        ['total_requests' => 1500, 'human_views' => 800, 'bytes_sent' => 5_000_000],
        [
            ['url' => '/',         'hits' => 400],
            ['url' => '/about',    'hits' => 120],
            ['url' => '/contact',  'hits' => 80],
        ],
        [
            ['url' => '/big.zip',  'bytes_total' => 3_000_000, 'hits' => 12],
            ['url' => '/img.jpg',  'bytes_total' => 800_000,   'hits' => 80],
        ]
    );

    // --- getDailyTotals
    $row = $ta->getDailyTotals($user, 42, '2026-05-01');
    assert_true($row !== null, 'getDailyTotals returns row');
    assert_eq(1500, (int)$row['total_requests'], 'daily totals: total_requests');
    assert_eq(800, (int)$row['human_views'], 'daily totals: human_views');
    assert_eq(5_000_000, (int)$row['bytes_sent'], 'daily totals: bytes_sent');
    assert_eq('example.com', $row['domain'], 'daily totals: domain');

    assert_eq(null, $ta->getDailyTotals($user, 42, '2026-05-02'), 'getDailyTotals returns null when missing');
    assert_eq(null, $ta->getDailyTotals($user, 99, '2026-05-01'), 'getDailyTotals returns null for unknown site');

    // --- getTopUrls / getTopBandwidth
    $tu = $ta->getTopUrls($user, 42, '2026-05-01');
    assert_eq(3, count($tu), 'getTopUrls row count');
    assert_eq('/', $tu[0]['url'], 'getTopUrls rank 1 url');
    assert_eq(400, (int)$tu[0]['hits'], 'getTopUrls rank 1 hits');

    $tb = $ta->getTopBandwidth($user, 42, '2026-05-01');
    assert_eq(2, count($tb), 'getTopBandwidth row count');
    assert_eq('/big.zip', $tb[0]['url'], 'getTopBandwidth rank 1 url');
    assert_eq(3_000_000, (int)$tb[0]['bytes_total'], 'getTopBandwidth rank 1 bytes');
    assert_eq(12, (int)$tb[0]['hits'], 'getTopBandwidth rank 1 hits');

    // --- INSERT OR REPLACE re-snapshot overwrites
    $ta->recordDailySnapshot(
        $user, 42, 'example.com', '2026-05-01',
        ['total_requests' => 9, 'human_views' => 5, 'bytes_sent' => 100],
        [['url' => '/only', 'hits' => 9]],
        [['url' => '/only', 'bytes_total' => 100, 'hits' => 9]]
    );
    $row = $ta->getDailyTotals($user, 42, '2026-05-01');
    assert_eq(9, (int)$row['total_requests'], 're-snapshot overwrites totals');
    $tu2 = $ta->getTopUrls($user, 42, '2026-05-01');
    assert_eq(1, count($tu2), 're-snapshot replaces top urls');
    assert_eq('/only', $tu2[0]['url'], 're-snapshot top url is new value');

    // --- top-N caps at 10
    $bigUrls = [];
    for ($i = 1; $i <= 25; $i++) {
        $bigUrls[] = ['url' => "/p$i", 'hits' => 100 - $i];
    }
    $ta->recordDailySnapshot(
        $user, 42, 'example.com', '2026-05-02',
        ['total_requests' => 1000, 'human_views' => 1000, 'bytes_sent' => 0],
        $bigUrls,
        []
    );
    $tu3 = $ta->getTopUrls($user, 42, '2026-05-02');
    assert_eq(10, count($tu3), 'top urls capped at 10');
    assert_eq('/p1', $tu3[0]['url'], 'top urls first row preserved');

    // --- getMonthSummary: returns rows for the queried site only, ASC by date
    $ta->recordDailySnapshot(
        $user, 99, 'other.com', '2026-05-03',
        ['total_requests' => 50, 'human_views' => 50, 'bytes_sent' => 0],
        [], []
    );
    $summary = $ta->getMonthSummary($user, 42, '2026-05');
    assert_eq(2, count($summary), 'month summary: only the queried site rows');
    assert_eq('2026-05-01', $summary[0]['date'], 'month summary: ordered ASC');
    assert_eq('2026-05-02', $summary[1]['date'], 'month summary: ordered ASC #2');

    // --- listAvailableMonths: descending, capped to 12
    $ta->getOrCreateMonthDb($user, '2026-04');
    $ta->getOrCreateMonthDb($user, '2026-03');
    $months = $ta->listAvailableMonths($user);
    assert_eq(['2026-05', '2026-04', '2026-03'], $months, 'listAvailableMonths sorts desc');

    // --- getRecentDailyTotals: across two month files
    $ta->recordDailySnapshot(
        $user, 42, 'example.com', '2026-04-30',
        ['total_requests' => 7, 'human_views' => 4, 'bytes_sent' => 0],
        [], []
    );
    $todayStr = (new DateTimeImmutable('today'))->format('Y-m-d');
    $ta->recordDailySnapshot(
        $user, 42, 'example.com', $todayStr,
        ['total_requests' => 11, 'human_views' => 9, 'bytes_sent' => 0],
        [], []
    );
    $recent = $ta->getRecentDailyTotals($user, 42, 60);
    assert_true(count($recent) >= 3, 'recent daily totals spans both months');
    $dates = array_column($recent, 'date');
    $sortedDates = $dates; sort($sortedDates);
    assert_eq($sortedDates, $dates, 'recent daily totals ordered ASC');

    // --- empty / missing-month behavior
    assert_eq([], $ta->getMonthSummary($user, 42, '2025-01'), 'getMonthSummary missing month returns []');
    assert_eq([], $ta->getTopUrls($user, 42, '2025-01-15'), 'getTopUrls missing month returns []');
    assert_eq([], $ta->getTopBandwidth($user, 42, '2025-01-15'), 'getTopBandwidth missing month returns []');
    assert_eq(null, $ta->getDailyTotals($user, 42, '2025-01-15'), 'getDailyTotals missing month returns null');

    // --- input validation
    $caught = false;
    try { $ta->getOrCreateMonthDb($user, '2026-13'); } catch (InvalidArgumentException $e) { $caught = true; }
    assert_true($caught, 'invalid year-month raises');

    $caught = false;
    try { $ta->getDailyTotals($user, 1, '2026-02-30'); } catch (InvalidArgumentException $e) { $caught = true; }
    assert_true($caught, 'invalid date raises');

    $caught = false;
    try { $ta->listAvailableMonths('../escape'); } catch (InvalidArgumentException $e) { $caught = true; }
    assert_true($caught, 'invalid username raises');

} finally {
    // cleanup
    if (is_dir($tmp)) {
        $rii = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($tmp, FilesystemIterator::SKIP_DOTS),
            RecursiveIteratorIterator::CHILD_FIRST
        );
        foreach ($rii as $f) {
            $f->isDir() ? @rmdir($f->getPathname()) : @unlink($f->getPathname());
        }
        @rmdir($tmp);
    }
}

echo "\n";
echo "Ran $total assertions; $failures failed.\n";
exit($failures === 0 ? 0 : 1);
