# syntax=docker/dockerfile:1.7

ARG NODE_IMAGE=node:24.18.0-bookworm-slim


# ============================================================
# 1. Dependencias
# ============================================================
FROM ${NODE_IMAGE} AS deps

WORKDIR /app

ARG DEPS_CACHE_REV=20260724-3

ENV NODE_ENV=development \
    NEXT_TELEMETRY_DISABLED=1 \
    NPM_CONFIG_REGISTRY=https://registry.npmjs.org/ \
    NPM_CONFIG_REPLACE_REGISTRY_HOST=always \
    NPM_CONFIG_PACKAGE_LOCK=true \
    NPM_CONFIG_PACKAGE_LOCK_ONLY=false \
    NPM_CONFIG_DRY_RUN=false \
    NPM_CONFIG_GLOBAL=false \
    NPM_CONFIG_AUDIT=false \
    NPM_CONFIG_FUND=false \
    NPM_CONFIG_FETCH_RETRIES=2 \
    NPM_CONFIG_FETCH_RETRY_MINTIMEOUT=5000 \
    NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT=30000 \
    NPM_CONFIG_FETCH_TIMEOUT=120000 \
    NPM_CONFIG_MAXSOCKETS=8 \
    NPM_CONFIG_LOGLEVEL=info

COPY package.json package-lock.json .npmrc ./


# ============================================================
# Normalizar registries grabados en package-lock.json
# ============================================================
RUN node <<'NODE'
const fs = require("node:fs");

const filename = "package-lock.json";
const publicRegistry = "https://registry.npmjs.org/";

let content = fs.readFileSync(filename, "utf8");
let replacements = 0;

const internalRegistryPatterns = [
  /https?:\/\/packages\.applied-caas-gateway1\.internal\.api\.openai\.org\/artifactory\/api\/npm\/npm-public\//g,
];

for (const pattern of internalRegistryPatterns) {
  content = content.replace(pattern, () => {
    replacements += 1;
    return publicRegistry;
  });
}

fs.writeFileSync(
  filename,
  `${content.trimEnd()}\n`,
  "utf8"
);

const normalizedContent = fs.readFileSync(
  filename,
  "utf8"
);

const forbiddenHosts = [
  "internal.api.openai.org",
  "packages.applied-caas-gateway1",
];

const detectedHosts = forbiddenHosts.filter(
  (host) => normalizedContent.includes(host)
);

if (detectedHosts.length > 0) {
  console.error(
    `ERROR: registries no permitidos: ${detectedHosts.join(", ")}`
  );

  process.exit(1);
}

const packageJson = JSON.parse(
  fs.readFileSync("package.json", "utf8")
);

const lockfile = JSON.parse(normalizedContent);

const productionDependencies = Object.keys(
  packageJson.dependencies || {}
);

const developmentDependencies = Object.keys(
  packageJson.devDependencies || {}
);

const declaredDependencies =
  productionDependencies.length +
  developmentDependencies.length;

const lockedPackages = Object.keys(
  lockfile.packages || {}
).length;

console.log(
  `Proyecto: ${packageJson.name}`
);

console.log(
  `Dependencias production: ${productionDependencies.length}`
);

console.log(
  `Dependencias development: ${developmentDependencies.length}`
);

console.log(
  `Dependencias declaradas: ${declaredDependencies}`
);

console.log(
  `Entradas package-lock: ${lockedPackages}`
);

console.log(
  `Referencias normalizadas: ${replacements}`
);

if (declaredDependencies === 0) {
  console.error(
    "ERROR: package.json no contiene dependencias."
  );

  process.exit(1);
}

if (lockedPackages <= 1) {
  console.error(
    "ERROR: package-lock.json no contiene el árbol de dependencias."
  );

  process.exit(1);
}
NODE


# ============================================================
# Validar configuración efectiva de npm
# ============================================================
RUN echo "DEPS_CACHE_REV=${DEPS_CACHE_REV}" \
    && node --version \
    && npm --version \
    && echo "registry=$(npm config get registry)" \
    && echo "package-lock=$(npm config get package-lock)" \
    && echo "package-lock-only=$(npm config get package-lock-only)" \
    && echo "dry-run=$(npm config get dry-run)" \
    && echo "global=$(npm config get global)" \
    && echo "omit=$(npm config get omit)" \
    && npm ping


