# ─────────────────────────────────────────────────────────────────────────────
# Stage 1 — npm install (Tailwind CLI)
# Uses node:24-alpine for a small image. npm ci is used for reproducible,
# fast installs from the lockfile.
# ─────────────────────────────────────────────────────────────────────────────
FROM node:24-alpine AS npm-install

WORKDIR /npm
COPY Htmx.ApiDemo/package.json Htmx.ApiDemo/package-lock.json* ./
RUN npm ci

# ─────────────────────────────────────────────────────────────────────────────
# Stage 2 — AOT publish
# Uses the Alpine SDK image so the binary is linked against musl libc,
# making it compatible with the Alpine runtime image in Stage 3.
#
# Alpine packages are cached via BuildKit mount cache — after the first build
# `apk add` is near-instant because the package index and downloads are
# reused from the local cache rather than re-fetched from the network.
# ─────────────────────────────────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS publish

# Install AOT linker tools and Node.js (for the Tailwind MSBuild target).
# --mount=type=cache keeps the apk package cache between builds on this machine.
RUN --mount=type=cache,target=/var/cache/apk \
    apk add --no-cache clang lld musl-dev build-base nodejs npm

WORKDIR /src

# Copy project files first — NuGet restore layer is cached until these change.
COPY Htmx.slnx .
COPY Htmx.ApiDemo/Htmx.ApiDemo.csproj          Htmx.ApiDemo/
COPY Htmx.SourceGenerator/Htmx.SourceGenerator.csproj Htmx.SourceGenerator/

RUN dotnet restore Htmx.ApiDemo/Htmx.ApiDemo.csproj -r linux-musl-x64

# Bring in pre-installed node_modules from Stage 1.
COPY --from=npm-install /npm/node_modules Htmx.ApiDemo/node_modules

# Copy the rest of the source
COPY . .

# AOT publish targeting musl so the binary runs on Alpine.
RUN dotnet publish Htmx.ApiDemo/Htmx.ApiDemo.csproj \
        -c Release \
        -r linux-musl-x64 \
        --no-restore \
        --self-contained true \
        -o /publish

# ─────────────────────────────────────────────────────────────────────────────
# Stage 3 — Runtime image (~12 MB base vs ~100 MB on Debian)
# runtime-deps:alpine provides only the native libs the AOT binary needs.
# No .NET runtime is included — the binary is fully self-contained.
# ─────────────────────────────────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine AS runtime

WORKDIR /app
COPY --from=publish /publish .

# Cloud Run injects PORT (default 8080).
# ASP.NET Core reads ASPNETCORE_HTTP_PORTS directly — no entrypoint script needed.
ENV ASPNETCORE_HTTP_PORTS=8080

# Use the built-in non-root user provided by the official .NET Alpine image.
USER $APP_UID

EXPOSE 8080

ENTRYPOINT ["./Htmx.ApiDemo"]
