#!/bin/sh
set -eu

: "${PRE15_DUT:?PRE15_DUT is required}"
: "${PRE15_ROOT:?PRE15_ROOT is required}"
: "${PRE15_RUN_DIR:?PRE15_RUN_DIR is required}"
: "${PRE15_LIB_DIR:?PRE15_LIB_DIR is required}"
. "$PRE15_LIB_DIR/runner.sh"

gate=$PRE15_DUT/tests/pre15/gates/P15-006.sh
input=$PRE15_DUT/tests/pre15/gates/P15-006-input.json
oracle=$PRE15_DUT/tests/pre15/fixtures/B07-map-oracle.json
artifacts=$PRE15_RUN_DIR/artifacts

for tool in cc python3 sha256sum; do
	command -v "$tool" >/dev/null 2>&1 || \
		pre15_infra_blocked "missing B07a host tool: $tool"
done

pre15_record_fixture b07a-gate "$gate"
pre15_record_fixture b07a-input "$input"
pre15_record_fixture b07a-oracle "$oracle"
for source in internal.h data.c zmap.c zdata.c super.c; do
	pre15_record_fixture "b07a-$source" "$PRE15_DUT/src/$source"
done

mkdir -p "$artifacts"
pre15_target_reached

if "$gate" --worktree --oracle "$oracle" --output "$artifacts/gate" \
	>"$artifacts/gate.stdout" 2>"$artifacts/gate.stderr"; then
	:
else
	pre15_dut_fail 'B07a P15-006 candidate replay failed'
fi

if python3 - "$PRE15_DUT" "$oracle" "$artifacts/gate" \
	"$artifacts/adapter-check.json" <<'PY'
from __future__ import annotations

import json
from pathlib import Path
import re
import sys


dut = Path(sys.argv[1])
oracle = json.loads(Path(sys.argv[2]).read_text(encoding="ascii"))
gate = Path(sys.argv[3])
output = Path(sys.argv[4])
result = json.loads((gate / "result.json").read_text(encoding="ascii"))
tuples = json.loads((gate / "tuples.json").read_text(encoding="ascii"))
devices = json.loads((gate / "devices.json").read_text(encoding="ascii"))

if result["status"] != "GO" or result["failures"]:
    raise SystemExit(f"P15-006 candidate did not GO: {result!r}")
if not result["adapter_mode"] or result["oracle_equal"] is not True:
    raise SystemExit("candidate did not use the common map adapter and frozen oracle")
if result["map_case_count"] != 80 or result["device_case_count"] != 13:
    raise SystemExit("P15-006 corpus denominator changed")
if result["model_sha256"] != oracle["model_sha256"]:
    raise SystemExit("independent model digest changed")
if result["tuple_bytes_sha256"] != oracle["tuple_bytes_sha256"]:
    raise SystemExit("tuple byte stream changed")
if result["adapter_probe"]["status"] != "PASS":
    raise SystemExit("FULL/COMPACT adapter pass-through probe failed")
if not result["adapter_probe"]["full_and_compact_dispatch"]:
    raise SystemExit("compressed layout dispatch is incomplete")
if result["adapter_probe"]["flag_mask"] != 0x1F:
    raise SystemExit("not all map flag bits passed through the adapter")
if tuples["tuple_bytes_sha256"] != oracle["tuple_bytes_sha256"]:
    raise SystemExit("tuple artifact does not match frozen bytes")
if devices["status"] != "PASS" or len(devices["records"]) != 13:
    raise SystemExit("device/GEOM resolution oracle failed")


def extract_function(source: str, name: str) -> str:
    match = re.search(r"^" + re.escape(name) + r"\s*\(", source, re.MULTILINE)
    if not match:
        raise SystemExit(f"missing function: {name}")
    start = source.rfind("\n\n", 0, match.start()) + 2
    brace = source.find("{", match.end())
    depth = 0
    state = "code"
    index = brace
    while index < len(source):
        char = source[index]
        following = source[index + 1] if index + 1 < len(source) else ""
        if state == "code":
            if char == "/" and following == "*":
                state = "block"
                index += 2
                continue
            if char == "/" and following == "/":
                state = "line"
                index += 2
                continue
            if char == '"':
                state = "string"
            elif char == "'":
                state = "character"
            elif char == "{":
                depth += 1
            elif char == "}":
                depth -= 1
                if depth == 0:
                    return source[start : index + 1]
        elif state == "block" and char == "*" and following == "/":
            state = "code"
            index += 2
            continue
        elif state == "line" and char == "\n":
            state = "code"
        elif state in {"string", "character"}:
            if char == "\\":
                index += 2
                continue
            if (state == "string" and char == '"') or (
                state == "character" and char == "'"
            ):
                state = "code"
        index += 1
    raise SystemExit(f"unterminated function: {name}")


