#!/usr/bin/env php
<?php
/*
 * ARION AER-1 negative must-fail corpus — conformance runner (PHP port).
 *
 * Port of ../neg-corpus/run.js: replays manifest.json through
 * aer1verify.php's verifyDocument() and asserts each case produces
 * EXACTLY the pinned {check, reason} failure set — every expected failure
 * must be observed, and no unlisted failure may appear.
 *
 *   php run_negcorpus.php [/path/to/neg-corpus]   # exit 0 iff all conform
 *
 * Written by ARION, an autonomous agent.
 */

$DIR = $argv[1] ?? (dirname(__DIR__) . "/neg-corpus");
require __DIR__ . "/aer1verify.php";

$manifest = json_decode(file_get_contents($DIR . "/manifest.json"));

$pass = 0;
$fail = 0;

function runDoc($doc, $flags) {
    $opts = ["kind" => "auto", "strict" => false, "profile" => false,
             "anchor" => false];
    foreach (get_object_vars((object)$flags) as $k => $v) $opts[$k] = $v;
    return verifyDocument($doc, $opts)["failures"];
}

function diffSets($expect, $observed) {
    $e = array_map(fn($x) => $x->check . " :: " . $x->reason, $expect);
    $o = array_map(fn($x) => $x["check"] . " :: " . $x["reason"], $observed);
    return ["missing" => array_values(array_diff($e, $o)),
            "extra"   => array_values(array_diff($o, $e))];
}

foreach ($manifest->cases as $c) {
    $doc = json_decode(file_get_contents($DIR . "/" . $c->file));
    $observed = runDoc($doc, $c->flags ?? new stdClass);
    $d = diffSets($c->expect, $observed);
    $ok = !count($d["missing"]) && !count($d["extra"]);
    if ($ok) { $pass++; echo "PASS " . $c->name . "\n"; }
    else {
        $fail++;
        echo "FAIL " . $c->name .
            (count($d["missing"]) ? " missing=" . json_encode($d["missing"], JSON_UNESCAPED_SLASHES) : "") .
            (count($d["extra"])   ? " extra="   . json_encode($d["extra"], JSON_UNESCAPED_SLASHES)   : "") . "\n";
    }
}

foreach ($manifest->differentials as $d) {
    $doc = json_decode(file_get_contents($DIR . "/" . $d->file));
    $loose = runDoc($doc, new stdClass);
    $strict = $d->strict_mode === "chain06"
        ? runDoc($doc, (object)["kind" => "chain06"])
        : runDoc($doc, (object)["strict" => true]);
    $dl = diffSets($d->expect_loose, $loose);
    $ds = diffSets($d->expect_strict, $strict);
    $ok = !count($dl["missing"]) && !count($dl["extra"]) &&
          !count($ds["missing"]) && !count($ds["extra"]);
    if ($ok) { $pass++; echo "PASS " . $d->name . " (differential)\n"; }
    else {
        $fail++;
        echo "FAIL " . $d->name . " (differential)" .
            " looseDiff=" . json_encode($dl, JSON_UNESCAPED_SLASHES) .
            " strictDiff=" . json_encode($ds, JSON_UNESCAPED_SLASHES) . "\n";
    }
}

echo "\n" . $pass . "/" . ($pass + $fail) . " corpus cases conform\n";
exit($fail ? 1 : 0);
