# Multi-stage Dockerfile for Cuanto Mailer
# Creates a single image containing the API, Worker, Web dashboard and SMTP relay
# Use SERVICE environment variable to specify which service to run

# ============================================
# Stage 1: Dependencies (All dependencies for building)
# ============================================
# Use build platform (AMD64) to install dependencies, avoiding QEMU issues
FROM --platform=$BUILDPLATFORM node:20-slim AS deps
ARG TARGETPLATFORM
ARG BUILDPLATFORM
WORKDIR /app

# Install curl for health checks
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

# Enable Corepack and set Yarn version
RUN corepack enable && corepack prepare yarn@4.9.1 --activate

# Copy Yarn configuration and release
COPY .yarnrc.yml ./
COPY .yarn/releases ./.yarn/releases

# Copy package files for dependency installation
COPY package.json yarn.lock ./

# Copy workspace package.json files
# Every workspace listed in the root package.json must be present here, even if
# it is never built or run in the image: `yarn install --immutable` re-resolves
# the whole workspace graph and fails if a manifest is missing, because the
# lockfile it produces would differ from the committed one.
COPY apps/api/package.json ./apps/api/
COPY apps/smtp/package.json ./apps/smtp/
COPY apps/web/package.json ./apps/web/
COPY packages/db/package.json ./packages/db/
COPY packages/ui/package.json ./packages/ui/
COPY packages/shared/package.json ./packages/shared/
COPY packages/types/package.json ./packages/types/
COPY packages/email/package.json ./packages/email/
COPY packages/typescript-config/package.json ./packages/typescript-config/
COPY packages/eslint-config/package.json ./packages/eslint-config/

# Install dependencies (runs on build platform, fetches binaries for target platform)
# Use cache mounts for Yarn cache to speed up dependency installation
RUN --mount=type=cache,target=/root/.yarn/berry/cache,sharing=locked \
    --mount=type=cache,target=/root/.cache/yarn,sharing=locked \
    echo "Building on $BUILDPLATFORM for $TARGETPLATFORM" && \
    yarn install --immutable

# ============================================
# Stage 1b: Production Dependencies for API/SMTP
# ============================================
# Install only production dependencies needed for API and SMTP services
FROM --platform=$BUILDPLATFORM node:20-slim AS prod-deps
ARG TARGETPLATFORM
ARG BUILDPLATFORM
WORKDIR /app

# Enable Corepack and set Yarn version
RUN corepack enable && corepack prepare yarn@4.9.1 --activate

# Copy Yarn configuration
COPY .yarnrc.yml ./
COPY .yarn/releases ./.yarn/releases

# Copy all package.json files (needed for workspace resolution)
COPY package.json yarn.lock ./
COPY apps/api/package.json ./apps/api/
COPY apps/smtp/package.json ./apps/smtp/
COPY packages/db/package.json ./packages/db/
COPY packages/shared/package.json ./packages/shared/
COPY packages/types/package.json ./packages/types/
COPY packages/email/package.json ./packages/email/

# Install ONLY production dependencies for api, smtp, and their workspace dependencies
# This excludes devDependencies and unneeded workspaces (web, landing, wiki, ui)
RUN --mount=type=cache,target=/root/.yarn/berry/cache,sharing=locked \
    --mount=type=cache,target=/root/.cache/yarn,sharing=locked \
    echo "Installing production dependencies for API/SMTP on $BUILDPLATFORM for $TARGETPLATFORM" && \
    yarn workspaces focus api smtp --production

# ============================================
# Stage 2: Builder
# ============================================
# Builder runs on target platform to generate platform-specific artifacts
FROM node:20-slim AS builder
ARG TARGETPLATFORM

# Build-time arguments for URL configuration
# These are only used during the build process (for wiki OpenAPI generation and static assets)
# Runtime URLs are configured via *_DOMAIN and USE_HTTPS environment variables at container startup
ARG API_URI=https://api.mailer.placeholder
ARG DASHBOARD_URI=https://app.mailer.placeholder
ARG LANDING_URI=https://www.mailer.placeholder
ARG WIKI_URI=https://docs.mailer.placeholder

WORKDIR /app

