# The application image, built off site and shipped as a file.
#
# `architecture.md` §1: the factory LAN has no internet, ever. Nothing here may
# reach out at build time on site or at run time at all. The build happens on a
# connected machine, `docker save` writes a tar, and the tar crosses on a USB
# disk. That is why every stage below pins its base image by digest-bearing tag
# and why the lockfile is copied before the source: a build that resolves a
# floating version is a build that cannot be reproduced six months later when
# somebody needs to roll back.
#
# The fonts are already local files under `app/`, loaded through
# `next/font/local`, so the build reaches nothing even when it could. The
# comment in `app/layout.tsx` records why that choice was made before there was
# a Dockerfile to justify it.

# ---------------------------------------------------------------------------
# Dependencies. Its own stage so a source change does not reinstall anything.
# ---------------------------------------------------------------------------
FROM oven/bun:1.3-alpine AS deps
WORKDIR /app
COPY package.json bun.lock ./
# --frozen-lockfile: the lockfile decides, and a build that would have changed
# it fails instead. On a machine that cannot reach a registry later, "it
# resolved something newer" is not a recoverable state.
RUN bun install --frozen-lockfile

# ---------------------------------------------------------------------------
# Build.
# ---------------------------------------------------------------------------
FROM oven/bun:1.3-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# `lib/env.ts` validates these with Zod at import time and throws on a missing
# one. `next build` imports the app, so the build needs values even though it
# connects to nothing: these are placeholders that satisfy the shape and are
# never baked into the output. The real ones arrive from the environment at
# run time. A secret of at least 32 characters, because that is what the schema
# demands and a shorter placeholder would fail the build for the wrong reason.
#
# The database in the placeholder must be named `advmfr_hr`, and so must the
# real one. `lib/env.ts` refuses any other name, which is what stops a stray
# DATABASE_URL from a neighbouring project reaching this product. It costs the
# freedom to name the production database something else; that trade was made
# deliberately and the runbook records it.
ENV DATABASE_URL="postgres://build:build@localhost:5432/advmfr_hr" \
    BETTER_AUTH_SECRET="build-time-placeholder-not-a-secret-32" \
    BETTER_AUTH_URL="http://localhost:3000" \
    NEXT_TELEMETRY_DISABLED=1

RUN bun run build

# ---------------------------------------------------------------------------
# Run.
#
# The whole node_modules travels, which is heavier than it needs to be. Next
# can emit a self-contained bundle with `output: "standalone"` in
# `next.config.ts`, which cuts this image by roughly two thirds and matters on
# a deployment that crosses on a USB disk. That file is deliberately not
# touched here; see deploy/README.md.
# ---------------------------------------------------------------------------
FROM oven/bun:1.3-alpine AS run
WORKDIR /app

# Not root. The image writes nothing outside /tmp at run time, and the one
# thing it must not be able to do is modify its own code.
RUN addgroup -g 1001 -S app && adduser -u 1001 -S app -G app

COPY --from=build --chown=app:app /app/.next ./.next
COPY --from=build --chown=app:app /app/public ./public
COPY --from=build --chown=app:app /app/node_modules ./node_modules
COPY --from=build --chown=app:app /app/package.json ./package.json

USER app
ENV NODE_ENV=production \
    NEXT_TELEMETRY_DISABLED=1 \
    PORT=3000
EXPOSE 3000

CMD ["bun", "run", "start"]

# ---------------------------------------------------------------------------
# Migrations and scheduled work.
#
# The same source, with the dev dependencies the runtime image does not carry:
# drizzle-kit reads `drizzle.config.ts` and the SQL under `drizzle/`, and the
# nightly job is a TypeScript file rather than a compiled entry point.
#
# Separate from `run` on purpose. A container that can rewrite the schema
# should not be the one serving requests, and `deploy.sh` runs this one to
# completion before the other one starts.
# ---------------------------------------------------------------------------
FROM oven/bun:1.3-alpine AS tools
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NODE_ENV=production \
    NEXT_TELEMETRY_DISABLED=1
CMD ["bun", "run", "db:migrate"]
