# syntax=docker/dockerfile:1
# Build/version info. APP_VERSION comes from the committed VERSION file (always correct,
# even for a plain `docker compose build`); GIT_SHA/BUILD_DATE are best-effort build-args
# the deploy scripts pass. An explicit APP_VERSION build-arg still overrides the file.
#
# GIT_SHA/BUILD_DATE differ on EVERY build: declare them as late as possible in each stage —
# anything below them can never cache.
ARG APP_VERSION=
ARG GIT_SHA=unknown
ARG BUILD_DATE=

# Stage 1: build the frontend SPA
FROM node:20-alpine AS frontend
WORKDIR /fe
# Deps first, keyed on the lockfile, so editing app source never re-resolves node_modules.
COPY frontend/package.json frontend/package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY frontend/ ./
COPY VERSION ./.version
# Version args land here — immediately before the only step that reads them — so they cannot
# invalidate the dependency layers above.
ARG APP_VERSION
ARG GIT_SHA
ARG BUILD_DATE
# Vite inlines VITE_*-prefixed env at build time; version falls back to the VERSION file.
RUN VITE_APP_VERSION="${APP_VERSION:-$(cat ./.version)}" \
    VITE_GIT_SHA="$GIT_SHA" \
    VITE_BUILD_DATE="$BUILD_DATE" \
    npm run build

# Stage 2: backend runtime, serving the built SPA
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

WORKDIR /app

# Create the app user BEFORE the copies so files land already-owned via COPY --chown (a trailing
# `chown -R /app` would rewrite every file into a duplicate layer). /downloads must be appuser-owned
# so a fresh named volume mounted there inherits that ownership (Docker copies the image dir's
# ownership into a new empty volume) — the worker/API can then write without a manual chown. A bind
# mount to a host path instead needs that path writable by this uid (see docs/self-hosting.md).
RUN adduser --disabled-password --gecos "" appuser \
    && mkdir -p /downloads \
    && chown appuser /app /downloads

# ffmpeg: merge separate video+audio streams + postprocessing. Deno: the JavaScript runtime
# yt-dlp uses to solve YouTube's "n" signature challenge for the web player clients (required
# alongside the PO token, else only storyboards are offered). yt-dlp auto-detects deno on PATH.
ARG DENO_VERSION=v2.9.1
RUN apt-get update \
    && apt-get install -y --no-install-recommends ffmpeg unzip curl ca-certificates \
    && curl -fsSL "https://github.com/denoland/deno/releases/download/${DENO_VERSION}/deno-x86_64-unknown-linux-gnu.zip" -o /tmp/deno.zip \
    && unzip -o /tmp/deno.zip -d /usr/local/bin \
    && chmod +x /usr/local/bin/deno \
    && rm /tmp/deno.zip \
    && rm -rf /var/lib/apt/lists/*

COPY backend/requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install --upgrade pip && pip install -r requirements.txt

COPY --chown=appuser backend/ .
# Read at runtime by config.py as the app_version (a committed single source of truth).
COPY --chown=appuser VERSION ./VERSION
COPY --chown=appuser --from=frontend /fe/dist ./app/static_spa

# Version stamps LAST — keep them below everything expensive (see the header note).
ARG GIT_SHA
ARG BUILD_DATE
ENV GIT_SHA=$GIT_SHA \
    BUILD_DATE=$BUILD_DATE

USER appuser
EXPOSE 8000
CMD ["sh", "entrypoint.sh"]