# ============================================================
# Instalar dependencias
# ============================================================
RUN --mount=type=cache,target=/root/.npm,sharing=private <<'SHELL'
set -eu

echo "=================================================="
echo "Instalando dependencias de ARI Shopping"
echo "=================================================="

rm -rf /app/node_modules

timeout \
  --signal=TERM \
  --kill-after=30s \
  900s \
npm ci \
  --include=prod \
  --include=dev \
  --include=optional \
  --package-lock=true \
  --package-lock-only=false \
  --dry-run=false \
  --global=false \
  --no-audit \
  --no-fund \
  --prefer-online \
  --foreground-scripts \
  --loglevel=info \
  --timing

echo
echo "=================================================="
echo "Validando node_modules"
echo "=================================================="

test -d /app/node_modules || {
  echo "ERROR: npm ci no creó /app/node_modules."
  exit 1
}

test -f /app/node_modules/next/package.json || {
  echo "ERROR: Next.js no fue instalado."
  exit 1
}

test -f /app/node_modules/react/package.json || {
  echo "ERROR: React no fue instalado."
  exit 1
}

test -f /app/node_modules/typescript/package.json || {
  echo "ERROR: TypeScript no fue instalado."
  exit 1
}

node -e "
  console.log(
    'Next.js:',
    require('./node_modules/next/package.json').version
  )
"

node -e "
  console.log(
    'React:',
    require('./node_modules/react/package.json').version
  )
"

node -e "
  console.log(
    'TypeScript:',
    require('./node_modules/typescript/package.json').version
  )
"

du -sh /app/node_modules
SHELL


# ============================================================
# 2. Compilación Next.js
# ============================================================
FROM ${NODE_IMAGE} AS builder

WORKDIR /app

ENV NODE_ENV=production \
    NEXT_TELEMETRY_DISABLED=1 \
    NODE_OPTIONS=--max-old-space-size=2048

COPY --from=deps \
     /app/node_modules \
     ./node_modules

COPY package.json package-lock.json ./

COPY . .

RUN test -d /app/node_modules \
    && test -f /app/node_modules/next/package.json \
    && test -f /app/node_modules/react/package.json

RUN --mount=type=cache,target=/app/.next/cache,sharing=private \
    npm run build

RUN test -f /app/.next/standalone/server.js \
    && test -d /app/.next/static


# ============================================================
# 3. Runtime de producción
# ============================================================
FROM ${NODE_IMAGE} AS runner

WORKDIR /app

ENV DEBIAN_FRONTEND=noninteractive \
    NODE_ENV=production \
    NEXT_TELEMETRY_DISABLED=1 \
    HOSTNAME=0.0.0.0 \
    PORT=80

RUN apt-get update \
    && apt-get install -y \
       --no-install-recommends \
       ca-certificates \
       libcap2-bin \
    && rm -rf /var/lib/apt/lists/* \
    && groupadd \
       --gid 1001 \
       nodejs \
    && useradd \
       --uid 1001 \
       --gid 1001 \
       --create-home \
       --shell /usr/sbin/nologin \
       nextjs \
    && setcap \
       'cap_net_bind_service=+ep' \
       /usr/local/bin/node

COPY --from=builder \
     --chown=nextjs:nodejs \
     /app/public \
     ./public

COPY --from=builder \
     --chown=nextjs:nodejs \
     /app/.next/standalone \
     ./

COPY --from=builder \
     --chown=nextjs:nodejs \
     /app/.next/static \
     ./.next/static

USER nextjs

EXPOSE 80

HEALTHCHECK --interval=30s --timeout=8s --start-period=30s --retries=3 CMD ["node","-e","fetch('http://127.0.0.1/api/health').then((response) => process.exit(response.ok ? 0 : 1)).catch(() => process.exit(1))"]

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