import { Request, Response, NextFunction } from "express";
import * as loggers from "../common/logger";

/**
 * Wrapper de middleware para manejar funciones asíncronas en Express.
 *
 * Intercepta el método `res.send` para registrar cada respuesta con su
 * método HTTP, URL, código de estado y mensaje. Los errores (statusCode >= 400)
 * se registran como `error`; el resto como `info`.
 *
 * Cualquier excepción lanzada por el handler asíncrono `fn` se captura
 * automáticamente y se pasa a `next(err)` para que el errorHandler global
 * la procese.
 *
 * @param fn - Función de controlador asíncrona `(req, res, next) => Promise<void>`
 * @returns Middleware de Express que envuelve `fn` con manejo de errores y logging
 *
 * @example
 * router.get("/users", asyncHandler(async (req, res) => {
 *   const users = await UserService.findAll();
 *   res.json(users);
 * }));
 */
const asyncHandler =
  (fn: Function) => (req: Request, res: Response, next: NextFunction) => {
    const originalSend = res.send;

    // Sobreescribimos res.send para interceptar cada respuesta saliente
    // y registrarla en los logs antes de enviarla al cliente.
    res.send = function (data: any) {
      const statusCode = res.statusCode;
      const method = req.method;
      const url = req.originalUrl;
      const timestamp = new Date().toISOString();

      // Extraer el mensaje de la respuesta (puede ser string JSON u objeto)
      let message = "";
      if (typeof data === "string") {
        try {
          const parsed = JSON.parse(data);
          message = parsed.message || "";
        } catch {
          message = data;
        }
      } else if (typeof data === "object" && data?.message) {
        message = data.message;
      }

      const logMessage = `${method} ${url} - ${statusCode}${
        message ? ` - ${message}` : ""
      }`;

      // Registrar como error si el código de estado indica fallo (4xx / 5xx)
      if (statusCode >= 400) {
        //console.error(logMessage);
        loggers.error(logMessage);
      } else {
        //console.log(logMessage);
        loggers.info(logMessage);
      }
      return originalSend.call(this, data);
    };

    // Ejecutar el handler y redirigir cualquier rechazo de promesa al next de Express
    Promise.resolve(fn(req, res, next)).catch(next);
  };

export default asyncHandler;
