const std = @import("std");

/// The repo's ONE version, read from `pyproject.toml` at configure time.
///
/// M-B B2 — the broker's wire hello reported a hardcoded `"0.1.0"` while
/// the project shipped 0.1.9. Nobody parsed it (verified by grep across
/// mgf-hrb + hrb-app before the change), which is exactly why it drifted
/// nine releases without anyone noticing — and exactly why replacing it
/// with ANOTHER literal would only reset the drift clock. Reading the
/// packaging metadata makes it structurally unable to go stale: there is
/// one version in the repo and this is it.
///
/// The fallback is deliberately not a plausible-looking version. If the
/// file cannot be read (an unusual build layout), the wire says
/// `0.0.0+unknown`, which an operator can act on. A confident wrong
/// number is the failure mode being removed.
fn projectVersion(b: *std.Build) []const u8 {
    const unknown = "0.0.0+unknown";
    const bytes = b.build_root.handle.readFileAlloc(
        b.graph.io,
        "../pyproject.toml",
        b.allocator,
        .limited(1 << 20),
    ) catch return unknown;
    const tbl = std.mem.indexOf(u8, bytes, "[project]") orelse return unknown;
    const needle = "\nversion = \"";
    const at = std.mem.indexOfPos(u8, bytes, tbl, needle) orelse return unknown;
    const start = at + needle.len;
    const end = std.mem.indexOfScalarPos(u8, bytes, start, '"') orelse return unknown;
    if (end <= start) return unknown;
    return bytes[start..end];
}

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // M-B B2 — compile-time facts the binary reports about itself.
    const build_opts = b.addOptions();
    build_opts.addOption([]const u8, "broker_version", projectVersion(b));
    const build_options = build_opts.createModule();

    // ───────────────────────────────────────────────────────────────
    // E1 — in-repo package modules. Each INDEX.md sibling becomes a
    // named module whose `.imports` list IS its enforced dependency
    // boundary: a file rooted in `mgf_hrb_model` literally cannot
    // `@import` anything (the module has no imports), so the clean DAG
    // is now a compile-time guarantee, not a convention. Modules are
    // added bottom-up; `@import("model.zig")` → `@import("mgf_hrb_model")`
    // at every call site. This is the in-repo step (E1); carving these
    // into their own repos (E2) is a near-mechanical follow-on, deferred.
    //
    // `pkg_mods` is wired into every executable + test target below so
    // any file (still-path-imported or module-rooted) can resolve the
    // package names. Unused imports are harmless; a module's OWN
    // `.imports` (set at creation) is where the boundary is enforced.
    // ───────────────────────────────────────────────────────────────
    // E2: `model` is carved into its own repo (mgf-hrb-model) and consumed
    // as a package dependency rather than a local file. The rest of the
    // module graph below is unchanged — `model` is still just a Module.
    const model = b.dependency("mgf_hrb_model", .{
        .target = target,
        .optimize = optimize,
    }).module("mgf_hrb_model");
    // kabi seam + reference fake_backend — carved to mgf-hrb-kabi (E2),
    // consumed as a package. The kabi package transitively pins
    // mgf-hrb-model (same commit/hash this broker pins), so zig dedups.
    const kabi_dep = b.dependency("mgf_hrb_kabi", .{
        .target = target,
        .optimize = optimize,
    });
    const kabi = kabi_dep.module("mgf_hrb_kabi");
    const fake_backend = kabi_dep.module("mgf_hrb_fake_backend");

    // rt — ring/fdpass/liveness, carved to mgf-hrb-rt (E2). One repo,
    // three modules (each a single root file). Deps model (transitive).
    const rt_dep = b.dependency("mgf_hrb_rt", .{
        .target = target,
        .optimize = optimize,
    });
    const ring = rt_dep.module("mgf_hrb_ring");
    const liveness = rt_dep.module("mgf_hrb_liveness");
    const fdpass = rt_dep.module("mgf_hrb_fdpass");
    const json_escape = b.createModule(.{ .root_source_file = b.path("src/json_escape.zig"), .target = target, .optimize = optimize });
    // fairness — anti-starvation contention policy (Tier-1 #10). Pure state over
    // model URNs; consumed by core's claim/release seams.
    const fairness = b.createModule(.{ .root_source_file = b.path("src/fairness.zig"), .target = target, .optimize = optimize });
    fairness.addImport("mgf_hrb_model", model);
    // audit + admission depend only on the kabi seam.
    const audit = b.createModule(.{ .root_source_file = b.path("src/audit.zig"), .target = target, .optimize = optimize });
    audit.addImport("mgf_hrb_kabi", kabi);
    const admission = b.createModule(.{ .root_source_file = b.path("src/admission.zig"), .target = target, .optimize = optimize });
    admission.addImport("mgf_hrb_kabi", kabi);
    // shm — per-tenant memfd-backed region pool (Part 5.3 data plane).
    // Imports the rt ring so the background per-channel drain thread (slice 4)
    // can host the SPSC consumer (`HostedRing.popUntrustedProducer`) directly.
    const shm = b.createModule(.{ .root_source_file = b.path("src/shm.zig"), .target = target, .optimize = optimize });
    shm.addImport("mgf_hrb_kabi", kabi);
    shm.addImport("mgf_hrb_ring", ring);
    // irq_monitor — interrupt-rate containment policy (D7, Tier-2). Pure
    // history+policy over model URNs; consumed by the facade's scan.
    const irq_monitor = b.createModule(.{ .root_source_file = b.path("src/irq_monitor.zig"), .target = target, .optimize = optimize });
    irq_monitor.addImport("mgf_hrb_model", model);
    irq_monitor.addImport("mgf_hrb_kabi", kabi);
    // linux_irq — the real /proc/interrupts + sysfs counter source for D7.
    const linux_irq = b.createModule(.{ .root_source_file = b.path("src/linux_irq.zig"), .target = target, .optimize = optimize });
    linux_irq.addImport("mgf_hrb_model", model);
    linux_irq.addImport("mgf_hrb_kabi", kabi);
    linux_irq.addImport("mgf_hrb_irq_monitor", irq_monitor);
    // linux_bound — "bound at hardware" classification for restart reconcile
    // precision (R-T1-8 residual b). Pure-ish sysfs reader over model URNs.
    const linux_bound = b.createModule(.{ .root_source_file = b.path("src/linux_bound.zig"), .target = target, .optimize = optimize });
    linux_bound.addImport("mgf_hrb_model", model);
    // restart_marker — broker self-restart detection (R-T1-8 residual a). std-only.
    const restart_marker = b.createModule(.{ .root_source_file = b.path("src/restart_marker.zig"), .target = target, .optimize = optimize });

    // cap — the capability engine, carved to mgf-hrb-cap (E2). Deps
    // model + kabi (transitively pinned at the same commits).
    const cap = b.dependency("mgf_hrb_cap", .{
        .target = target,
        .optimize = optimize,
    }).module("mgf_hrb_cap");
    // identity — the MODEL-01 comparator, carved to mgf-common-identity
    // (E2, federation-level). Module name stays mgf_hrb_identity for the
    // current consumers; deps model + kabi (transitive).
    const identity = b.dependency("mgf_common_identity", .{
        .target = target,
        .optimize = optimize,
    }).module("mgf_hrb_identity");
    // subscription depends on model + kabi only.
    const subscription = b.createModule(.{ .root_source_file = b.path("src/subscription.zig"), .target = target, .optimize = optimize });
    subscription.addImport("mgf_hrb_model", model);
    subscription.addImport("mgf_hrb_kabi", kabi);
    // F-B2-02 — the hidden-subscription table is sized by the
    // concurrent-connection cap, which admission owns. admission depends
    // only on kabi, so this edge adds no cycle.
    subscription.addImport("mgf_hrb_admission", admission);
    // event_pump — M-B B2. The broker's own backend-event drainer: one
    // thread, poll over every worker's watch fd, straight into the
    // subscription fan-out. Deps model + kabi + subscription ONLY — it
    // takes `{fd, ctx, drain-fn}` triples, so it never learns the facade
    // exists and stays testable against a pipe.
    const event_pump = b.createModule(.{ .root_source_file = b.path("src/event_pump.zig"), .target = target, .optimize = optimize });
    event_pump.addImport("mgf_hrb_model", model);
    event_pump.addImport("mgf_hrb_kabi", kabi);
    event_pump.addImport("mgf_hrb_subscription", subscription);
    // request — the S1 hardened trust-gate decode (bytes -> typed Request).
    const request = b.createModule(.{ .root_source_file = b.path("src/request.zig"), .target = target, .optimize = optimize });
    request.addImport("mgf_hrb_model", model);
    request.addImport("mgf_hrb_cap", cap);
    // core — the lease engine (atomic grant, G3 ghost-free reclamation).
    const core = b.createModule(.{ .root_source_file = b.path("src/core.zig"), .target = target, .optimize = optimize });
    core.addImport("mgf_hrb_model", model);
    core.addImport("mgf_hrb_kabi", kabi);
    core.addImport("mgf_hrb_cap", cap);
    core.addImport("mgf_hrb_fairness", fairness);
    // facade — the live constellation (workers + caps + liveness).
    const facade = b.createModule(.{ .root_source_file = b.path("src/facade.zig"), .target = target, .optimize = optimize });
    facade.addImport("mgf_hrb_model", model);
    facade.addImport("mgf_hrb_kabi", kabi);
    facade.addImport("mgf_hrb_cap", cap);
    facade.addImport("mgf_hrb_core", core);
    facade.addImport("mgf_hrb_liveness", liveness);
    facade.addImport("mgf_hrb_irq_monitor", irq_monitor);
    facade.addImport("mgf_hrb_linux_bound", linux_bound);
    // M-B B1 — the facade composes the constellation, so it is the facade
    // that points each worker Core's event sink at the subscription
    // fan-out. subscription imports only model + kabi, so this edge adds
    // no cycle: core stays ignorant of subscriptions entirely.
    facade.addImport("mgf_hrb_subscription", subscription);

    // Internal helper modules (not INDEX siblings, but shared across
    // multiple units — so they must be modules, else each consumer would
    // compile its own copy and their types would not unify at the seam).
    const sysfs_walk = b.createModule(.{ .root_source_file = b.path("src/sysfs_walk.zig"), .target = target, .optimize = optimize });
    sysfs_walk.addImport("mgf_hrb_kabi", kabi);
    const driver_registry = b.createModule(.{ .root_source_file = b.path("src/driver_registry.zig"), .target = target, .optimize = optimize });
    // fake_backend now comes from the mgf-hrb-kabi package (see above).
    const usb_synth = b.createModule(.{ .root_source_file = b.path("src/usb_synth.zig"), .target = target, .optimize = optimize });
    usb_synth.addImport("mgf_hrb_model", model);
    usb_synth.addImport("mgf_hrb_kabi", kabi);
    usb_synth.addImport("mgf_hrb_identity", identity);
    const usb = b.createModule(.{ .root_source_file = b.path("src/usb.zig"), .target = target, .optimize = optimize });
    usb.addImport("mgf_hrb_model", model);
    usb.addImport("mgf_hrb_identity", identity);
    usb.addImport("mgf_hrb_fake_backend", fake_backend);
    // linux_iommu — IOMMU-group resolution + DMA-safety posture for the PCI
    // claim path (#7 / D9). Pure-ish sysfs reader over model URNs.
    const linux_iommu = b.createModule(.{ .root_source_file = b.path("src/linux_iommu.zig"), .target = target, .optimize = optimize });
    linux_iommu.addImport("mgf_hrb_model", model);
    // vfio — the VFIO container/group/device bind orchestration (#7 / R5 slice 1).
    const vfio = b.createModule(.{ .root_source_file = b.path("src/vfio.zig"), .target = target, .optimize = optimize });
    vfio.addImport("mgf_hrb_model", model);
    const linux_backend = b.createModule(.{ .root_source_file = b.path("src/linux_backend.zig"), .target = target, .optimize = optimize });
    linux_backend.addImport("mgf_hrb_model", model);
    linux_backend.addImport("mgf_hrb_kabi", kabi);
    linux_backend.addImport("mgf_hrb_sysfs_walk", sysfs_walk);
    linux_backend.addImport("mgf_hrb_linux_iommu", linux_iommu);
    linux_backend.addImport("mgf_hrb_vfio", vfio);

    // wire layer — the broker-protocol decomposition from Part 1. Kept
    // in-repo (NOT an E2 carve target); packagized for SRP + enforced
    // boundaries. format is the formatter set; dispatch sits above it.
    const format = b.createModule(.{ .root_source_file = b.path("src/format.zig"), .target = target, .optimize = optimize });
    format.addImport("mgf_hrb_model", model);
    format.addImport("mgf_hrb_kabi", kabi);
    format.addImport("mgf_hrb_cap", cap);
    format.addImport("mgf_hrb_core", core);
    format.addImport("mgf_hrb_facade", facade);
    format.addImport("mgf_hrb_request", request);
    format.addImport("mgf_hrb_audit", audit);
    format.addImport("mgf_hrb_subscription", subscription);
    // M-B B2 — `state_snapshot`'s broker_version comes from the same
    // build-time authority as the hello handshake.
    format.addImport("build_options", build_options);
    format.addImport("mgf_hrb_driver_registry", driver_registry);
    format.addImport("mgf_hrb_json_escape", json_escape);
    format.addImport("mgf_hrb_irq_monitor", irq_monitor);
    format.addImport("mgf_hrb_linux_bound", linux_bound);
    const dispatch = b.createModule(.{ .root_source_file = b.path("src/dispatch.zig"), .target = target, .optimize = optimize });
    dispatch.addImport("mgf_hrb_model", model);
    dispatch.addImport("mgf_hrb_kabi", kabi);
    dispatch.addImport("mgf_hrb_cap", cap);
    dispatch.addImport("mgf_hrb_facade", facade);
    dispatch.addImport("mgf_hrb_request", request);
    dispatch.addImport("mgf_hrb_audit", audit);
    dispatch.addImport("mgf_hrb_admission", admission);
    dispatch.addImport("mgf_hrb_format", format);
    dispatch.addImport("mgf_hrb_shm", shm);
    dispatch.addImport("mgf_hrb_ring", ring);
    // linux_usb — the USB worker (2,167 LOC). Tier-2 assembly over the
    // linux substrate + usb helpers.
    const linux_usb = b.createModule(.{ .root_source_file = b.path("src/linux_usb.zig"), .target = target, .optimize = optimize });
    linux_usb.addImport("mgf_hrb_model", model);
    linux_usb.addImport("mgf_hrb_kabi", kabi);
    linux_usb.addImport("mgf_hrb_identity", identity);
    linux_usb.addImport("mgf_hrb_sysfs_walk", sysfs_walk);
    linux_usb.addImport("mgf_hrb_linux_backend", linux_backend);
    linux_usb.addImport("mgf_hrb_usb", usb);
    linux_usb.addImport("mgf_hrb_usb_synth", usb_synth);
    // privileged-helper IPC (Part 11). priv_ipc is the typed wire on
    // fd 3; usb_backend_via_ipc is the broker-side client. They run in
    // separate processes, so they need not unify types at compile time,
    // but packagizing keeps the DAG complete + boundaries enforced.
    const priv_ipc = b.createModule(.{ .root_source_file = b.path("src/priv_ipc.zig"), .target = target, .optimize = optimize });
    priv_ipc.addImport("mgf_hrb_model", model);
    const usb_backend_via_ipc = b.createModule(.{ .root_source_file = b.path("src/usb_backend_via_ipc.zig"), .target = target, .optimize = optimize });
    usb_backend_via_ipc.addImport("mgf_hrb_model", model);
    usb_backend_via_ipc.addImport("mgf_hrb_kabi", kabi);
    usb_backend_via_ipc.addImport("mgf_hrb_linux_usb", linux_usb);
    usb_backend_via_ipc.addImport("mgf_hrb_priv_ipc", priv_ipc);

    const pkg_mods = [_]std.Build.Module.Import{
        .{ .name = "build_options", .module = build_options },
        .{ .name = "mgf_hrb_model", .module = model },
        .{ .name = "mgf_hrb_kabi", .module = kabi },
        .{ .name = "mgf_hrb_ring", .module = ring },
        .{ .name = "mgf_hrb_liveness", .module = liveness },
        .{ .name = "mgf_hrb_fdpass", .module = fdpass },
        .{ .name = "mgf_hrb_json_escape", .module = json_escape },
        .{ .name = "mgf_hrb_audit", .module = audit },
        .{ .name = "mgf_hrb_admission", .module = admission },
        .{ .name = "mgf_hrb_cap", .module = cap },
        .{ .name = "mgf_hrb_identity", .module = identity },
        .{ .name = "mgf_hrb_subscription", .module = subscription },
        .{ .name = "mgf_hrb_event_pump", .module = event_pump },
        .{ .name = "mgf_hrb_request", .module = request },
        .{ .name = "mgf_hrb_core", .module = core },
        .{ .name = "mgf_hrb_facade", .module = facade },
        .{ .name = "mgf_hrb_sysfs_walk", .module = sysfs_walk },
        .{ .name = "mgf_hrb_driver_registry", .module = driver_registry },
        .{ .name = "mgf_hrb_fake_backend", .module = fake_backend },
        .{ .name = "mgf_hrb_usb_synth", .module = usb_synth },
        .{ .name = "mgf_hrb_usb", .module = usb },
        .{ .name = "mgf_hrb_linux_backend", .module = linux_backend },
        .{ .name = "mgf_hrb_linux_iommu", .module = linux_iommu },
        .{ .name = "mgf_hrb_format", .module = format },
        .{ .name = "mgf_hrb_dispatch", .module = dispatch },
        .{ .name = "mgf_hrb_shm", .module = shm },
        .{ .name = "mgf_hrb_irq_monitor", .module = irq_monitor },
        .{ .name = "mgf_hrb_linux_irq", .module = linux_irq },
        .{ .name = "mgf_hrb_linux_bound", .module = linux_bound },
        .{ .name = "mgf_hrb_restart_marker", .module = restart_marker },
        .{ .name = "mgf_hrb_fairness", .module = fairness },
        .{ .name = "mgf_hrb_linux_usb", .module = linux_usb },
        .{ .name = "mgf_hrb_priv_ipc", .module = priv_ipc },
        .{ .name = "mgf_hrb_usb_backend_via_ipc", .module = usb_backend_via_ipc },
    };

    const exe_srcs = [_]struct { name: []const u8, root: []const u8 }{
        // the broker server executable
        .{ .name = "mgf-hrb-server", .root = "src/server.zig" },
        // U2 exit-gate reporter: walks /sys/bus/usb/devices and prints
        // the fidelity-tagged tree. Direct-substrate tool — no broker.
        .{ .name = "mgf-hrb-usb-scan", .root = "src/usb_scan.zig" },
        // U2b live demo: opens a netlink uevent watch and streams
        // add/remove events for USB interfaces.
        .{ .name = "mgf-hrb-usb-watch", .root = "src/usb_watch.zig" },
        // U3b: the per-worker privileged USB helper subprocess (Part 11
        // / D-031). Holds the only elevated handle for USBDEVFS ops;
        // speaks the typed `priv_ipc.zig` protocol on fd 3.
        .{ .name = "mgf-hrb-usb-priv", .root = "src/usb_priv_main.zig" },
    };
    inline for (exe_srcs) |e| {
        const root_mod = b.createModule(.{
            .root_source_file = b.path(e.root),
            .target = target,
            .optimize = optimize,
        });
        for (pkg_mods) |imp| root_mod.addImport(imp.name, imp.module);
        const exe = b.addExecutable(.{ .name = e.name, .root_module = root_mod });
        b.installArtifact(exe);
    }

    const test_step = b.step("test", "Run all test suites");
    inline for (.{
        // conformance.zig is carved to mgf-hrb-kabi (its acceptance test
        // ships with the seam); it runs in that repo, not here.
        "src/core_test.zig", "src/cap_test.zig",
        "src/fairness.zig",
        // ring_test + fdpass_test are carved to mgf-hrb-rt; liveness_test
        // stays here (it drives the broker-internal lease engine).
        "src/liveness_test.zig", "src/linux_test.zig",
        "src/linux_iommu.zig",
        "src/vfio.zig",
        "src/facade_test.zig", "src/request_test.zig",
        // identity_test is carved to mgf-common-identity.
        "src/usb_test.zig", "src/linux_usb_test.zig",
        "src/usb_synth.zig", "src/stub_usb_test.zig", "src/priv_ipc.zig",
        "src/usb_priv_main.zig",
        "src/subscription_test.zig",
        // M-B B2 — the in-broker event pump's lifecycle + readiness pins.
        "src/event_pump_test.zig",
        "src/shm.zig",
        "src/irq_monitor.zig",
        "src/linux_irq.zig",
        "src/linux_bound.zig",
        "src/restart_marker.zig",
        "src/admission.zig",
        "src/format_test.zig",
        "src/dispatch.zig",
        "src/urn_test.zig",
        "src/usb_backend_via_ipc.zig",
        // linux_usb carries 9 inline tests; as a module (E1) they no
        // longer run via path-importers, so it is a test target now.
        "src/linux_usb.zig",
        // G2 (review wave 2) — extracted from server.zig so its
        // escape discipline (which protects every formatter that
        // splices substrate-controlled bytes into JSON) is reachable
        // by `zig build test`. server.zig itself is built as an
        // executable, not a test target.
        "src/json_escape.zig",
        // Federation cross-language conformance (mgf-common OB-10): the Zig
        // half of the canonical LogRecord emitter, vendored from mgf-common
        // (which ships no Zig toolchain). Its embedded `test` block reproduces
        // the shared golden fixture byte-for-byte. mgf-hrb is the federation
        // member that compiles + pins it. See vendor/logrecord/log_record.zig.
        "vendor/logrecord/log_record.zig",
    }) |src| {
        const tmod = b.createModule(.{
            .root_source_file = b.path(src),
            .target = target,
            .optimize = optimize,
        });
        for (pkg_mods) |imp| tmod.addImport(imp.name, imp.module);
        const t = b.addTest(.{ .root_module = tmod });
        test_step.dependOn(&b.addRunArtifact(t).step);
    }
}