# Install OpenSSL for Prisma
RUN apt-get update && apt-get install -y openssl && rm -rf /var/lib/apt/lists/*

# Enable Corepack and set Yarn version
RUN corepack enable && corepack prepare yarn@4.9.1 --activate

# Copy dependencies from deps stage
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/.yarn ./.yarn
COPY --from=deps /app/.yarnrc.yml ./
COPY --from=deps /app/package.json ./
COPY --from=deps /app/yarn.lock ./

# ============================================
# OPTIMIZATION: Copy and build in layers for better Docker cache utilization
# Docker will only rebuild from the first changed layer, not everything
# ============================================

# Copy root config files needed for Turbo
COPY turbo.json ./

# Copy manifest generation script
COPY docker/generate-url-manifest.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/generate-url-manifest.sh

# Step 1: Copy and build shared packages (these change less frequently)
# Shared packages are dependencies for apps, so build them first.

COPY packages ./packages
RUN yarn workspace @mailer/db db:generate
RUN --mount=type=cache,target=/app/.turbo,sharing=locked \
    API_URI=${API_URI} \
    DASHBOARD_URI=${DASHBOARD_URI} \
    LANDING_URI=${LANDING_URI} \
    WIKI_URI=${WIKI_URI} \
    NEXT_PUBLIC_API_URI=${API_URI} \
    NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} \
    NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \
    NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \
    yarn turbo build --filter="@mailer/*"

# Step 2: Copy and build API (backend services)
COPY apps/api ./apps/api
COPY apps/smtp ./apps/smtp
RUN --mount=type=cache,target=/app/.turbo,sharing=locked \
    API_URI=${API_URI} \
    DASHBOARD_URI=${DASHBOARD_URI} \
    LANDING_URI=${LANDING_URI} \
    WIKI_URI=${WIKI_URI} \
    NEXT_PUBLIC_API_URI=${API_URI} \
    NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} \
    NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \
    NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \
    yarn turbo build --filter=api --filter=smtp

# Step 4: Copy and build Web dashboard
COPY apps/web ./apps/web
RUN --mount=type=cache,target=/app/.turbo,sharing=locked \
    API_URI=${API_URI} \
    DASHBOARD_URI=${DASHBOARD_URI} \
    LANDING_URI=${LANDING_URI} \
    WIKI_URI=${WIKI_URI} \
    NEXT_PUBLIC_API_URI=${API_URI} \
    NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} \
    NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \
    NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \
    yarn turbo build --filter=web
# Generate sitemap for web
RUN NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} yarn workspace web sitemap
# Generate URL replacement manifest for web (build-time optimization)
RUN generate-url-manifest.sh web /app/apps/web

# Copy any remaining root files (if needed)
COPY . .

# Ensure directories exist (create empty ones if build didn't generate them)
RUN mkdir -p \
    apps/web/public \
    apps/web/.next/standalone

# ============================================
# Stage 3: Production Runtime
# ============================================
FROM node:20-alpine AS runner
ARG TARGETPLATFORM
ARG BUILDPLATFORM
WORKDIR /app

# Install OpenSSL for Prisma, curl for health checks, nginx, and gettext (for envsubst)
RUN apk add --no-cache openssl curl nginx gettext

# Install PM2 globally for process management
# Use cache mount and specific version to prevent hangs
RUN --mount=type=cache,target=/root/.npm \
    npm install -g pm2@5.4.2 --prefer-offline --no-audit

# Create non-root user for security
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 mailer

# Create nginx directories and set permissions
RUN mkdir -p /var/log/nginx /var/lib/nginx /run/nginx && \
    chown -R mailer:nodejs /var/log/nginx /var/lib/nginx /run/nginx /etc/nginx

# ============================================
# Copy API and SMTP services with minimal dependencies
# ============================================

# Copy built API and SMTP services
COPY --from=builder --chown=mailer:nodejs /app/apps/api/dist ./apps/api/dist
COPY --from=builder --chown=mailer:nodejs /app/apps/smtp/dist ./apps/smtp/dist

# Copy ONLY production dependencies for API/SMTP (excludes dev deps and frontend packages)
COPY --from=prod-deps --chown=mailer:nodejs /app/node_modules ./node_modules

# Copy Prisma client from builder (includes generated client with correct platform binaries)
COPY --from=builder --chown=mailer:nodejs /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder --chown=mailer:nodejs /app/node_modules/@prisma ./node_modules/@prisma

# Copy only the shared packages that are built (not source files)
# These are needed by API/SMTP at runtime
COPY --from=builder --chown=mailer:nodejs /app/packages/db/dist ./packages/db/dist
COPY --from=builder --chown=mailer:nodejs /app/packages/db/package.json ./packages/db/package.json
COPY --from=builder --chown=mailer:nodejs /app/packages/shared/dist ./packages/shared/dist
COPY --from=builder --chown=mailer:nodejs /app/packages/shared/package.json ./packages/shared/package.json
COPY --from=builder --chown=mailer:nodejs /app/packages/email/dist ./packages/email/dist
COPY --from=builder --chown=mailer:nodejs /app/packages/email/package.json ./packages/email/package.json
COPY --from=builder --chown=mailer:nodejs /app/packages/types/dist ./packages/types/dist
COPY --from=builder --chown=mailer:nodejs /app/packages/types/package.json ./packages/types/package.json

# Copy Prisma schema (needed for migrations at runtime)
COPY --from=builder --chown=mailer:nodejs /app/packages/db/prisma ./packages/db/prisma

# Copy root package.json and workspace config (needed for yarn workspace commands in entrypoint)
COPY --from=builder --chown=mailer:nodejs /app/package.json ./
COPY --from=prod-deps --chown=mailer:nodejs /app/.yarnrc.yml ./
COPY --from=prod-deps --chown=mailer:nodejs /app/.yarn ./.yarn
COPY --from=prod-deps --chown=mailer:nodejs /app/yarn.lock ./

# Copy API/SMTP package.json files
COPY --from=builder --chown=mailer:nodejs /app/apps/api/package.json ./apps/api/
COPY --from=builder --chown=mailer:nodejs /app/apps/smtp/package.json ./apps/smtp/

# ============================================
# Copy Next.js apps with their standalone builds
# ============================================
# Next.js standalone mode bundles all dependencies internally, so we don't need
# to copy node_modules for these apps - they're completely self-contained

# Web app - standalone build with static assets
COPY --from=builder --chown=mailer:nodejs /app/apps/web/.next/standalone ./apps/web/.next/standalone
COPY --from=builder --chown=mailer:nodejs /app/apps/web/public ./apps/web/.next/standalone/apps/web/public
COPY --from=builder --chown=mailer:nodejs /app/apps/web/.next/static ./apps/web/.next/standalone/apps/web/.next/static
# Copy URL replacement manifests to standalone directory
COPY --from=builder --chown=mailer:nodejs /app/apps/web/.next/url-manifest.txt ./apps/web/.next/standalone/apps/web/.next/url-manifest.txt
COPY --from=builder --chown=mailer:nodejs /app/apps/web/.next/sitemap-manifest.txt ./apps/web/.next/standalone/apps/web/.next/sitemap-manifest.txt

# AGPL-3.0 section 13: the complete corresponding source of this image, served by the
# dashboard at /source.tar.gz. rebrand/build-image.sh writes it from `git archive HEAD`.
COPY --chown=mailer:nodejs source.tar.gz ./apps/web/.next/standalone/apps/web/public/source.tar.gz

# Copy full .next directories for the entrypoint script (URL replacement via find command)
# These are much smaller than node_modules and needed for runtime URL replacement
COPY --from=builder --chown=mailer:nodejs /app/apps/web/.next ./apps/web/.next

# ============================================
# Copy runtime configuration
# ============================================

# Copy nginx configuration templates and setup script
COPY --chown=mailer:nodejs docker/nginx/ /app/docker/nginx/
RUN chmod +x /app/docker/nginx/setup-nginx.sh

# Copy optimized URL replacement script
COPY --chown=mailer:nodejs docker/replace-urls-optimized.sh /app/docker/
RUN chmod +x /app/docker/replace-urls-optimized.sh
COPY --chown=mailer:nodejs docker/apply-brand.sh /app/docker/

# Copy entrypoint script
COPY --chown=mailer:nodejs docker-entrypoint-nginx.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint-nginx.sh

USER mailer

# Fetch the musl Prisma schema engine at build time. The runner stage (alpine) only gets the
# debian engines from the builder, so `prisma migrate deploy` would otherwise download it from
# binaries.prisma.sh on every container start, and fail on hosts without egress.
RUN /app/node_modules/.bin/prisma -v >/dev/null && ls /app/node_modules/@prisma/engines/ | grep -q musl

# Expose nginx port (default 80), SMTP ports (465, 587)
# Port 80 is also used for ACME HTTP-01 challenges
EXPOSE 80 465 587

# Health check through nginx
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:80/ || exit 1

# Default to running all services via entrypoint
ENV SERVICE=all

ENTRYPOINT ["docker-entrypoint-nginx.sh"]
