import express from "express";
import cors from "cors";
import helmet from "helmet";
import compression from "compression";
import cookieParser from "cookie-parser";
import pinoHttp from "pino-http";
import pino from "pino";
import hpp from "hpp";
import { env } from "./config/env";
import { logger } from "./common/utils/logger";
import { errorMiddleware } from "./common/middlewares/error.middleware";
import { notFoundMiddleware } from "./common/middlewares/not-found.middleware";
import { generalLimiter } from "./common/middlewares/rate-limit.middleware";
import { sendSuccess } from "./common/utils/api-response";
import apiRoutes from "./routes";
import swaggerRoutes from "./infrastructure/openapi/swagger.routes";
import { registry } from "./infrastructure/openapi/registry";
import { z } from "zod";

export const app = express();

// const corsOptions = {
//   origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => {
//     console.log("[CORS Check] Request Origin:", origin);
//     if (!origin) return callback(null, true);
//     return callback(null, true);
//   },
//   credentials: true,
//   methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
//   allowedHeaders: [
//     "Content-Type",
//     "Authorization",
//     "X-Requested-With",
//     "Accept",
//   ],
//   optionsSuccessStatus: 200,
// };

app.use(cors());
// app.options("*", cors());

// 2. Helmet Security Headers (Configured for Cross-Origin resource access)
app.use(
  helmet({
    crossOriginResourcePolicy: { policy: "cross-origin" },
    contentSecurityPolicy: {
      directives: {
        ...helmet.contentSecurityPolicy.getDefaultDirectives(),
        "script-src": ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net"],
        "style-src": ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net"],
        "img-src": ["'self'", "data:", "https://fastly.jsdelivr.net"],
      },
    },
  }),
);
app.use(compression());
app.use(cookieParser());
// Raw body for Stripe webhook, JSON for everything else
app.use((req, res, next) => {
  if (req.originalUrl === "/api/v1/payments/webhook") {
    express.raw({ type: "application/json" })(req, res, next);
  } else {
    express.json()(req, res, next);
  }
});
app.use(express.urlencoded({ extended: true }));
app.use(hpp());

app.use(
  pinoHttp({
    logger,
    serializers: {
      req: (req: any) => {
        return {
          id: req.id,
          method: req.method,
          url: req.url,
          query: req.raw?.query || req.query,
          params: req.raw?.params || req.params,
          remoteAddress: req.remoteAddress,
          remotePort: req.remotePort,
        };
      },
      res: (res: any) => {
        const error = res.raw?.error || res.error;
        const errorCode = res.raw?.errorCode || res.errorCode;
        const details = res.raw?.details || res.details;
        const message = res.raw?.message || res.message;

        return {
          statusCode: res.statusCode,
          ...(message && { message }),
          ...(error && { error }),
          ...(errorCode && { errorCode }),
          ...(details && { details }),
        };
      },
    },
  }),
);

// Register health route in OpenAPI
registry.registerPath({
  method: "get",
  path: "/health",
  summary: "Health check endpoint",
  description: "Returns backend server status and operational state.",
  tags: ["System"],
  servers: [{ url: "/", description: "Root Server" }],
  responses: {
    200: {
      description: "Server is healthy",
      content: {
        "application/json": {
          schema: z.object({
            success: z.boolean().default(true),
            statusCode: z.number().default(200),
            message: z.string().default("Backend is healthy"),
            data: z.object({ status: z.string().default("ok") }),
          }),
        },
      },
    },
  },
});
app.get("/health", (_req, res) =>
  sendSuccess(res, { status: "ok" }, 200, "Backend is healthy"),
);
app.get("/api/v1/health", (_req, res) =>
  sendSuccess(res, { status: "ok" }, 200, "Backend is healthy"),
);

// Mount Swagger documentation endpoint
app.use("/api/docs", swaggerRoutes);

app.use("/api/v1", generalLimiter, apiRoutes);

app.use(notFoundMiddleware);
app.use(errorMiddleware); // must be last
