#!/usr/bin/env nu
# Local test helper for lian-build/Dockerfile.
# Assembles the build context (rsync project + rustelo + stratumiops),
# then runs docker build --target <stage> for incremental verification.
#
# Build ARGs are read from build_directives.ncl (via nickel export) and passed
# to docker build. Individual flags override NCL values when explicitly provided.
#
# Usage:
#   nu lian-build/ctx-test.nu                          # prepare ctx only
#   nu lian-build/ctx-test.nu --stage planner          # verify crates/ layout
#   nu lian-build/ctx-test.nu --stage css              # verify unocss pipeline
#   nu lian-build/ctx-test.nu --stage builder-deps     # verify dep cook (slow)
#   nu lian-build/ctx-test.nu --stage builder          # full Rust build (very slow)
#   nu lian-build/ctx-test.nu --run                    # build final image + smoke test
#
# ARG overrides (bypass NCL, force a specific value):
#   nu lian-build/ctx-test.nu --stage builder --bin-features "content-static,auth"
#   nu lian-build/ctx-test.nu --run --lamina-registry myregistry.example.com/lamina

# Read artifacts[0].build_args from build_directives.ncl.
# Returns a record of { ARG_NAME: string } or {} on any failure. build_directives
# now exports one directive per render profile, so the profile field is selected
# before reading artifacts[0].build_args.
def read_build_args [script_dir: string, profile: string] {
    let directives = ($script_dir | path join "build_directives.ncl")
    if not ($directives | path exists) { return {} }
    if (which nickel | is-empty) { return {} }

    let lian_root = if "LIAN_BUILD_ROOT" in $env {
        $env.LIAN_BUILD_ROOT
    } else {
        $script_dir | path join ".." ".." "lian-build" | path expand
    }
    if not ($lian_root | path exists) { return {} }

    let res = do { ^nickel export --import-path $lian_root $directives } | complete
    if $res.exit_code != 0 {
        print $"warning: nickel eval failed — ($res.stderr | str trim)"
        return {}
    }
    $res.stdout | from json | get -i $profile | default {}
        | get -i artifacts | default [] | first | default {} | get -i build_args | default {}
}

# Render profile, auto-detected from rendering.ncl (same source as build-auto).
def detect_profile [project_root: string]: nothing -> string {
    let dev_root = ($env.DEV_ROOT? | default "/Users/Akasha/Development")
    let detector = $"($dev_root)/rustelo/scripts/check-wasm-needed.nu"
    if ($detector | path exists) {
        let res = (do {
            cd $project_root
            ^nu $detector --routes-dir site/config --workspace-config site/config/rendering.ncl
        } | complete)
        match $res.exit_code { 0 => "leptos-hydration", 1 => "htmx-ssr", _ => "leptos-hydration" }
    } else {
        let ncl = $"($project_root)/site/config/rendering.ncl"
        if ($ncl | path exists) {
            open $ncl | parse --regex 'default_profile = "(?P<p>[^"]+)"' | get p?.0? | default "leptos-hydration"
        } else { "leptos-hydration" }
    }
}

# Resolve a single ARG value: CLI flag wins if non-empty, else NCL, else fallback.
def resolve [flag: string, ncl: record, key: string, fallback: string] {
    if not ($flag | is-empty) { $flag }
    else { $ncl | get -i $key | default $fallback }
}

# Verify required lamina registry images exist before starting the build.
# Uses `docker manifest inspect` (metadata only, no layer download).
# Exits with a clear error listing the lamina commands needed to fix missing layers.
def preflight_lamina [registry: string, profile: string, rust_version: string, node_version: string] {
    # Disjoint layer requirements per profile: leptos-hydration needs the wasm
    # toolchain (lamina/leptos) + node (css stage); htmx-ssr builds on the thin
    # lamina/rust base with no wasm and no node/css stage.
    let required = if $profile == "htmx-ssr" {
        [
            { image: $"($registry)/nickel:latest",       fix: "just build nickel" },
            { image: $"($registry)/rust:($rust_version)", fix: "just build rust" },
        ]
    } else {
        [
            { image: $"($registry)/nickel:latest",         fix: "just build nickel" },
            { image: $"($registry)/leptos:($rust_version)", fix: "just build wasm && just build leptos" },
            { image: $"($registry)/node:($node_version)",   fix: "just build node" },
        ]
    }

    let missing = $required | filter { |r|
        let res = do { ^docker manifest inspect $r.image } | complete
        $res.exit_code != 0
    }

    if ($missing | is-empty) { return }

    print "\nerror: required lamina layers not available in registry:"
    for m in $missing {
        print $"  ($m.image)"
        print $"    → run in lamina/: ($m.fix)"
    }
    error make { msg: "build blocked: lamina layers missing — see above" }
}