internal = (dut / "src/internal.h").read_text(encoding="utf-8")
data = (dut / "src/data.c").read_text(encoding="utf-8")
zmap = (dut / "src/zmap.c").read_text(encoding="utf-8")
zdata = (dut / "src/zdata.c").read_text(encoding="utf-8")
super_source = (dut / "src/super.c").read_text(encoding="utf-8")

object_prototype = re.search(
    r"int erofs_map_blocks\s*\(struct erofs_sb_info \*sbi,\s*"
    r"struct erofs_inode \*vi,\s*struct erofs_map_blocks \*map\s*\);",
    internal,
    re.MULTILINE,
)
if object_prototype is None:
    raise SystemExit("common map object prototype is absent")
if re.search(r"erofs_map_blocks\s*\([^;]*phys_off", internal, re.DOTALL):
    raise SystemExit("legacy scatter prototype remains exported")
if "static int\nerofs_map_blocks_legacy(" not in data:
    raise SystemExit("legacy producer target is not file-local")
if len(re.findall(r"\berofs_map_blocks_legacy\s*\(", data)) != 4:
    raise SystemExit("B07a must retain exactly two legacy data consumers")
if len(re.findall(r"\berofs_map_blocks\s*\(", data)) != 1:
    raise SystemExit("B07a common map entry has an unexpected data.c caller")

adapter = extract_function(data, "erofs_map_blocks")
required_adapter = [
    "struct erofs_map_blocks next = { .m_la = map->m_la };",
    "z_erofs_map_blocks_iter(sbi, vi, &next, 0);",
    "next.m_deviceid = device_id;",
    "next.m_flags |= EROFS_MAP_MAPPED;",
    "next.m_flags |= EROFS_MAP_META;",
    "*map = next;",
]
for marker in required_adapter:
    if marker not in adapter:
        raise SystemExit(f"adapter contract marker is absent: {marker}")

for function in ("erofs_read_data", "erofs_read_uio"):
    body = extract_function(data, function)
    if "erofs_map_blocks_legacy(" not in body or "erofs_map_blocks(" in body:
        raise SystemExit(f"B07a migrated consumer early: {function}")
for source_name, source in (("zdata.c", zdata), ("super.c", super_source)):
    if "z_erofs_map_blocks_iter(" not in source or "erofs_map_blocks(" in source:
        raise SystemExit(f"B07a migrated compressed consumer early: {source_name}")
if "map adapter must preserve every map flag" not in zmap:
    raise SystemExit("compressed map flag contract assertion is absent")

summary = {
    "status": "PASS",
    "map_cases": result["map_case_count"],
    "device_cases": result["device_case_count"],
    "tuple_bytes_sha256": result["tuple_bytes_sha256"],
    "model_sha256": result["model_sha256"],
    "adapter_probe": result["adapter_probe"],
    "legacy_data_consumers": 2,
    "compressed_consumers_migrated": False,
    "qemu": result["qemu"],
    "full_feature_suite": result["full_feature_suite"],
}
with output.open("x", encoding="ascii") as stream:
    json.dump(summary, stream, ensure_ascii=True, indent=2, sort_keys=True)
    stream.write("\n")
print("B07a PASS map=80 device=13 tuple-bytes exact flags=0x1f")
PY
then
	:
else
	pre15_dut_fail 'B07a adapter boundary check failed'
fi

sha256sum "$artifacts/gate/result.json" "$artifacts/gate/tuples.json" \
	"$artifacts/gate/devices.json" "$artifacts/adapter-check.json" \
	>"$artifacts/SHA256SUMS"
printf 'B07a PASS exact map adapter; producers and consumers remain staged\n'
