import { ValidationPipe, VersioningType } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { ExpressAdapter } from "@nestjs/platform-express";
import { IoAdapter } from "@nestjs/platform-socket.io";
import { createServer } from "node:net";
import helmet from "helmet";
import { AppModule } from "./app.module";
import { ResponseTransformInterceptor } from "./common/interceptors/response.interceptor";
import { AllExceptionsFilter } from "./common/filters/all-exceptions.filter";
import { validateCorsOrigin } from "./common/security/origins";

async function portAvailable(port: number) {
  return new Promise<boolean>((resolveCheck) => {
    const probe = createServer();
    probe.unref();
    probe.once("error", () => resolveCheck(false));
    probe.listen(port, "0.0.0.0", () => probe.close(() => resolveCheck(true)));
  });
}

async function selectPort(preferredPort: number) {
  const managedRuntime = process.env.NODE_ENV === "production"
    || process.env.MAWJOD_ENV === "production"
    || Boolean(process.env.PASSENGER_APP_ENV || process.env.PASSENGER_NODEJS);
  if (managedRuntime) return preferredPort;
  for (let port = preferredPort; port < preferredPort + 100 && port <= 65535; port += 1) {
    if (await portAvailable(port)) return port;
  }
  throw new Error(`No available API port was found from ${preferredPort}.`);
}

async function bootstrap() {
  const app = await NestFactory.create(AppModule, new ExpressAdapter(), { logger: ["error", "warn", "log", "debug"] });
  const preferredPort = Number(process.env.PORT || 3001);
  if (!Number.isInteger(preferredPort) || preferredPort < 1 || preferredPort > 65535) throw new Error("PORT must be a valid TCP port.");
  const port = await selectPort(preferredPort);
  const trustProxyHops = Number(process.env.TRUST_PROXY_HOPS ?? 0);
  if (Number.isInteger(trustProxyHops) && trustProxyHops > 0) {
    app.getHttpAdapter().getInstance().set("trust proxy", trustProxyHops);
  }
  app.useWebSocketAdapter(new IoAdapter(app));

  app.enableCors({
    origin: validateCorsOrigin,
    methods: ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"],
    credentials: false,
    allowedHeaders: ["Authorization", "Content-Type"],
    maxAge: 600,
  });
  app.use(helmet({ crossOriginResourcePolicy: { policy: "cross-origin" } }));
  app.setGlobalPrefix("api");
  app.enableVersioning({ type: VersioningType.URI });
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      transform: true,
      forbidNonWhitelisted: true,
    })
  );
  app.useGlobalFilters(new AllExceptionsFilter());
  app.useGlobalInterceptors(new ResponseTransformInterceptor());

  await app.listen(port, "0.0.0.0");
  if (port !== preferredPort) console.warn(`[API] Port ${preferredPort} is occupied. Using port ${port}.`);
  console.log(`[API] Listening on port ${port}`);
  return port;
}

export const applicationReady = bootstrap();
