#!/usr/bin/env lua
-- run_conf10.lua — replay the draft-zambo-aer1-10 corpus against
-- aer1verify.lua, mirroring aer-1/conformance.py's traversal and verdict
-- logic vector for vector.
--
--   lua run_conf10.lua [/path/to/aer-1]   # exit 0 iff CONFORMANCE OK
--
-- Written by ARION, an autonomous agent.

local DIR = arg and arg[0] and arg[0]:match("^(.*)/[^/]*$") or "."
local AER = (arg and arg[1]) or "/workspace/research/zambo-aer1/aer-1"
package.path = DIR .. "/?.lua;" .. package.path
local V = require("aer1verify")

local pass, fail = 0, 0
local problems = {}
local function verdict(corpus, name, ok, detail)
    if ok then
        pass = pass + 1
        io.write(string.format("[%-11s] %s: PASS\n", corpus, name))
    else
        fail = fail + 1
        problems[#problems + 1] = corpus .. " " .. name
        io.write(string.format("[%-11s] %s: FAIL %s\n", corpus, name, detail or ""))
    end
end
local function reasons(failures)
    local t = {}
    for _, f in ipairs(failures) do t[#t + 1] = f.reason end
    return table.concat(t, "; ")
end
local OPT = { kind = "auto", strict = false, profile = false, anchor = false }
local function load(f)
    local h = assert(io.open(f, "rb"))
    local t = h:read("*a"); h:close()
    return V.parseJson(t)
end
local function globDir(d)
    local t = {}
    local p = io.popen('ls "' .. d .. '"/*.json 2>/dev/null')
    for l in p:lines() do t[#t + 1] = l end
    p:close()
    table.sort(t)
    return t
end
local function base(f) return f:match("[^/]*$") end
local function jsonStr(s)
    local r = s:gsub("\\", "\\\\"):gsub('"', '\\"')
    r = r:gsub("[%c]", function(c) return string.format("\\u%04x", c:byte()) end)
    return '"' .. r .. '"'
end
local function countCheck(failures, c)
    local n = 0
    for _, f in ipairs(failures) do if f.check == c then n = n + 1 end end
    return n
end

-- valid/
for _, f in ipairs(globDir(AER .. "/test-vectors/valid")) do
    local _, fails = V.verifyDocument(load(f), OPT)
    verdict("valid", base(f), #fails == 0, reasons(fails))
end
-- invalid/
for _, f in ipairs(globDir(AER .. "/test-vectors/invalid")) do
    local _, fails = V.verifyDocument(load(f), OPT)
    verdict("invalid", base(f), #fails > 0, "accepted!")
end
-- invalid-profile/
for _, f in ipairs(globDir(AER .. "/test-vectors/invalid-profile")) do
    local doc = load(f)
    local _, core = V.verifyDocument(doc, OPT)
    local o2 = { kind = "auto", strict = false, profile = true, anchor = false }
    local _, prof = V.verifyDocument(doc, o2)
    local ok = #core == 0 and countCheck(prof, "reference-profile") > 0
    verdict("invalid-profile", base(f), ok,
            #core > 0 and ("core: " .. reasons(core)) or "profile accepted")
end
-- anchored/
for _, f in ipairs(globDir(AER .. "/test-vectors/anchored")) do
    local doc = load(f)
    local o2 = { kind = "auto", strict = false, profile = false, anchor = true }
    local _, fails = V.verifyDocument(doc, o2)
    local expectPass = base(f) == "anchored-valid.json"
    local tierN = countCheck(fails, "disinterested-tier")
    local ok = countCheck(fails, "core") == 0 and
               (expectPass and tierN == 0 or tierN > 0)
    verdict("anchored", base(f), ok, reasons(fails))
end
-- chain-vectors.json (v06)
for _, v in ipairs(load(AER .. "/chain-vectors.json").vectors) do
    local tl = v.timeline or v.entries
    local _, fails = V.verifyDocument(tl, { kind = "chain06", strict = false })
    verdict("chain06", v.name, (#fails == 0) == (v.expected_verdict == "valid"),
            reasons(fails))
end
-- chain-vectors-v07.json
for _, v in ipairs(load(AER .. "/chain-vectors-v07.json").vectors) do
    local tl = v.timeline or v.entries
    local _, fails = V.verifyDocument(tl, { kind = "chain", strict = false })
    verdict("chain-v07", v.name, (#fails == 0) == (v.expected_verdict == "valid"),
            reasons(fails))
end
-- commitment-vectors.json
for _, v in ipairs(load(AER .. "/commitment-vectors.json").vectors) do
    local fails = V.verifyChainCommitment(v.timeline, v.commitment, OPT)
    verdict("commitment", v.name, (#fails == 0) == (v.expected_verdict == "valid"),
            table.concat(fails, "; "))
end
-- merkle-vectors.json
local function hexDec(h) return (h:gsub("..", function(x) return string.char(tonumber(x, 16)) end)) end
local function hexEnc(b) return (b:gsub(".", function(c) return string.format("%02x", c:byte()) end)) end
for _, v in ipairs(load(AER .. "/merkle-vectors.json").vectors) do
    if v.receipt_ids then
        local got = V.merkleRoot(v.receipt_ids)
        verdict("merkle", v.name, got == v.expected_root, "got " .. tostring(got))
    elseif v.leaves then
        local leaves = {}
        for _, l in ipairs(v.leaves) do leaves[#leaves + 1] = l end
        table.sort(leaves, function(a, b) return a.seq < b.seq end)
        local lvl = {}
        for _, l in ipairs(leaves) do
            lvl[#lvl + 1] = hexDec(V.sha256hex(l.preimage))
        end
        while #lvl > 1 do
            if #lvl % 2 == 1 then lvl[#lvl + 1] = lvl[#lvl] end
            local nxt = {}
            for i = 1, #lvl, 2 do
                nxt[#nxt + 1] = hexDec(V.sha256hex(lvl[i] .. lvl[i + 1]))
            end
            lvl = nxt
        end
        local got = #lvl == 0 and V.sha256hex("") or hexEnc(lvl[1])
        local ok = got == v.expected_root and got ~= v.must_not_equal
        verdict("merkle-legacy", v.name, ok, "got " .. tostring(got))
    end
    if v.expected_workflow_verdict then
        if not v.receipt_ids then
            verdict("merkle-wf", v.name, false, "no receipt_ids")
        else
            -- Build the workflow as JSON text and re-parse it so the
            -- document carries parseJson's object/array metatables.
            local stepJs = {}
            for i, rid in ipairs(v.receipt_ids) do
                stepJs[i] = string.format(
                    '{"seq":%d,"tool":"test-tool","receipt_id":%s,'
                    .. '"receipt_hash":"%s","started_at":"2026-01-01T00:00:00Z",'
                    .. '"ended_at":"2026-01-01T00:00:01Z","status":"ok"}',
                    i, jsonStr(rid), string.rep("a", 64))
            end
            local wfJson = string.format(
                '{"type":"verifiable-workflow-receipt","version":"1",'
                .. '"workflow_id":"11111111-1111-1111-1111-111111111111",'
                .. '"receipt_id":"22222222-2222-2222-2222-222222222222",'
                .. '"session_id":"test-session","goal":"test workflow",'
                .. '"status":"ok","steps":[%s],'
                .. '"merkle_root":%s,"output_hash":"%s",'
                .. '"verify_url":"https://example.com/workflow/test"}',
                table.concat(stepJs, ","),
                jsonStr(v.expected_root), string.rep("b", 64))
            local wf = V.parseJson(wfJson)
            local wfFails = V.checkWorkflow(wf, OPT)
            local gv = #wfFails == 0 and "valid" or "invalid"
            local ok = gv == v.expected_workflow_verdict
            if ok and gv == "invalid" and v.rejected_by then
                ok = false
                for _, f in ipairs(wfFails) do
                    if f:find(v.rejected_by, 1, true) then ok = true end
                end
            end
            verdict("merkle-wf", v.name, ok, "verdict " .. gv)
        end
    end
end

io.write(string.format("\n== SUMMARY ==  %d passed, %d failed\n", pass, fail))
for _, p in ipairs(problems) do io.write(" - " .. p .. "\n") end
if fail > 0 then os.exit(1) end
io.write("CONFORMANCE OK (-10 corpus): all vectors behave as expected.\n")
