// SPDX-License-Identifier: LCL-1.0
// Copyright (c) 2026 Markus Maiwald
// Stewardship: Self Sovereign Society Foundation
//
// This file is part of the Nexus Commonwealth.
// See legal/LICENSE_COMMONWEALTH.md for license terms.

//! Rumpk Build System — Unified Pipeline
//!
//! Single command builds everything: Nim kernel + Zig HAL + LwIP + link.
//!
//! Usage:
//!   zig build                         # Build kernel (default: riscv64)
//!   zig build -Darch=aarch64          # ARM64 target
//!   zig build -Darch=x86_64           # AMD64 target
//!   zig build nim                     # Recompile Nim kernel sources only
//!   zig build test-lwf                # Run LWF tests (native host)

const std = @import("std");

const Arch = enum { riscv64, aarch64, x86_64 };

/// Per-architecture build configuration.
const ArchConfig = struct {
    cpu_arch: std.Target.Cpu.Arch,
    code_model: std.builtin.CodeModel,
    switch_asm: []const u8,
    linker_script: []const u8,
    /// Arch name for build_nim.sh
    nim_arch: []const u8,
};

fn archConfig(arch: Arch) ArchConfig {
    return switch (arch) {
        .riscv64 => .{
            .cpu_arch = .riscv64,
            .code_model = .medany,
            .switch_asm = "hal/arch/riscv64/switch.S",
            .linker_script = "boot/linker.ld",
            .nim_arch = "riscv64",
        },
        .aarch64 => .{
            .cpu_arch = .aarch64,
            .code_model = .default,
            .switch_asm = "hal/arch/aarch64/switch.S",
            .linker_script = "boot/linker_aarch64.ld",
            .nim_arch = "aarch64",
        },
        .x86_64 => .{
            .cpu_arch = .x86_64,
            .code_model = .kernel,
            .switch_asm = "hal/arch/x86_64/switch.S",
            .linker_script = "boot/linker_x86_64.ld",
            .nim_arch = "x86_64",
        },
    };
}

/// Apply freestanding kernel settings to a module.
fn applyKernelSettings(mod: *std.Build.Module, code_model: std.builtin.CodeModel) void {
    mod.red_zone = false;
    mod.stack_check = false;
    mod.code_model = code_model;
    mod.sanitize_thread = false;
    mod.single_threaded = true;
    mod.strip = true;
}

