# htmx-ssr runtime image. Disjoint layer graph from Dockerfile.leptos-hydration: no
# wasm32 target, no wasm-bindgen, no wasm-opt, no cargo-leptos, no node/css
# stage. The server renders the full body server-side; interactivity is htmx
# attributes + /api/htmx/* endpoints, so the image bakes only the binary.
#
# Base: lamina/rust (rust-slim + sccache + cargo-chef + just + mold/clang) — NOT
# lamina/leptos/lamina/rustelo. The rustelo pre-cook layer buys nothing here:
# the framework source is COPY'd in regardless (path dep), so cargo-chef cooks
# the full recipe (rustelo + website deps) over the thin rust base.
#
# Content-agnostic: NO site/ baked in, and unlike the leptos image there is no
# WASM pkg/ either. The entire site tree (content/, config/, i18n/, templates/,
# public/ incl. freshly-built styles/website.css, rbac.ncl) is delivered to the
# PV at /var/www/site/ by the publish tool. website.css is built during distro
# assembly (just distro / content.nu run uno), never inside this image.
#
# Build arguments — supplied via --build-arg from lian-build/build_directives.ncl.
#   RUST_VERSION     lamina/rust tag
#   NICKEL_VERSION   lamina/nickel tag (runtime nickel + build.rs NCL eval)
#   LAMINA_REGISTRY  lamina catalog registry base
#   BIN_NAME         [[bin]] name in crates/server/Cargo.toml
#   BIN_FEATURES     full feature expression for the SSR build (e.g. "htmx-ssr")
#   SERVER_PORT      port the server listens on (EXPOSE + LEPTOS_SITE_ADDR)
ARG RUST_VERSION
ARG NICKEL_VERSION
ARG LAMINA_REGISTRY
ARG BIN_NAME
ARG BIN_FEATURES
ARG SERVER_PORT

# Named alias — BuildKit does not expand ARGs inside --from=; named stage required.
FROM --platform=$BUILDPLATFORM ${LAMINA_REGISTRY}/nickel:${NICKEL_VERSION} AS nickel-bin

# ─── Stage 0: cargo-chef planner ─────────────────────────────────────────────
FROM ${LAMINA_REGISTRY}/rust:${RUST_VERSION} AS planner
WORKDIR /workspace
ENV CARGO_TARGET_DIR=/workspace/website/target
COPY Cargo.toml Cargo.lock    ./website/
COPY crates/                  ./website/crates/
COPY rustelo/    /workspace/rustelo/
COPY stratumiops/ /workspace/stratumiops/
WORKDIR /workspace/website
RUN cargo chef prepare --recipe-path recipe.json

# ─── Stage 1: build (deps cook + SSR binary) ─────────────────────────────────
FROM ${LAMINA_REGISTRY}/rust:${RUST_VERSION} AS builder
# Pin target dir so the runtime-stage COPY path is stable (lamina/rust defaults
# to /cargo-install-target).
ENV CARGO_TARGET_DIR=/workspace/website/target

COPY --from=nickel-bin /usr/local/bin/nickel /usr/local/bin/nickel

WORKDIR /workspace/website
COPY --from=planner /workspace/website/recipe.json recipe.json
COPY rustelo/    /workspace/rustelo/
COPY stratumiops/ /workspace/stratumiops/

# Cook deps for the exact feature set the binary is built with — no WASM target,
# so a single native cook covers everything (contrast: the leptos image cooks
# ssr deps separately because cargo-leptos handles the wasm32 pass).
ARG BIN_FEATURES
RUN --mount=type=secret,id=sccache_env,required \
    --mount=type=cache,target=/workspace/website/target \
    --mount=type=cache,target=/usr/local/cargo/registry \
    set -a && . /run/secrets/sccache_env && set +a && \
    export RUSTC_WRAPPER=sccache && \
    RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=mold" \
    cargo chef cook --release --no-default-features --features "${BIN_FEATURES}" \
        --recipe-path recipe.json && \
    sccache --show-stats

# Full sources for the actual build.
COPY Cargo.toml Cargo.lock    ./
COPY crates/                  ./crates/
# ManifestResolver (rustelo_utils) walks up from CARGO_MANIFEST_DIR for this.
COPY rustelo.manifest.toml    ./rustelo.manifest.toml
# build.rs (build_page_generator) evaluates site/config NCL during compilation.
COPY site/config/             ./site/config/
COPY site/rbac.ncl            ./site/rbac.ncl

