<?php
/**
 * Contract test: every key AgentBrain::formatResult() reads must actually be
 * returned by the corresponding action.
 *
 * A mismatch here is silent in production — the UI reports zero while the
 * action worked (this is exactly how send_weekly_feeding_summary reported
 * "sent to 0 parents" on every successful run).
 *
 * Static check: greps each action for the literal keys it returns. No AI calls,
 * no SMS, no DB writes — safe to run anywhere.
 *
 * Usage:  php ai/test_result_contract.php
 */
declare(strict_types=1);

require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/agent_brain.php';

/** action type => file implementing it */
$files = [
    'send_fee_reminders'               => 'send_fee_reminders.php',
    'generate_terminal_reports'        => 'generate_terminal_reports.php',
    'generate_exam_paper'              => 'generate_exam_paper.php',
    'detect_at_risk_students'          => 'detect_at_risk_students.php',
    'financial_snapshot'               => 'financial_snapshot.php',
    'bulk_notify_parents'              => 'bulk_notify_parents.php',
    'batch_generate_report_pdfs'       => 'batch_generate_report_pdfs.php',
    'create_parent_accounts_bulk'      => 'create_parent_accounts_bulk.php',
    'check_timetable_conflicts'        => 'check_timetable_conflicts.php',
    'send_weekly_feeding_summary'      => 'send_weekly_feeding_summary.php',
    'bulk_create_canteen_entitlements' => 'bulk_create_canteen_entitlements.php',
    'canteen_daily_report'             => 'canteen_daily_report.php',
    'cancel_canteen_session'           => 'cancel_canteen_session.php',
    'send_canteen_reminder_sms'        => 'send_canteen_reminder_sms.php',
];

$contract = AgentBrain::resultKeyContract();
$failures = 0;
$checked  = 0;

echo "AgentBrain result-key contract", PHP_EOL;
echo str_repeat('-', 68), PHP_EOL;

foreach ($contract as $type => $keys) {
    $rel  = $files[$type] ?? null;
    $path = $rel ? __DIR__ . '/actions/' . $rel : null;

    if (!$path || !is_file($path)) {
        printf("  %-34s NO ACTION FILE\n", $type);
        $failures++;
        continue;
    }

    $src     = (string) file_get_contents($path);
    $missing = [];
    foreach ($keys as $k) {
        // The key must appear as an array key being returned: 'key' =>
        if (!preg_match("/['\"]" . preg_quote($k, '/') . "['\"]\s*=>/", $src)) {
            $missing[] = $k;
        }
    }

    $checked++;
    if ($missing) {
        printf("  %-34s MISSING: %s\n", $type, implode(', ', $missing));
        $failures++;
    } else {
        printf("  %-34s ok (%d keys)\n", $type, count($keys));
    }
}

// Every action wired into the streaming endpoint should have a formatter entry,
// otherwise its result falls through to "Completed successfully".
$stream = (string) @file_get_contents(__DIR__ . '/../ajax/ai_agent_stream.php');
preg_match_all("/'([a-z_]+)'\s*=>\s*'action_[a-z_]+'/", $stream, $m);
$wired    = array_unique($m[1] ?? []);
$unformat = array_diff($wired, array_keys($contract));

echo str_repeat('-', 68), PHP_EOL;
if ($unformat) {
    echo "  Actions wired in ai_agent_stream.php with no formatter entry:", PHP_EOL;
    foreach ($unformat as $u) {
        echo "    - {$u}", PHP_EOL;
        $failures++;
    }
} else {
    printf("  all %d wired actions have a formatter entry\n", count($wired));
}

echo str_repeat('-', 68), PHP_EOL;
echo $failures === 0
    ? "PASS — {$checked} action(s) satisfy the contract.\n"
    : "FAIL — {$failures} problem(s) found.\n";

exit($failures === 0 ? 0 : 1);