pub fn build(b: *std.Build) void {
    // =========================================================
    // Target Selection: -Darch=riscv64|aarch64|x86_64
    // =========================================================

    const arch = b.option(Arch, "arch", "Target architecture (default: riscv64)") orelse .riscv64;
    const config = archConfig(arch);

    var query: std.Target.Query = .{
        .cpu_arch = config.cpu_arch,
        .os_tag = .freestanding,
        .abi = .none,
    };
    // Disable NEON/FP in kernel code generation to avoid unaligned SIMD stores
    if (config.cpu_arch == .aarch64) {
        query.cpu_features_sub = std.Target.aarch64.featureSet(&.{.neon});
    }
    const target = b.resolveTargetQuery(query);

    // HEPHAESTUS DECREE: Force ReleaseSmall. A kernel runs naked.
    const optimize: std.builtin.OptimizeMode = .ReleaseSmall;

    // =========================================================
    // Step: Nim Kernel Compilation (nim -> C -> .o)
    // =========================================================
    // Calls build_nim.sh which handles:
    //   1. nim c --compileOnly -> generates C
    //   2. zig cc -> cross-compiles C to .o
    // Incremental: only recompiles changed files.

    const nim_step = b.addSystemCommand(&.{ "./build_nim.sh", config.nim_arch });
    const nim_build_step = b.step("nim", "Recompile Nim kernel sources (nim -> C -> .o)");
    nim_build_step.dependOn(&nim_step.step);

    // =========================================================
    // L0: Hardware Abstraction Layer (Zig)
    // =========================================================

    const hal_mod = b.createModule(.{
        .root_source_file = b.path("hal/abi.zig"),
        .target = target,
        .optimize = optimize,
    });
    applyKernelSettings(hal_mod, config.code_model);

    const hal = b.addLibrary(.{
        .name = "rumpk_hal",
        .root_module = hal_mod,
        .linkage = .static,
    });

    b.installArtifact(hal);

    // =========================================================
    // Boot: Entry Point (Assembly + Zig)
    // =========================================================

    const boot_mod = b.createModule(.{
        .root_source_file = b.path("boot/header.zig"),
        .target = target,
        .optimize = optimize,
    });
    applyKernelSettings(boot_mod, config.code_model);

    const boot = b.addObject(.{
        .name = "boot",
        .root_module = boot_mod,
    });

    // =========================================================
    // LwIP: Lightweight TCP/IP Stack
    // =========================================================
    // Compiled as C objects with freestanding flags.
    // ubsan disabled per-file to avoid runtime dependency.

    const lwip_base = "libs/membrane/external/lwip/src/";
    const lwip_srcs = [_][]const u8{
        // Core
        lwip_base ++ "core/init.c",
        lwip_base ++ "core/def.c",
        lwip_base ++ "core/dns.c",
        lwip_base ++ "core/inet_chksum.c",
        lwip_base ++ "core/ip.c",
        lwip_base ++ "core/mem.c",
        lwip_base ++ "core/memp.c",
        lwip_base ++ "core/netif.c",
        lwip_base ++ "core/pbuf.c",
        lwip_base ++ "core/raw.c",
        lwip_base ++ "core/sys.c",
        lwip_base ++ "core/tcp.c",
        lwip_base ++ "core/tcp_in.c",
        lwip_base ++ "core/tcp_out.c",
        lwip_base ++ "core/timeouts.c",
        lwip_base ++ "core/udp.c",
        // IPv4
        lwip_base ++ "core/ipv4/autoip.c",
        lwip_base ++ "core/ipv4/dhcp.c",
        lwip_base ++ "core/ipv4/etharp.c",
        lwip_base ++ "core/ipv4/icmp.c",
        lwip_base ++ "core/ipv4/ip4.c",
        lwip_base ++ "core/ipv4/ip4_addr.c",
        lwip_base ++ "core/ipv4/ip4_frag.c",
        // Netif
        lwip_base ++ "netif/ethernet.c",
        // SysArch (Rumpk integration)
        "libs/membrane/sys_arch.c",
    };

    const lwip_flags: []const []const u8 = switch (arch) {
        .riscv64 => &.{
            "-Os",             "-fno-sanitize=all",       "-Wno-everything",
            "-DNO_SYS=1",      "-mcmodel=medany",         "-Icore",
            "-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
        },
        .aarch64 => &.{
            "-Os",                     "-fno-sanitize=all",                         "-Wno-everything",
            "-DNO_SYS=1",              "-Icore",                                    "-Ilibs/membrane",
            "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
        },
        .x86_64 => &.{
            "-Os",             "-fno-sanitize=all",       "-Wno-everything",
            "-DNO_SYS=1",      "-mcmodel=kernel",         "-Icore",
            "-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
        },
    };

    const liblwip = b.addLibrary(.{
        .name = "lwip",
        .root_module = b.createModule(.{
            .target = target,
            .optimize = optimize,
        }),
        .linkage = .static,
    });
    liblwip.root_module.sanitize_thread = false;
    liblwip.root_module.red_zone = false;
    liblwip.root_module.single_threaded = true;

    for (lwip_srcs) |src| {
        liblwip.addCSourceFile(.{
            .file = b.path(src),
            .flags = lwip_flags,
        });
    }

    b.installArtifact(liblwip);

    // =========================================================
    // LittleFS: Sovereign Filesystem (Persistent Storage)
    // =========================================================
    // Compiled as C object with freestanding flags.
    // Uses LFS_CONFIG=lfs_rumpk.h to replace lfs_util.h entirely,
    // avoiding stdio/assert deps. malloc/free provided by clib.c.

    const lfs_flags: []const []const u8 = switch (arch) {
        .riscv64 => &.{
            "-Os",                      "-fno-sanitize=all", "-Wno-everything",
            "-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", "-mcmodel=medany",
        },
        .aarch64 => &.{
            "-Os",                      "-fno-sanitize=all", "-Wno-everything",
            "-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs",
        },
        .x86_64 => &.{
            "-Os",                      "-fno-sanitize=all", "-Wno-everything",
            "-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", "-mcmodel=kernel",
        },
    };

    const lfs_obj = b.addObject(.{
        .name = "littlefs",
        .root_module = b.createModule(.{
            .target = target,
            .optimize = optimize,
        }),
    });
    lfs_obj.root_module.sanitize_thread = false;
    lfs_obj.root_module.red_zone = false;
    lfs_obj.root_module.single_threaded = true;

    lfs_obj.addCSourceFile(.{
        .file = b.path("vendor/littlefs/lfs.c"),
        .flags = lfs_flags,
    });

    // =========================================================
    // Dependencies (CLib, LibC Shim, LWF Adapter, Switch)
    // =========================================================

    // 1. CLib (Minimal C stdlib)
    const clib = b.addObject(.{
        .name = "clib",
        .root_module = b.createModule(.{
            .target = target,
            .optimize = optimize,
        }),
    });
    clib.addCSourceFile(.{
        .file = b.path("libs/membrane/clib.c"),
        .flags = switch (arch) {
            .riscv64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1", "-mcmodel=medany" },
            .aarch64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1" },
            .x86_64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1", "-mcmodel=kernel" },
        },
    });

    // 2. LibC Shim (Zig)
    const libc_shim_mod = b.createModule(.{
        .root_source_file = b.path("libs/membrane/libc_shim.zig"),
        .target = target,
        .optimize = optimize,
    });
    applyKernelSettings(libc_shim_mod, config.code_model);

    const libc_shim = b.addObject(.{
        .name = "libc_shim",
        .root_module = libc_shim_mod,
    });

    // 3. LittleFS HAL (VirtIO-Block ↔ LittleFS glue)
    const lfs_hal_mod = b.createModule(.{
        .root_source_file = b.path("hal/littlefs_hal.zig"),
        .target = target,
        .optimize = optimize,
    });
    applyKernelSettings(lfs_hal_mod, config.code_model);

    const lfs_hal = b.addObject(.{
        .name = "lfs_hal",
        .root_module = lfs_hal_mod,
    });

    // 4. LWF Adapter (Project LibWeb — Libertaria Wire Frame)
    const lwf_adapter_mod = b.createModule(.{
        .root_source_file = b.path("libs/libertaria/lwf_adapter.zig"),
        .target = target,
        .optimize = optimize,
    });
    applyKernelSettings(lwf_adapter_mod, config.code_model);

    const lwf_adapter = b.addObject(.{
        .name = "lwf_adapter",
        .root_module = lwf_adapter_mod,
    });

    // 4. Context Switch (Architecture-specific Assembly)
    const switch_obj = b.addObject(.{
        .name = "switch",
        .root_module = b.createModule(.{
            .target = target,
            .optimize = optimize,
        }),
    });
    switch_obj.addAssemblyFile(b.path(config.switch_asm));

    // =========================================================
    // Final Link: rumpk.elf
    // =========================================================

    const kernel_mod = b.createModule(.{
        .root_source_file = b.path("hal/abi.zig"),
        .target = target,
        .optimize = optimize,
    });
    applyKernelSettings(kernel_mod, config.code_model);

    const kernel = b.addExecutable(.{
        .name = "rumpk.elf",
        .root_module = kernel_mod,
    });

    kernel.setLinkerScript(b.path(config.linker_script));
    kernel.addObject(boot);
    kernel.addObject(clib);
    kernel.addObject(libc_shim);
    kernel.addObject(lfs_hal);
    kernel.addObject(lwf_adapter);
    kernel.addObject(switch_obj);

    // LwIP linked into kernel — Membrane/NetSwitch drives DHCP/TCP/ICMP.
    kernel.linkLibrary(liblwip);

    // LittleFS IS linked into kernel — provides /nexus persistent storage.
    kernel.addObject(lfs_obj);

    // Nim-generated objects from build/nimcache/
    {
        var nimcache_dir = std.fs.cwd().openDir("build/nimcache", .{ .iterate = true }) catch |err| {
            std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err});
            return;
        };
        defer nimcache_dir.close();
        var it = nimcache_dir.iterate();
        while (it.next() catch null) |entry| {
            if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".o")) {
                const path = b.fmt("build/nimcache/{s}", .{entry.name});
                kernel.addObjectFile(b.path(path));
            }
        }
    }

    // Embedded InitRD — assemble for the target architecture
    const initrd_obj = b.addObject(.{
        .name = "initrd",
        .root_module = b.createModule(.{
            .target = target,
            .optimize = optimize,
        }),
    });
    initrd_obj.addAssemblyFile(b.path("build/embed_initrd.S"));
    kernel.addObject(initrd_obj);

    b.installArtifact(kernel);

    // Make default install depend on Nim compilation
    kernel.step.dependOn(&nim_step.step);

    // =========================================================
    // Tests (always run on native host)
    // =========================================================

    // HAL unit tests
    const hal_test_mod = b.createModule(.{
        .root_source_file = b.path("hal/abi.zig"),
        .target = b.graph.host,
    });

    const hal_tests = b.addTest(.{
        .root_module = hal_test_mod,
    });

    const run_hal_tests = b.addRunArtifact(hal_tests);
    const test_step = b.step("test", "Run Rumpk HAL tests (native host)");
    test_step.dependOn(&run_hal_tests.step);

    // LWF Adapter + Membrane tests (Project LibWeb)
    const lwf_test_mod = b.createModule(.{
        .root_source_file = b.path("libs/libertaria/lwf_adapter.zig"),
        .target = b.graph.host,
    });

    const lwf_tests = b.addTest(.{
        .root_module = lwf_test_mod,
    });

    const run_lwf_tests = b.addRunArtifact(lwf_tests);
    const lwf_test_step = b.step("test-lwf", "Run LWF Adapter tests (Project LibWeb)");
    lwf_test_step.dependOn(&run_lwf_tests.step);

    const lwf_membrane_mod = b.createModule(.{
        .root_source_file = b.path("libs/libertaria/lwf_membrane.zig"),
        .target = b.graph.host,
    });

    const lwf_membrane_tests = b.addTest(.{
        .root_module = lwf_membrane_mod,
    });

    const run_lwf_membrane_tests = b.addRunArtifact(lwf_membrane_tests);
    lwf_test_step.dependOn(&run_lwf_membrane_tests.step);
}
