import express from 'express';
import helmet from 'helmet';
import cors from 'cors';
import rateLimit from 'express-rate-limit';
import swaggerUi from 'swagger-ui-express';
import routes from './routes/index.js';
import errorHandler from './middlewares/errorHandler.js';
import requestLogger from './middlewares/requestLogger.js';
import requestId from './middlewares/requestId.js';
import docsAuth from './middlewares/docsAuth.js';
import { openapiSpec } from './docs/openapiSpec.js';
import { errorResponse } from './utils/response.js';
import { env } from './config/env.js';

const app = express();

// Behind a reverse proxy/load balancer, this makes req.ip and rate limiting
// use the real client IP from X-Forwarded-For instead of the proxy's IP.
if (env.TRUST_PROXY) app.set('trust proxy', 1);

// First: every request gets a correlation id before anything else runs,
// so it's available to the logger and to every downstream handler.
app.use(requestId);

// Logged next so every request is recorded (ip, url, status), even ones
// blocked below by CORS or the rate limiter.
app.use(requestLogger);

app.disable('x-powered-by');
app.use(helmet());

// No origin is allowed by default; set CORS_ORIGIN to a comma-separated
// allowlist to permit specific frontends to call this API cross-origin.
const allowedOrigins = env.CORS_ORIGIN.split(',').map((origin) => origin.trim()).filter(Boolean);
app.use(cors({
  origin: allowedOrigins.length ? allowedOrigins : false,
  // Custom response headers are invisible to browser JS unless listed here —
  // needed so a frontend can read the tokens signup/login return via headers.
  exposedHeaders: ['X-Request-Id', 'X-Id-Token', 'X-Refresh-Token'],
}));

app.use(rateLimit({
  windowMs: env.RATE_LIMIT_WINDOW_MS,
  max: env.RATE_LIMIT_MAX,
  standardHeaders: true,
  legacyHeaders: false,
  handler: (_req, res) => void errorResponse(res, 'Too many requests, please try again later', 429),
}));

// Docs are credential-gated (docsAuth) and get their own relaxed CSP —
// Swagger UI's bundled JS/CSS needs inline scripts/styles the default
// helmet() policy above blocks. This overrides only the headers on /docs.
app.use('/docs', docsAuth, helmet({
  contentSecurityPolicy: {
    directives: {
      ...helmet.contentSecurityPolicy.getDefaultDirectives(),
      'script-src': ["'self'", "'unsafe-inline'"],
      'style-src': ["'self'", "'unsafe-inline'"],
    },
  },
}), swaggerUi.serve, swaggerUi.setup(openapiSpec));

app.use(express.json({ limit: '1mb' }));
app.use('/api/v1', routes);
app.use((_req, res) => void errorResponse(res, 'Route not found', 404));
app.use(errorHandler);

export default app;
