// Differential corpus oracle: minimized inputs where the markdown component
// has diverged from the cmark 0.31.2 reference implementation, with cmark's
// output as the expected HTML. Fixture provenance:
// commonmark-differential-corpus.txt, generated by
// tools/freeze-markdown-divergences.py from a tools/fuzz-markdown-vs-cmark.py
// run (cmark 0.31.2, `--unsafe`).
//
// This corpus was the acceptance gate for the delimiter-stack rewrite of
// components/text/markdown/lib/commonmark.zig and has been green since that
// rewrite landed; it runs in test-comply against both markdown components.
// Keep growing the fixture from future fuzz runs (tools/fuzz-markdown-vs-cmark.py
// then tools/freeze-markdown-divergences.py) as further divergences are fixed.
//
// Same fence/separator format as commonmark-spec-0.31.2.txt, but examples are
// embedded verbatim: tabs are literal bytes, so there is no arrow
// normalization, and the case count is derived from the fixture rather than
// pinned.
const std = @import("std");

extern "qip" fn must_render_exactly(
    ordinal: u64,
    input_ptr: u32,
    input_len: u32,
    expected_ptr: u32,
    expected_len: u32,
) i32;

const CORPUS_TEXT = @embedFile("commonmark-differential-corpus.txt");
const OPEN_LINE = ("`" ** 32) ++ " example\n";
const SEPARATOR = "\n.\n";
const EMPTY_SEPARATOR = ".\n";
const CLOSE_LINE = ("`" ** 32) ++ "\n";

const Segment = struct { start: usize, len: usize };
const Case = struct { input: Segment, expected: Segment };

fn countExamples(comptime text: []const u8) usize {
    @setEvalBranchQuota(1_000_000);
    var count: usize = 0;
    var cursor: usize = 0;
    while (std.mem.indexOfPos(u8, text, cursor, OPEN_LINE)) |open_start| {
        count += 1;
        cursor = open_start + OPEN_LINE.len;
    }
    return count;
}

const CASE_COUNT = countExamples(CORPUS_TEXT);

fn parseCorpus(comptime text: []const u8) [CASE_COUNT]Case {
    @setEvalBranchQuota(1_000_000);
    var cases: [CASE_COUNT]Case = undefined;
    var cursor: usize = 0;
    var count: usize = 0;
    while (std.mem.indexOfPos(u8, text, cursor, OPEN_LINE)) |open_start| {
        const input_start = open_start + OPEN_LINE.len;
        const input_end, const expected_start = if (std.mem.startsWith(u8, text[input_start..], EMPTY_SEPARATOR))
            .{ input_start, input_start + EMPTY_SEPARATOR.len }
        else blk: {
            const separator_start = std.mem.indexOfPos(u8, text, input_start, SEPARATOR) orelse
                @compileError("commonmark-differential-corpus.txt example has no separator");
            break :blk .{ separator_start + 1, separator_start + SEPARATOR.len };
        };
        const close_start = std.mem.indexOfPos(u8, text, expected_start, CLOSE_LINE) orelse
            @compileError("commonmark-differential-corpus.txt example has no closing fence");
        cases[count] = .{
            .input = .{ .start = input_start, .len = input_end - input_start },
            .expected = .{ .start = expected_start, .len = close_start - expected_start },
        };
        count += 1;
        cursor = close_start + CLOSE_LINE.len;
    }
    return cases;
}

const CASES = parseCorpus(CORPUS_TEXT);

export fn comply() i32 {
    inline for (CASES, 0..) |case, ordinal| {
        const input = CORPUS_TEXT[case.input.start..][0..case.input.len];
        const expected = CORPUS_TEXT[case.expected.start..][0..case.expected.len];
        _ = must_render_exactly(
            ordinal,
            @intCast(@intFromPtr(input.ptr)),
            input.len,
            @intCast(@intFromPtr(expected.ptr)),
            expected.len,
        );
    }
    return CASE_COUNT;
}
