import { Request, Response, NextFunction } from "express";
import mongoose, { ClientSession } from "mongoose";
import { AsyncHandlerOptions, AsyncFn } from "../types/sharedTypes";
import { MongoHandledError } from "../utils/mongoErrorHandler";

export const asyncHandler = (
  fn: AsyncFn,
  options: AsyncHandlerOptions = {}
): AsyncFn => {
  const { useTransaction = false, customErrorHandler } = options;

  return async (
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> => {
    let session: ClientSession | null = null;

    try {
      if (useTransaction) {
        session = await mongoose.startSession();
        session.startTransaction();
        req.session = session;
      }

      await fn(req, res, next);

      if (session) {
        await session.commitTransaction();
        session.endSession();
      }
    } catch (error: any) {
      console.error("❌ Error caught in asyncHandler:", error);

      if (session) {
        try {
          await session.abortTransaction();
          session.endSession();
        } catch (abortErr) {
          console.error("⚠️ Failed to abort transaction:", abortErr);
        }
      }

      // Wrap known MongoDB errors
      if (
        error.name === "MongoServerError" ||
        error.name === "MongoError" ||
        error.name === "CastError" ||
        error.name === "ValidationError"
      ) {
        error = new MongoHandledError(error);
      }

      // 🔁 Support nested asyncHandlers
      if (typeof customErrorHandler === "function") {
        try {
          const result = customErrorHandler(error, req, res, next);
          if (result instanceof Promise) {
            await result;
          }
        } catch (customHandlerError) {
          console.error("⚠️ Error inside customErrorHandler:", customHandlerError);
        }
      }

      return next({
        message: error?.message || "Internal Server Error",
        statusCode: error?.statusCode || 500,
      });
    }
  };
};
