# 基础镜像（使用 Alpine，手动处理 OpenSSL 兼容性）
ARG BASE_IMAGE=node:18-alpine
ARG ALPINE_MIRROR=https://mirrors.tuna.tsinghua.edu.cn/alpine
FROM ${BASE_IMAGE} AS base

# 安装依赖阶段
FROM base AS deps
ARG ALPINE_MIRROR
RUN sed -i "s|https://dl-cdn.alpinelinux.org/alpine|${ALPINE_MIRROR}|g" /etc/apk/repositories && \
    apk add --no-cache libc6-compat python3 make g++ cairo-dev jpeg-dev pango-dev giflib-dev
WORKDIR /app

# 复制 package.json（利用 Docker 层缓存）
COPY package.json package-lock.json* ./

# canvas 在项目里是可选依赖，且服务端代码已做降级处理。
# 构建机当前卡在 canvas 的 node-pre-gyp 安装阶段，因此这里只在镜像构建时跳过它。
RUN node -e "const fs=require('fs'); const pkg=require('./package.json'); if (pkg.optionalDependencies && pkg.optionalDependencies.canvas) { delete pkg.optionalDependencies.canvas; fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\\n'); }"

# 安装依赖（使用淘宝镜像 + BuildKit缓存）
RUN --mount=type=cache,target=/root/.npm \
    echo "Installing dependencies (using cache if available)..." && \
    npm config set registry https://registry.npmmirror.com && \
    npm install --prefer-offline --no-audit --no-fund --legacy-peer-deps && \
    echo "Dependencies installed ($(du -sh /app/node_modules | cut -f1))"

# 构建阶段
FROM base AS builder
ARG ALPINE_MIRROR
# 安装构建依赖
RUN sed -i "s|https://dl-cdn.alpinelinux.org/alpine|${ALPINE_MIRROR}|g" /etc/apk/repositories && \
    apk add --no-cache libc6-compat python3 make g++ cairo-dev jpeg-dev pango-dev giflib-dev ffmpeg openssl
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# 这两个动态路由负责兜底访问本地上传文件；如果它们在构建上下文里丢失，
# 线上音频/视频就会直接 404。这里提前失败，避免再把缺路由的镜像发出去。
RUN test -f "/app/app/api/uploads/[...path]/route.ts" && \
    test -f "/app/app/uploads/[...path]/route.ts"

# 设置环境变量
ENV NEXT_TELEMETRY_DISABLED=1

# 构建时使用假的环境变量（只用于构建，运行时会被容器的真实环境变量覆盖）
# 这样 Prisma 和其他模块在构建时不会报错
ENV DATABASE_URL="postgresql://build:build@localhost:5432/build"
ENV NEXTAUTH_SECRET="build-time-secret"
ENV NEXTAUTH_URL="http://localhost:3000"
ENV ADMIN_API_KEY="build-time-key"
ENV DINGTALK_CLIENT_ID="build-time-dingtalk-id"
ENV DINGTALK_CLIENT_SECRET="build-time-dingtalk-secret"

# 构建应用（复用 Next.js 构建缓存）
RUN --mount=type=cache,target=/app/.next/cache \
    NODE_ENV=production npm run build

# 清理 standalone 目录中不需要的文件（减少 1GB+ 体积）
RUN echo "清理 standalone 目录..." && \
    rm -rf .next/standalone/uploads && \
    rm -rf .next/standalone/.build-cache && \
    rm -rf .next/standalone/.docker-cache && \
    rm -rf .next/standalone/.git && \
    rm -rf .next/standalone/__tests__ && \
    rm -rf .next/standalone/docs && \
    rm -rf .next/standalone/backups && \
    rm -rf .next/standalone/.kiro && \
    rm -rf .next/standalone/scripts && \
    echo "清理完成"

# 生产运行阶段
FROM base AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

ARG ALPINE_MIRROR
RUN sed -i "s|https://dl-cdn.alpinelinux.org/alpine|${ALPINE_MIRROR}|g" /etc/apk/repositories && \
    apk add --no-cache ffmpeg python3 cairo jpeg pango giflib openssl poppler-utils

RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

RUN mkdir -p /app/scripts

# 复制必要文件
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/app ./app

# 只复制运行时必需的 node_modules
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/ag-psd ./node_modules/ag-psd
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/ioredis ./node_modules/ioredis
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@ioredis ./node_modules/@ioredis
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@aws-sdk ./node_modules/@aws-sdk
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@smithy ./node_modules/@smithy

# 只复制 schema.prisma，不复制 migrations 和 engine-cache（减少 35MB）
COPY --from=builder --chown=nextjs:nodejs /app/prisma/schema.prisma ./prisma/schema.prisma
COPY --from=builder --chown=nextjs:nodejs /app/lib ./lib
COPY --from=builder --chown=nextjs:nodejs /app/scripts/public-traffic-rollup-from-nginx.py ./scripts/public-traffic-rollup-from-nginx.py
COPY --from=builder --chown=nextjs:nodejs /app/scripts/collapse-performance-survey-template-versions.mjs ./scripts/collapse-performance-survey-template-versions.mjs

# 创建必要目录并收紧权限。
# 应用代码归 root 所有，运行账号只能写上传目录和 Next.js 运行时缓存；
# 即使上层文件路径校验再次出现缺陷，也不能覆盖 server.js 或业务源码。
RUN mkdir -p /app/public /app/uploads && \
    mkdir -p /app/scripts && \
    mkdir -p /app/.next/cache && \
    mkdir -p /app/uploads/temp && \
    mkdir -p /app/uploads/assembled && \
    mkdir -p /app/uploads/materials && \
    mkdir -p /app/uploads/cleanup && \
    chown -R root:root /app && \
    chown -R nextjs:nodejs /app/.next/cache /app/uploads && \
    chmod 0750 /app/uploads /app/uploads/temp /app/uploads/assembled /app/uploads/materials /app/uploads/cleanup

USER nextjs

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]