def main [
    --stage: string = ""
    --run
    --keep
    --profile: string            = ""   # leptos-hydration | htmx-ssr (default: auto-detect)
    --platform: string           = "linux/arm64"
    --rust-version: string       = ""
    --node-version: string       = ""
    --lamina-registry: string    = ""
    --bin-name: string           = ""
    --leptos-output-name: string = ""
    --bin-features: string       = ""
    --server-port: string        = ""
] {
    let script_dir       = $env.FILE_PWD
    let project_root     = ($script_dir | path join "..") | path expand
    let framework_root   = ($project_root | path join ".." "rustelo")     | path expand
    let stratumiops_root = ($project_root | path join ".." "stratumiops") | path expand

    let profile = if ($profile | is-not-empty) { $profile } else { detect_profile $project_root }
    if $profile not-in ["leptos-hydration" "htmx-ssr"] {
        error make { msg: $"invalid --profile '($profile)' (leptos-hydration | htmx-ssr)" }
    }
    let is_htmx = ($profile == "htmx-ssr")

    let ncl = read_build_args $script_dir $profile

    let default_features = if $is_htmx { "htmx-ssr" } else { "content-static" }
    let rust_version       = resolve $rust_version       $ncl "RUST_VERSION"       "1.89"
    let nickel_version     = resolve ""                  $ncl "NICKEL_VERSION"     "1.16.0"
    let node_version       = resolve $node_version       $ncl "NODE_VERSION"       "22"
    let lamina_registry    = resolve $lamina_registry    $ncl "LAMINA_REGISTRY"    "registry.example.com/lamina"
    let bin_name           = resolve $bin_name           $ncl "BIN_NAME"           "rustelo-htmx-server"
    let leptos_output_name = resolve $leptos_output_name $ncl "LEPTOS_OUTPUT_NAME" "website"
    let bin_features       = resolve $bin_features       $ncl "BIN_FEATURES"       $default_features
    let server_port        = resolve $server_port        $ncl "SERVER_PORT"        "3000"

    if not ($framework_root | path exists) {
        error make { msg: $"rustelo not found at ($framework_root)" }
    }

    # Pre-flight: fail fast with actionable message before any rsync or build.
    preflight_lamina $lamina_registry $profile $rust_version $node_version

    let ctx = (^mktemp -d | str trim)

    print $"profile:        ($profile)"
    print $"ctx:            ($ctx)"
    print $"project:        ($project_root)"
    print $"framework:      ($framework_root)"
    print $"lamina:         ($lamina_registry)"
    print $"build_args:     RUST_VERSION=($rust_version) BIN_FEATURES=($bin_features)"

    # Sync project into context root.
    print "syncing project..."
    ^rsync -a --delete ...[
        "--exclude=.git/"
        "--exclude=target/"
        "--exclude=node_modules/"
        "--exclude=.coder/"
        "--exclude=works-pv/"
        "--exclude=lian-build/ctx-*"
        $"($project_root)/"
        $"($ctx)/"
    ]

    # Sync rustelo as ctx/rustelo/ — Dockerfile uses COPY rustelo/ (primary context).
    print "syncing rustelo..."
    ^rsync -a --delete ...[
        "--exclude=.git/"
        "--exclude=target/"
        $"($framework_root)/"
        $"($ctx)/rustelo/"
    ]

    # Sync stratumiops if present — may be a transitive path dep of rustelo.
    if ($stratumiops_root | path exists) {
        print "syncing stratumiops..."
        ^rsync -a --delete ...[
            "--exclude=.git/"
            "--exclude=target/"
            $"($stratumiops_root)/"
            $"($ctx)/stratumiops/"
        ]
    }

    print $"context ready: ($ctx)"

    # Dockerfile suffix === profile name (no short forms).
    let dockerfile = ($project_root | path join "lian-build" $"Dockerfile.($profile)")

    # Common ARGs both Dockerfiles declare. NODE_VERSION and LEPTOS_OUTPUT_NAME
    # only exist in Dockerfile.leptos (no node/css stage or WASM output in htmx).
    let common_args = [
        "--build-arg" $"RUST_VERSION=($rust_version)"
        "--build-arg" $"NICKEL_VERSION=($nickel_version)"
        "--build-arg" $"LAMINA_REGISTRY=($lamina_registry)"
        "--build-arg" $"BIN_NAME=($bin_name)"
        "--build-arg" $"BIN_FEATURES=($bin_features)"
        "--build-arg" $"SERVER_PORT=($server_port)"
    ]
    let build_args = if $is_htmx {
        $common_args
    } else {
        $common_args ++ [
            "--build-arg" $"NODE_VERSION=($node_version)"
            "--build-arg" $"LEPTOS_OUTPUT_NAME=($leptos_output_name)"
        ]
    }

    if $run {
        let tag = "website:local-test"
        print $"\nbuilding final image: ($tag)"
        ^docker build ...([
            "--platform" $platform
            "-t"         $tag
            "-f"         $dockerfile
        ] ++ $build_args ++ [$ctx])

        if not $keep { ^rm -rf $ctx }

        let content_vol = $"($project_root)/site/content:/var/www/site/content:ro"
        let i18n_vol    = $"($project_root)/site/i18n:/var/www/site/i18n:ro"

        print "\nsmoke test — ctrl-c to stop"
        print $"health: http://localhost:($server_port)/health"
        ^docker run --rm -p $"($server_port):($server_port)" -v $content_vol -v $i18n_vol $tag
        return
    }

    if ($stage | is-empty) {
        print $"\nctx ready at: ($ctx)"
        print "pass --stage <name> to build a stage, or --run for the full image"
        if not $keep { ^rm -rf $ctx }
        return
    }

    print $"\nbuilding stage: ($stage)"
    ^docker build ...([
        "--platform" $platform
        "--target"   $stage
        "-t"         $"jpl-($stage):test"
        "-f"         $dockerfile
    ] ++ $build_args ++ [$ctx])

    if $keep { print $"ctx preserved: ($ctx)" } else { ^rm -rf $ctx }
    print $"\nimage: jpl-($stage):test"
}
