FROM node:20-slim AS base
WORKDIR /workspace
RUN apt-get update && apt-get install -y ca-certificates curl && rm -rf /var/lib/apt/lists/*

FROM base AS deps
RUN apt-get update && apt-get install -y python3 make g++ openssl libssl-dev && rm -rf /var/lib/apt/lists/*
COPY pnpm-workspace.yaml package.json tsconfig.json ./
# Copy .npmrc if it exists (optional - create empty if missing)
COPY apps/gateway ./apps/gateway
# Copy packages directory but we'll remove client-side ones immediately
COPY packages ./packages
# Remove client-side packages BEFORE pnpm reads the workspace (os-cli depends on unpublished @4runr/devkit)
RUN rm -rf packages/os-cli packages/cli packages/cli-tool packages/cli-tool-standalone packages/devkit || true
COPY prisma ./prisma
COPY src ./src
# Create .npmrc if it doesn't exist (pnpm will work without it)
RUN touch .npmrc || true
RUN corepack enable && corepack prepare pnpm@9.12.1 --activate
# Set node-linker to hoisted for flat node_modules structure
RUN pnpm config set node-linker hoisted
# Skip Prisma generation during install (we'll generate it in build if needed)
ENV PRISMA_SKIP_POSTINSTALL_GENERATE=1
RUN pnpm install --filter @4runr/shared --filter @4runr/sentinel --filter @4runr/gateway --workspace-root

FROM deps AS build
# Clean shared and sentinel package dists to ensure fresh build with updated types
RUN rm -rf packages/shared/dist packages/shared/.tsbuildinfo packages/sentinel/dist packages/sentinel/.tsbuildinfo
RUN pnpm --filter @4runr/shared run build
RUN pnpm --filter @4runr/sentinel run build
# Generate Prisma client before building gateway
# Use dummy DATABASE_URL for generation (just needs schema, not actual connection)
# Prisma schema is at /workspace/prisma/schema.prisma
RUN cd /workspace/apps/gateway && \
    export DATABASE_URL="postgresql://dummy:dummy@localhost:5432/dummy" && \
    pnpm db:generate && \
    (test -d node_modules/.prisma/client || test -d ../../node_modules/.prisma/client) && \
    echo "✓ Prisma client generated" || echo "✗ Prisma client generation failed"
# Compile root src/crypto folder (used by gateway routes)
# Create a temporary tsconfig for compiling just the crypto folder
RUN mkdir -p dist/src/crypto && \
    echo '{"compilerOptions":{"target":"ES2022","module":"ES2022","moduleResolution":"bundler","esModuleInterop":true,"skipLibCheck":true,"outDir":"dist/src/crypto","rootDir":"src/crypto"},"include":["src/crypto/envelope.ts"]}' > /tmp/crypto-tsconfig.json && \
    npx tsc --project /tmp/crypto-tsconfig.json 2>&1 | head -10 && \
    test -f dist/src/crypto/envelope.js && echo "✓ Compiled envelope.js" || echo "⚠ envelope.js compilation may have failed"

RUN rm -rf apps/gateway/dist && \
    pnpm --filter @4runr/gateway run build && \
    (test -f apps/gateway/dist/index.js || test -f apps/gateway/dist/apps/gateway/src/index.js) && \
    echo "✓ Gateway build completed successfully" || \
    (echo "✗ Build failed: index.js not found" && exit 1)

FROM base AS runner
WORKDIR /workspace
RUN apt-get update && apt-get install -y openssl ca-certificates curl && rm -rf /var/lib/apt/lists/*
# Copy package files, config, and built artifacts
COPY --from=build /workspace/package.json ./package.json
COPY --from=build /workspace/pnpm-workspace.yaml ./pnpm-workspace.yaml
# .npmrc is optional - create empty if it doesn't exist
RUN touch .npmrc || true
COPY --from=build /workspace/packages ./packages
COPY --from=build /workspace/apps/gateway/package.json ./apps/gateway/package.json
COPY --from=build /workspace/apps/gateway/dist ./apps/gateway/dist
# Copy public directory (static files like dashboard)
COPY --from=deps /workspace/apps/gateway/public ./apps/gateway/public
# Copy node_modules from build stage (hoisted node-linker creates flat structure)
# Use --chown to set ownership during copy to avoid breaking symlinks
COPY --from=build --chown=node:node /workspace/node_modules ./node_modules
# Copy Prisma schema (needed for runtime client generation if needed)
COPY --from=build /workspace/prisma ./prisma
# Copy src directory (contains crypto and memory-db for AI providers)
COPY --from=build /workspace/src ./src
# Copy compiled crypto (if it was compiled)
RUN mkdir -p dist/src && (cp -r /workspace/dist/src/* dist/src/ 2>/dev/null || echo "Note: No compiled src found")
# Verify Prisma client was copied (could be in root node_modules with hoisted linker)
RUN (test -d node_modules/.prisma/client || test -d apps/gateway/node_modules/.prisma/client) && \
    echo "✓ Prisma client found" || \
    echo "⚠ Prisma client not found - checking..." && \
    find node_modules -name ".prisma" -type d 2>/dev/null | head -5 || \
    echo "⚠ Will generate Prisma client at runtime if needed"
# Verify shared package is accessible
RUN test -d packages/shared/dist && echo "✓ shared/dist exists" || echo "✗ shared/dist missing"
RUN test -f packages/shared/dist/index.js && echo "✓ shared/dist/index.js exists" || echo "✗ shared/dist/index.js missing"
# Check if @4runr/shared exists and if it's a symlink
# If not, create it (symlinks don't need special ownership)
RUN if [ ! -L node_modules/@4runr/shared ] && [ ! -d node_modules/@4runr/shared ]; then \
        echo "✗ @4runr/shared NOT found in node_modules, creating symlink..."; \
        mkdir -p node_modules/@4runr && \
        ln -s ../../packages/shared node_modules/@4runr/shared && \
        echo "✓ Created symlink: node_modules/@4runr/shared -> ../../packages/shared"; \
    fi
# Verify the shared package's package.json exists (for ESM resolution)
RUN test -f node_modules/@4runr/shared/package.json && echo "✓ @4runr/shared/package.json exists" || echo "✗ @4runr/shared/package.json missing"
# Verify the package.json has the correct exports field for ESM resolution
RUN node -e "try { const pkg = require('./node_modules/@4runr/shared/package.json'); if (pkg.exports && pkg.exports['.']) { console.log('✓ package.json exports field configured correctly'); } else { console.error('✗ package.json missing exports field'); process.exit(1); } } catch(e) { console.error('✗ Error reading shared package.json:', e.message); process.exit(1); }"
# Verify Sentinel package is accessible
RUN test -d packages/sentinel/dist && echo "✓ sentinel/dist exists" || echo "✗ sentinel/dist missing"
RUN test -f packages/sentinel/dist/index.js && echo "✓ sentinel/dist/index.js exists" || echo "✗ sentinel/dist/index.js missing"
# Check if @4runr/sentinel exists and if it's a symlink
# If not, create it (symlinks don't need special ownership)
RUN if [ ! -L node_modules/@4runr/sentinel ] && [ ! -d node_modules/@4runr/sentinel ]; then \
        echo "✗ @4runr/sentinel NOT found in node_modules, creating symlink..."; \
        mkdir -p node_modules/@4runr && \
        ln -s ../../packages/sentinel node_modules/@4runr/sentinel && \
        echo "✓ Created symlink: node_modules/@4runr/sentinel -> ../../packages/sentinel"; \
    fi
# Verify the Sentinel package's package.json exists (for ESM resolution)
RUN test -f node_modules/@4runr/sentinel/package.json && echo "✓ @4runr/sentinel/package.json exists" || echo "✗ @4runr/sentinel/package.json missing"
# Verify the package.json has the correct exports field for ESM resolution
RUN node -e "try { const pkg = require('./node_modules/@4runr/sentinel/package.json'); if (pkg.exports && pkg.exports['.']) { console.log('✓ Sentinel package.json exports field configured correctly'); } else { console.error('✗ Sentinel package.json missing exports field'); process.exit(1); } } catch(e) { console.error('✗ Error reading Sentinel package.json:', e.message); process.exit(1); }"
# Verify fastify exists (should be directly in node_modules with hoisted node-linker)
RUN test -d node_modules/fastify && echo "✓ fastify found in node_modules" || \
    (echo "✗ fastify NOT found!" && ls -la node_modules/ | head -40)
RUN test -f node_modules/fastify/package.json && echo "✓ fastify package.json exists" || echo "✗ fastify package.json missing"
# Test Node.js ESM resolution from the gateway dist directory
RUN cd apps/gateway/dist && \
    node --input-type=module -e "import('fastify').then(() => console.log('✓ Node.js can resolve fastify from dist')).catch(e => console.error('✗ Cannot resolve:', e.message))" || \
    echo "Testing from workspace root:" && \
    cd /workspace && \
    node --input-type=module -e "import('fastify').then(() => console.log('✓ Node.js can resolve fastify from root')).catch(e => console.error('✗ Cannot resolve from root:', e.message))"
USER node
WORKDIR /workspace
# Run from workspace root - Node.js ESM should find node_modules at /workspace/node_modules
CMD ["node", "apps/gateway/dist/apps/gateway/src/index.js"]
