import "reflect-metadata";
import { NestFactory } from "@nestjs/core";
import helmet from "helmet";
import { Pool } from "pg";
import { Client } from "@opensearch-project/opensearch";
import { AppModule } from "./http/app.module";
import { OrderService } from "./core";
import { ErrorFilter } from "./http/http";
import { loadConfig } from "./config";
import { ensureIndex, IndexWorker } from "./infra/opensearch";
import { PgProducts } from "./infra/pg";

async function bootstrap() {
  const cfg = loadConfig();
  const app = await NestFactory.create(AppModule, { rawBody: true }); // rawBody: o webhook de pagamento confere a assinatura sobre o corpo original
  if (cfg.trustProxy) (app.getHttpAdapter().getInstance() as any).set("trust proxy", 1); // atrás de proxy/balanceador, para o IP real
  app.use(helmet());
  app.use((req: any, res: any, next: () => void) => { if (req.headers["content-length"] > 100_000) return res.status(413).json({ error: { code: "TOO_LARGE", message: "Requisição grande demais." } }); next(); });
  app.enableCors({ origin: cfg.corsOrigins.length ? cfg.corsOrigins : false, methods: ["GET", "POST", "PATCH"], allowedHeaders: ["Content-Type", "Authorization"], maxAge: 600 });
  app.setGlobalPrefix("v1"); app.useGlobalFilters(new ErrorFilter()); app.enableShutdownHooks();
  const os = new Client({ node: cfg.opensearchUrl }); await ensureIndex(os);
  // worker de indexação no mesmo processo (em produção pode rodar em um processo separado)
  const db = new Pool({ connectionString: cfg.databaseUrl, max: 2 }); const worker = new IndexWorker(db, os, new PgProducts(db));
  const t: any = setInterval(() => worker.drain().catch(() => {}), 2000); t.unref?.();
  // cancela pedidos cujo Pix/boleto venceu e devolve o estoque
  const orders = app.get(OrderService); const t2: any = setInterval(() => orders.expireUnpaid().catch(() => {}), 60_000); t2.unref?.();
  await app.listen(cfg.port, "0.0.0.0");
}
bootstrap().catch((e) => { console.error(e); process.exit(1); });