ARG BIN_NAME
# build.rs reads SITE_CONFIG_PATH; nickel resolves framework contracts via
# NICKEL_IMPORT_PATH (rustelo layout is resources/nickel/, not nickel/).
ENV SITE_CONFIG_PATH=/workspace/website/site/config/index.ncl
ENV NICKEL_IMPORT_PATH=/workspace/rustelo/resources/nickel:/workspace/website/site/config
RUN --mount=type=secret,id=sccache_env,required \
    --mount=type=cache,target=/workspace/website/target \
    --mount=type=cache,target=/usr/local/cargo/registry \
    set -a && . /run/secrets/sccache_env && set +a && \
    export RUSTC_WRAPPER=sccache && \
    sccache --start-server && \
    RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=mold" \
    cargo build --release -p rustelo-htmx-server --no-default-features --features "${BIN_FEATURES}" && \
    cp /workspace/website/target/release/${BIN_NAME} /tmp/${BIN_NAME} && \
    CARGO_TARGET_DIR=/workspace/website/target \
    cargo build --release -p rustelo_content_graph_ssr --features gen \
        --manifest-path /workspace/rustelo/crates/foundation/crates/rustelo_content_graph_ssr/Cargo.toml && \
    cp /workspace/website/target/release/gen-content-graph /tmp/gen-content-graph && \
    sccache --show-stats

# ─── Stage 2: runtime ─────────────────────────────────────────────────────────
FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates libssl3 curl \
    && rm -rf /var/lib/apt/lists/*

RUN useradd -m -u 1000 www

ARG BIN_NAME
ARG SERVER_PORT

# Server binary → /usr/local/bin/ so data PV mounts never shadow it.
COPY --from=builder /tmp/${BIN_NAME} /usr/local/bin/${BIN_NAME}
# Content-graph generator: regenerates content_graph.json from site/content + ontoref
# without a rebuild. Run: gen-content-graph [out.json] (needs ONTOREF_NICKEL_IMPORT_PATH).
COPY --from=builder /tmp/gen-content-graph /usr/local/bin/gen-content-graph
# nickel required at runtime: server calls `nickel export` to parse site/config/*.ncl
# on startup and site/rbac.ncl on RBAC hot-reload.
COPY --from=nickel-bin /usr/local/bin/nickel /usr/local/bin/nickel
# Rustelo framework nickel contracts — required by site/config/*.ncl at runtime.
COPY --from=builder /workspace/rustelo/resources/nickel/ /usr/local/share/rustelo/nickel/

WORKDIR /var/www
# Content-agnostic image: NO site/ baked in, NO WASM pkg/. The site tree
# (public/ incl. styles/website.css, content/, config/, i18n/, templates/,
# rbac.ncl) is delivered to the PV mounted at /var/www/site/ by the publish
# tool. server.ncl public_dir = "site/public" resolves identically in dev
# (source tree) and prod (PV-delivered tree). The htmx shell loads its runtime
# from public/ (htmx.min.js, extensions, reinit handlers) — all PV-served.
# The PV mount must be populated before first pod start; the K8s securityContext
# fsGroup=1000 (catalog deployment template) handles ownership at mount.

USER www
EXPOSE ${SERVER_PORT}

ENV RUST_LOG=info \
    LEPTOS_ENV=PROD \
    LEPTOS_SITE_ADDR=0.0.0.0:${SERVER_PORT} \
    LEPTOS_SITE_ROOT=site \
    SITE_CONFIG_PATH=/var/www/site/config/index.ncl \
    NICKEL_IMPORT_PATH=/usr/local/share/rustelo/nickel:/var/www/site/config \
    SITE_SERVER_CONTENT_URL=/r \
    SITE_SERVER_ROOT_CONTENT=r \
    SITE_SERVER_CONTENT_ROOT=site/r \
    NATS_ADMIN_CREDENTIALS_FILE=

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD curl -sf http://localhost:${SERVER_PORT}/health > /dev/null || exit 1

# Entrypoint: wait for the PV-delivered site tree before exec'ing the server.
# Image is content-agnostic; site/config/index.ncl must exist for the binary to
# boot. While the PV is empty (first install, before publish-tool delivery),
# this keeps the container alive and logs a clear "waiting" message instead of
# crash-looping. Signals propagate via exec when the server takes over.
COPY --chmod=755 <<'EOF' /usr/local/bin/entrypoint.sh
#!/bin/sh
set -eu
CONFIG="${SITE_CONFIG_PATH:-/var/www/site/config/index.ncl}"
while [ ! -f "$CONFIG" ]; do
    printf '[%s] site config missing at %s — waiting for PV bootstrap (publish tool to deliver site tarball)\n' \
        "$(date -Iseconds)" "$CONFIG" >&2
    sleep 10
done
printf '[%s] site config found at %s — starting server\n' "$(date -Iseconds)" "$CONFIG" >&2
exec /usr/local/bin/rustelo-htmx-server "$@"
EOF

CMD ["/usr/local/bin/entrypoint.sh"]
