# syntax=docker/dockerfile:1.7
#
# Polaris Task Force — production image for Coolify.
#
# Stack: Next.js 16 (output: "standalone") + Payload CMS 3.79 + Bun 1.3.11.
# Installed and built with Bun; the Next.js runtime runs on Node 22
# (Next's standalone server is a node script), and Bun is kept in the
# runner image so the Payload CLI bin scripts (game-tick / market-tick /
# migrate) work via `docker exec ... bun run payload <bin>` against the
# running container.
#
# See DEPLOYMENT.md for the full Coolify workflow (per-env Postgres,
# env vars, scheduled jobs, persistent media volume, rollback).

# ───────────────────────────── base ─────────────────────────────
# Node 22 alpine + Bun, shared by all three stages.
FROM node:22-alpine AS base
RUN apk add --no-cache libc6-compat curl wget bash
# && npm install -g bun@1.3.11 \
# && bun --version && node --version

SHELL ["/bin/bash", "-c"]

# Bun (baseline) for CPU without support for AVX/AVX2
RUN curl -fsSL https://bun.sh/install | bash

# ────────────────────────── prod-deps ────────────────────────────
# Production-only install. Used at runtime alongside the standalone
# server so the Payload CLI / migrations / bin scripts have every
# package they need (the Next standalone prunes node_modules to only
# what the web server imports — pruned tree does NOT include `payload`,
# `@payloadcms/*` admin libs, drizzle, etc.).
FROM base AS prod-deps
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --production --frozen-lockfile

# ─────────────────────────── builder ─────────────────────────────
# Full install (incl. devDeps) + Next.js standalone build.
FROM base AS builder
# `git` is needed only at build time so `generate:version-info` can populate
# commitHash / gitDescribe / commitDate in src/generated/versionInfo.json
# (the script gracefully no-ops without it, but we want the metadata in the
# admin VersionOverlay). Not carried into the runner image.
RUN apk add --no-cache git
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
COPY . .
# next.config.mjs requires `output: "standalone"` (already set).
# `bun run build` == `bun run generate:version-info && next build --webpack`.
# We deliberately DO NOT run `payload generate:importmap` / `generate:types`
# here: both bootstrap the Payload config, which connects to the database
# (none is available at build time). The committed artifacts in the repo
# are the source of truth and ship as-is.
#
# Build-time-only env shim: Next.js prerenders /login and other pages that
# import `@payload-config` (Payload.init runs at import time during static
# generation). Payload refuses to init without PAYLOAD_SECRET, and Nodemailer
# emits a (non-fatal) warning without EMAIL_* — both come from real env vars
# at runtime, NOT baked into the image. We pass stand-in values via ARGs to
# let the build's static-gen step complete. These values never ship: ENV in
# later stages and the runtime container's env (set by Coolify per env) win.
ARG PAYLOAD_SECRET_BUILD=dummy-build-secret-not-used-at-runtime
ARG EMAIL_HOST_BUILD=localhost
ARG EMAIL_PORT_BUILD=587
ENV PAYLOAD_SECRET=$PAYLOAD_SECRET_BUILD \
    EMAIL_HOST=$EMAIL_HOST_BUILD \
    EMAIL_PORT=$EMAIL_PORT_BUILD
RUN bun run build

# ─────────────────────────── runner ──────────────────────────────
# Final production image: Next standalone runtime + full prod deps
# (for payload bins) + src/migrations (for `bun run payload <bin>`).
FROM base AS runner
WORKDIR /appregistry.onyxsimple.com/polaris-task-force

ENV NODE_ENV=production \
    NEXT_TELEMETRY_DISABLED=1 \
    PORT=3000 \
    HOSTNAME=0.0.0.0 \
    PNPM_HOME=/app

# The `node` user already exists in `node:*-alpine` images.
RUN mkdir -p /app/media \
 && chown -R node:node /app

# 1) Next.js standalone runtime (server.js + traced node_modules + .next).
#    `.next/standalone` is laid out flat at /app, so server.js ends up at
#    /app/server.js and its traced node_modules at /app/node_modules.
COPY --from=builder --chown=node:node /app/.next/standalone ./
# 2) Static assets (standalone does NOT include these).
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
# 3) Public assets (standalone does NOT include these either).
COPY --from=builder --chown=node:node /app/public ./public

# 4) Overlay the FULL production node_modules on top of the pruned
#    standalone one. Docker COPY merges directories file-by-file
#    (existing files overwritten, others preserved), so the result
#    is the union — effectively the full prod install — while keeping
#    any Next-internal files the standalone tree brought along.
COPY --from=prod-deps --chown=node:node /app/node_modules ./node_modules

# 5) Source + top-level config so `bun run payload <bin>` and
#    `payload migrate` work via `docker exec`. These read
#    @payload-config (alias in tsconfig.json) and the Payload
#    migration files under src/migrations/*.
COPY --from=builder --chown=node:node /app/src ./src
COPY --from=builder --chown=node:node /app/package.json ./package.json
COPY --from=builder --chown=node:node /app/tsconfig.json ./tsconfig.json
COPY --from=builder --chown=node:node /app/bun.lock ./bun.lock
COPY --from=builder --chown=node:node /app/next.config.mjs ./next.config.mjs
COPY --from=builder --chown=node:node /app/postcss.config.mjs ./postcss.config.mjs
COPY --from=builder --chown=node:node /app/components.json ./components.json
COPY --from=builder --chown=node:node /app/drizzle.config.ts ./drizzle.config.ts

# Persistent storage mount point for locally-stored Payload uploads.
# In Coolify: attach a Persistent Storage Volume here so uploaded media
# survives container restarts and rollbacks. (See DEPLOYMENT.md.)
VOLUME ["/app/media"]

EXPOSE 3000

# Liveness probe hooked into our `/api/health` route (no DB dependency,
# so a transient DB outage does NOT cycle the container).
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
  CMD wget -qO- "http://127.0.0.1:${PORT:-3000}/api/health" >/dev/null 2>&1 || exit 1

COPY --chmod=755 docker-entrypoint.sh /docker-entrypoint.sh

USER node

# Next.js standalone server (node process). Plain `node server.js` — Bun is
# installed for the `docker exec` one-shot bins, not for the web runtime
# (Next is shipped and tested on Node).
ENTRYPOINT ["/bin/sh", "/docker-entrypoint.sh"]
CMD ["node", "server.js"]