# syntax=docker/dockerfile:1

# PHP 8.4 et non 8.5 : c'est la version contre laquelle Composer résout les
# dépendances (voir config.platform dans composer.json). L'hôte de dev peut être
# en 8.5 sans que cela change ce qui sera déployé.
ARG PHP_VERSION=8.4
ARG FRANKENPHP_VERSION=1
ARG NODE_VERSION=22

# ─────────────────────────────────────────────────────────────────────────────
# base — runtime commun dev/prod
# ─────────────────────────────────────────────────────────────────────────────
FROM dunglas/frankenphp:${FRANKENPHP_VERSION}-php${PHP_VERSION} AS app_base

WORKDIR /app

# `acl` sert au setfacl sur var/ ; `gettext` fournit envsubst utilisé par l'entrypoint.
RUN apt-get update && apt-get install -y --no-install-recommends \
        acl \
        file \
        gettext \
        git \
    && rm -rf /var/lib/apt/lists/*

RUN install-php-extensions \
        @composer \
        apcu \
        intl \
        opcache \
        pdo_mysql \
        redis \
        zip

ENV COMPOSER_ALLOW_SUPERUSER=1
ENV PHP_INI_SCAN_DIR=":$PHP_INI_DIR/app.conf.d"

COPY --link frankenphp/conf.d/10-app.ini $PHP_INI_DIR/app.conf.d/
COPY --link frankenphp/Caddyfile /etc/frankenphp/Caddyfile

ENTRYPOINT ["docker-php-entrypoint"]
CMD ["frankenphp", "run", "--config", "/etc/frankenphp/Caddyfile"]

HEALTHCHECK --interval=10s --timeout=3s --start-period=45s --retries=5 \
    CMD curl -fsS http://localhost:2019/metrics || exit 1

# ─────────────────────────────────────────────────────────────────────────────
# dev — sources montées en volume, pas copiées
# ─────────────────────────────────────────────────────────────────────────────
FROM app_base AS app_dev

# APP_ENV n'est volontairement PAS défini ici. Le fichier .env fournit déjà
# `dev` par défaut, et une variable d'environnement réelle prendrait le pas sur
# le réglage de phpunit.dist.xml : `bin/phpunit` tournerait alors en dev, où le
# service test.service_container n'existe pas.
ENV XDEBUG_MODE=off

COPY --link frankenphp/conf.d/20-app.dev.ini $PHP_INI_DIR/app.conf.d/

RUN install-php-extensions xdebug

# Pas de composer install ici : le volume monté par Compose écraserait vendor/.
# L'entrypoint s'en charge au démarrage du conteneur.
COPY --link docker/entrypoint.dev.sh /usr/local/bin/entrypoint.dev.sh
RUN chmod +x /usr/local/bin/entrypoint.dev.sh

# CMD doit être redéclaré : Docker le remet à zéro dès qu'un étage redéfinit
# ENTRYPOINT, même si l'étage de base en fournissait un.
ENTRYPOINT ["docker-php-entrypoint", "entrypoint.dev.sh"]
CMD ["frankenphp", "run", "--config", "/etc/frankenphp/Caddyfile"]

# ─────────────────────────────────────────────────────────────────────────────
# vendor — couche de dépendances isolée pour maximiser le cache de build
# ─────────────────────────────────────────────────────────────────────────────
FROM app_base AS app_vendor

COPY --link composer.json composer.lock symfony.lock ./

RUN --mount=type=cache,target=/tmp/composer \
    composer install --no-cache --prefer-dist --no-dev --no-autoloader --no-scripts --no-progress

# ─────────────────────────────────────────────────────────────────────────────
# frontend — construction du back-office React
# ─────────────────────────────────────────────────────────────────────────────
# Indispensable, et pas une commodité : `public/build/` et `public/index.html`
# sont dans .gitignore. Une image construite depuis le dépôt sans cet étage
# démarre sans back-office — SpaController répond alors 503 « Le back-office
# n'est pas construit » sur chaque URL de l'interface. L'API, elle, fonctionne :
# la panne est donc partielle, ce qui la rend plus déroutante encore.
FROM node:${NODE_VERSION}-alpine AS app_frontend

WORKDIR /build

# Les manifestes d'abord : cette couche ne se reconstruit que lorsque les
# dépendances changent, pas à chaque modification d'un composant.
COPY --link package.json package-lock.json ./
RUN npm ci --no-audit --no-fund

COPY --link tsconfig.json vite.config.ts ./
COPY --link assets ./assets

# `npm run build` lance `tsc --noEmit && vite build` : une erreur de typage
# casse donc le déploiement ici, et non en production.
RUN npm run build

# ─────────────────────────────────────────────────────────────────────────────
# prod — image finale, worker mode activé
# ─────────────────────────────────────────────────────────────────────────────
FROM app_base AS app_prod

ENV APP_ENV=prod
ENV APP_RUNTIME="Runtime\FrankenPhpSymfony\Runtime"
# Active le worker mode : le kernel Symfony reste en mémoire entre les requêtes.
# C'est ce qui met la Delivery API sous les 20 ms.
ENV FRANKENPHP_CONFIG="worker ./public/index.php"

COPY --link frankenphp/conf.d/20-app.prod.ini $PHP_INI_DIR/app.conf.d/
COPY --from=app_vendor --link /app/vendor ./vendor
COPY --link . ./

# Le front APRÈS les sources : `COPY . ./` écraserait sinon public/ avec la
# version du dépôt, qui ne contient que index.php.
COPY --from=app_frontend --link /build/public/index.html ./public/index.html
COPY --from=app_frontend --link /build/public/build ./public/build

RUN set -eux; \
    mkdir -p var/cache var/log var/storage; \
    composer dump-autoload --classmap-authoritative --no-dev; \
    composer dump-env prod; \
    composer run-script --no-dev post-install-cmd; \
    chmod +x bin/console docker/entrypoint.prod.sh; \
    setfacl -R -m u:www-data:rwX -m u:root:rwX var; \
    setfacl -dR -m u:www-data:rwX -m u:root:rwX var

# Vérification au BUILD que le back-office est bien dans l'image. Le contrôleur
# dégrade proprement en 503 s'il manque — ce qui est le bon comportement en
# exécution, mais transformerait ici une erreur de build en panne silencieuse
# découverte par le premier utilisateur.
RUN test -f public/index.html && test -d public/build \
    || (echo 'Le front est absent de l’image : étage app_frontend cassé.' >&2 && exit 1)

# CMD est redéclaré parce que Docker le remet à zéro dès qu'un étage redéfinit
# ENTRYPOINT — le même piège que dans l'étage dev.
ENTRYPOINT ["docker-php-entrypoint", "docker/entrypoint.prod.sh"]
CMD ["frankenphp", "run", "--config", "/etc/frankenphp/Caddyfile"]

VOLUME /app/var/storage
