import { Request, Response } from "express";
import Operation from "../models/operation";
import asyncHandler from "../common/asyncHandler";
import { Types } from "mongoose";
import * as env from "../config/env.config";
import OperationPayment from "../models/operationPayment";
import { getRateS } from "../services/rateService";
import {
  getDaysDifference,
  getOperationPaymentStatusName,
  getOperationStatusName,
  startOfToday,
} from "../common/helper";
import Config from "../models/config";
import { cargoEnCuentaLink } from "../services/banescoService";
import { formatInTimeZone } from "date-fns-tz";
import {
  payDebtConfirmationValidationSchema,
  payDebtValidationSchema,
} from "../validations/operation/payDebt";
import Payment from "../models/payment";
import User from "../models/user";
import Level from "../models/level";
import LevelHistory from "../models/levelHistory";
import { registerBalance } from "../services/balanceService";
import { requestOperationValidationSchema } from "../validations/operation/requestOperation";
import { icons } from "../config/data/icons";
import {
  credit,
  debitOtp,
  getRejectedCodeDescription,
  getStatusDescription,
  getTransaction,
  getTransactionResult,
  requestOtp,
  TransactionStatusCode,
} from "../services/sypagoService";
import { findPaymentMobile } from "../services/bancamigaService";
import { requestOtpValidationSchema } from "../validations/operation/requestOtp";
import Balance from "../models/balance";

/**
 * Request operation
 *
 * @exports
 * @async
 * @param {Request} req
 * @param {Response} res
 * @returns {unknown}
 */
export const requestOperation = asyncHandler(
  async (req: Request, res: Response) => {
    const { body } = req;

    const { error, value } = requestOperationValidationSchema.validate(body, {
      abortEarly: false,
    });

    if (error) {
      return res.status(400).json({
        success: false,
        message: `Error de validación: ${error.details
          .map((d) => d.message)
          .join(", ")}`,
        code: "field_missing",
      });
    }

    // if (body.isThirdParty && body.currency === "USD") {
    //   return res.status(400).json({
    //     success: false,
    //     message:
    //       "Para realizar un pago a terceros debes colocar el monto a solicitar en Bolívares",
    //     code: "usd_not_allowed",
    //   });
    // }

    // let account;
    // if (!body.isThirdParty) {
    //   account = await Account.findOne({
    //     user: req.user!.id,
    //     _id: body.account,
    //   })
    //     .lean()
    //     .exec();

    //   if (!account) {
    //     return res.status(400).json({
    //       success: false,
    //       message: "No se encontró la cuenta",
    //       code: "not_found",
    //     });
    //   }
    // }

    const config = await Config.findOne({ key: "credit-active" }).lean();
    if (!config?.value) {
      return res.status(400).json({
        success: false,
        message:
          "El sistema de generación de operaciones de crédito no está activo en este momento. Por favor, intente mas tarde",
        code: "not_active",
      });
    }

    const user = await User.findById(req.user!.id);
    if (!user) {
      return res.status(400).json({
        success: false,
        message: "No se encontró información del cliente",
        code: "not_found",
      });
    }

    let amountUsd, amountVef;
    const rate = await getRateS();
    const rateUsd = parseFloat(rate!.usd.toFixed(2));

    if (body.currency === "USD") {
      amountUsd = parseFloat(body.amount.toFixed(2));
      amountVef = parseFloat((body.amount * rateUsd).toFixed(2));
    } else {
      amountUsd = parseFloat((body.amount / rateUsd).toFixed(2));
      amountVef = parseFloat(body.amount.toFixed(2));
    }

    const checkMinBalanceResult = await checkMinBalance(amountVef);
    if (!checkMinBalanceResult) {
      return res.status(400).json({
        success: false,
        message:
          "En estos momentos no podemos procesar su solicitud de crédito. Por favor, intente más tarde",
      });
    }

    if (amountUsd > user.maxAmount && !env.TESTING) {
      return res.status(400).json({
        success: false,
        message:
          "La cantidad solicitada excede el máximo permitido para el usuario",
        code: "max_amount_exceeded",
      });
    }
    if (!user.allowedFeeCount.includes(body.feeCount)) {
      return res.status(400).json({
        success: false,
        message: "El número de cuotas es inválido",
        code: "fee_count_invalid",
      });
    }

    const amountPending = await OperationPayment.aggregate([
      {
        $match: {
          user: new Types.ObjectId(req.user!.id),
          status: { $nin: ["void", "paid"] },
        },
      },
      {
        $group: {
          _id: null, // Agrupa todos los documentos en un solo grupo para sumar todos los montos
          totalAmount: { $sum: "$amountUsd" }, // Calcula la suma de amountUsd
        },
      },
    ]).exec();

    const totalPendingAmount =
      amountPending.length > 0 ? amountPending[0].totalAmount : 0;

    if (totalPendingAmount + amountUsd > user.maxAmount) {
      return res.status(400).json({
        success: false,
        message:
          "La cantidad solicitada excede el máximo permitido para el usuario",
        code: "max_amount_exceeded",
      });
    }

    const annualCommission = 60;
    const periodCommission = annualCommission / 12;
    const commissionAmount = parseFloat(
      ((amountVef * body.feeCount * periodCommission) / 100).toFixed(2)
    );
    const settledAmount = parseFloat((amountVef - commissionAmount).toFixed(2));

    const expireAt = new Date();
    expireAt.setSeconds(expireAt.getSeconds() + env.OPERATION_EXPIRE_AT);

    const operationData: any = {
      user: req.user!.id,
      currency: body.currency,
      amountVef: amountVef,
      rate: rateUsd,
      amountUsd: amountUsd,
      annualCommission: annualCommission,
      commissionAmount: commissionAmount,
      settledAmount: settledAmount,
      feeCount: body.feeCount,
      isThirdParty: body.isThirdParty,
      //beneficiary: body.isThirdParty ? body.beneficiary : undefined,
      beneficiary: body.beneficiary,
      status: "void",
      expireAt: expireAt,
      // account: !body.isThirdParty
      //   ? {
      //       bank: account!.bank,
      //       type: account!.type,
      //       number: account!.number,
      //     }
      //   : undefined,
    };

    //let session: ClientSession | null = null;
    // try {
    //   // 1. Iniciar una sesión de transacción
    //   session = await startSession();
    //   session.startTransaction();

    // } catch (error) {
    //   console.error("Error durante la transacción:", error);
    //   // Si hay un error, abortar la transacción para revertir los cambios
    //   if (session) {
    //     await session.abortTransaction();
    //     session.endSession();
    //   }
    //   return res.status(500).json({
    //     success: false,
    //     message: "Error creando operacion",
    //     code: "error",
    //     error: error,
    //   });
    // }

    const operationPayments = [];
    const feeAmount = parseFloat(
      (operationData.amountUsd / operationData.feeCount).toFixed(2)
    );

    for (let i = 0; i < body.feeCount; i++) {
      const feeDate = new Date(); // fresh instance
      feeDate.setHours(0, 0, 0, 0);
      feeDate.setMonth(feeDate.getMonth() + i + 1);
      const operationPaymentData = {
        date: feeDate,
        amountUsd: feeAmount,
        status: "void",
        user: req.user!.id,
        expireAt: expireAt,
      };

      const operationPayment = new OperationPayment(operationPaymentData);
      const savedOperationPayment = await operationPayment.save();
      operationPayments.push(savedOperationPayment);
    }

    operationData.paymentPlan = operationPayments.map(
      (operationPayment) => operationPayment._id
    );

    const operation = new Operation(operationData);
    const savedOperation = await operation.save();

    operationData.id = savedOperation._id;

    // operationPayments.forEach(
    //   async (operationPayment) => await OperationPayment.updateOne({ _id: operationPayment._id }, { $set: { operation: savedOperation._id } }, { session: session ?? undefined })
    // );
    for (const op of operationPayments) {
      await OperationPayment.updateOne(
        { _id: op._id },
        { $set: { operation: savedOperation._id } }
      );
    }

    // 5. Si todo ha ido bien, confirmar la transacción
    // await session.commitTransaction();

    const respData = {
      id: operationData.id,
      currency: operationData.currency,
      amountVef: operationData.amountVef,
      rate: operationData.rate,
      amountUsd: operationData.amountUsd,
      feeCount: operationData.feeCount,
      isThirdParty: operationData.isThirdParty,
      beneficiary: operationData.beneficiary,
      account: operationData.account,
      paymentPlan: operationPayments.map((operationPayment) => ({
        date: operationPayment.date,
        amount: operationPayment.amountUsd,
      })),
    };

    return res.status(201).json({
      success: true,
      message: "Operation created successfully",
      data: respData,
    });
  }
);

/**
 * Confirm operation
 *
 * @exports
 * @async
 * @param {Request} req
 * @param {Response} res
 * @returns {unknown}
 */
export const confirmOperation = asyncHandler(
  async (req: Request, res: Response) => {
    const { body } = req;
    if (!body.operationId) {
      return res.status(400).json({
        success: false,
        message: "Campo 'operationId' es requerido",
        code: "field_missing",
      });
    }

    const operation = await Operation.findById(body.operationId).populate(
      "user"
    );
    if (!operation) {
      return res.status(404).json({
        success: false,
        message: "No se encontró la operación",
        code: "not_found",
      });
    }

    if (operation.status !== "void") {
      return res.status(400).json({
        success: false,
        message: "La operación tiene estatus no válido",
        code: "invalid_status",
      });
    }

    const checkMinBalanceResult = await checkMinBalance(operation.amountVef);
    if (!checkMinBalanceResult) {
      return res.status(400).json({
        success: false,
        message:
          "En estos momentos no podemos procesar su solicitud de crédito. Por favor, intente más tarde",
      });
    }

    const zonaHorariaVenezuela = "America/Caracas";
    // Define el formato de salida deseado
    const formatoSalida = "yyy-MM-dd HH:mm:ss";
    // Convertir la fecha y la hora actuales en el formato deseado
    //const fechaActual = new Date();
    const fechaActualFormateada = formatInTimeZone(
      operation.createdAt,
      zonaHorariaVenezuela,
      formatoSalida
    );

    // const banescoVueltoP2PBody: banescoVueltoP2P = {
    //   dataRequest: {
    //     device: {
    //       type: body.deviceType,
    //       description: body.deviceDescription,
    //       ipAddress: body.ipAddress,
    //       sid: uuidv4(),
    //     },
    //     p2p: {
    //       accountFrom: {
    //         accountId: "",
    //       },
    //       accountTo: {
    //         bankId: operation.beneficiary?.bankCode ?? "0134",
    //         customerId:
    //           (operation.beneficiary?.identificationType ?? "V") +
    //           (operation.beneficiary?.identificationNumber ?? "13873278"),
    //         phoneNum: operation.beneficiary?.phone ?? "584169619039",
    //       },
    //       //amount: operation.settledAmount,
    //       amount: 9,
    //       paymentId: generarNumeroAleatorio14Digitos().toString(),
    //       concept: "Pago movil",
    //       trnDate: fechaActualFormateada.split(" ")[0],
    //       trnTime: fechaActualFormateada.split(" ")[1],
    //     },
    //   },
    // };

    // const transaction = await vueltoP2p(banescoVueltoP2PBody);

    const now = new Date();
    const todayOperationsCount = await Operation.countDocuments({
      status: "approved",
      createdAt: {
        $gte: new Date(now.getFullYear(), now.getMonth(), now.getDate()),
      },
    });

    const diferenciaMilisegundos = Math.abs(
      now.getTime() - new Date(1983, 11, 30).getTime()
    );
    const milisegundosPorDia = 1000 * 60 * 60 * 24;
    const diferenciaDias = Math.floor(
      diferenciaMilisegundos / milisegundosPorDia
    );

    const operationCount = await Operation.countDocuments({
      user: operation.user,
      status: { $nin: ["void", "rejected"] },
    });

    const reference = `${diferenciaDias}${String(
      todayOperationsCount + 1
    ).padStart(5, "0")}`;

    const sypagoCreditBody = {
      internal_id: reference,
      //group_id: generarNumeroAleatorio14Digitos().toString(),
      account: {
        bank_code: env.SYPAGO_BANK_ACCOUNT_CODE!,
        type: "CNTA",
        number: env.SYPAGO_BANK_ACCOUNT_NUMBER!,
      },
      sub_product: "222",
      amount: {
        //amt: 2,
        amt: operation.settledAmount,
        currency: "VES",
      },
      notification_urls: {
        web_hook_endpoint: env.SYPAGO_WEBHOOK_URL!,
      },
      receiving_user: {
        name: "Test User",
        document_info: {
          type: "V",
          number: operation.beneficiary?.identificationNumber,
        },
        account: {
          bank_code: operation.beneficiary?.bankCode,
          type: "CELE",
          number: operation.beneficiary?.phone,
        },
      },
      concept: "Pago movil",
    };

    const creditTransaction = await credit(sypagoCreditBody);

    if (!creditTransaction.success) {
      return res.status(400).json({
        success: false,
        message: "Error al generar la transacción",
        code: "error",
        error: creditTransaction.message,
      });
    }

    const transaction = await getTransactionResult(
      creditTransaction.data.transaction_id
    );

    if (!transaction.success) {
      return res.status(400).json({
        success: false,
        message: "Error al obtener la transacción",
        code: "error",
        error: transaction.message,
      });
    }

    if (transaction.data.status !== TransactionStatusCode.ACCP) {
      return res.status(400).json({
        success: false,
        message:
          "La transacción no está aprobada - " +
          getRejectedCodeDescription(transaction.data.rejected_code),
        code: "error",
        error: getStatusDescription(transaction.data.status),
      });
    }

    await registerBalance(
      false,
      operation.settledAmount,
      `Liquidación crédito`,
      transaction.data.ref_ibp,
      `${operation.user.name} ${operation.user.lastname}`
    );

    operation.reference = reference;
    operation.sypagoId = transaction.data.transaction_id;
    operation.status = "approved";
    operation.expireAt = undefined;
    operation.comment = body.comment;
    operation.icon = icons[operationCount % icons.length];
    await operation.save();

    await OperationPayment.updateMany(
      { operation: body.operationId },
      { $set: { status: "pending" }, $unset: { expireAt: "" } }
    );

    // const subscription = await Subscription.findOne({ user: req.user!.id });
    // if (!subscription) {
    //   await createSubscription(req.user!.id);
    // }

    res.status(200).json({ success: true, message: "Operación confirmada" });
  }
);

/**
 * Get completed operations
 *
 * @exports
 * @async
 * @param {Request} req
 * @param {Response} res
 * @returns {unknown}
 */
export const getCompletedOperations = asyncHandler(
  async (req: Request, res: Response) => {
    const userId = req.user!.id;
    const page = parseInt(req.query.page as string) || 1; // Default to page 1 if not provided
    const limit = parseInt(req.query.limit as string) || 10; // Default to 10 items per page if not provided
    const skip = (page - 1) * limit;
    const filter: any = {
      user: userId,
      status: "completed",
    };
    const totalCount = await Operation.countDocuments(filter);

    const operations = await Operation.find({
      user: userId,
      status: "completed",
    })
      .select("reference createdAt currency amountUsd amountVef status icon")
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(limit)
      .lean()
      .exec();

    const formattedOperations = operations.map((operation) => {
      return {
        id: operation._id,
        reference: operation.reference,
        createdAt: operation.createdAt,
        amount: operation.amountUsd,
        status: operation.status,
        statusName: "Finalizado",
        iconUrl: `${env.PUBLIC_API_URL}/icons/${operation.icon}-white.png`,
      };
    });

    res.status(200).json({
      success: true,
      message: "Operaciones finalizadas",
      data: formattedOperations,
      totalCount: totalCount,
    });
  }
);

/**
 * Get active operations
 *
 * @exports
 * @async
 * @param {Request} req
 * @param {Response} res
 * @returns {unknown}
 */
export const getActiveOperations = asyncHandler(
  async (req: Request, res: Response) => {
    const page = parseInt(req.query.page as string) || 1; // Default to page 1 if not provided
    const limit = parseInt(req.query.limit as string) || 10; // Default to 10 items per page if not provided
    const skip = (page - 1) * limit;

    const userId = req.user!.id;
    const filter: any = {
      user: userId,
      status: { $in: ["pending", "approved"] },
    };
    const totalCount = await Operation.countDocuments(filter);

    const operations = await Operation.find(filter)
      .select("reference createdAt currency amountUsd amountVef status icon")
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(limit)
      .lean()
      .exec();

    const formattedOperations = operations.map((operation) => {
      return {
        id: operation._id,
        reference: operation.reference,
        createdAt: operation.createdAt,
        amount: operation.amountUsd,
        status: operation.status,
        iconUrl: `${env.PUBLIC_API_URL}/icons/${operation.icon}-white.png`,
        statusName:
          operation.status === "pending"
            ? "En proceso"
            : operation.status === "approved"
            ? "Aprobado"
            : "Desconocido",
      };
    });

    res.status(200).json({
      success: true,
      message: "Operaciones activas",
      data: formattedOperations,
      totalCount: totalCount,
    });
  }
);

/**
 * Get operation details
 *
 * @exports
 * @async
 * @param {Request} req
 * @param {Response} res
 * @returns {unknown}
 */
export const getOperationDetails = asyncHandler(
  async (req: Request, res: Response) => {
    const { operationId } = req.params;
    const userId = req.user!.id;
    if (!operationId) {
      return res.status(400).json({
        success: false,
        message: "Campo 'operationId' es requerido",
        code: "field_missing",
      });
    }

    const operation = await Operation.findOne({
      user: userId,
      _id: operationId,
    })
      .select(
        "reference createdAt amountUsd amountVef rate feeCount paymentPlan account comment"
      ) // Selecciona los campos que quieres
      .populate("paymentPlan", "date status amountUsd") // Popula el plan de pago
      .populate({
        // Popula el subcampo 'bank' dentro de 'account'
        path: "account.bank",
        select: "name", // Selecciona solo el campo 'name' del banco
      })
      .lean() // Convertir a objetos JavaScript planos
      .exec();

    if (!operation) {
      return res.status(404).json({
        success: false,
        message: "Operación no encontrada",
        code: "not_found",
      });
    }

    // const config = await Config.find().lean().exec();
    // const paidOnTimePoints = config.find(
    //   (config) => config.key === "paid-on-time-points"
    // )!.value;
    // const paidLatePoints = config.find(
    //   (config) => config.key === "paid-late-points"
    // )!.value;

    // Formatear el campo 'account' después de obtener los datos

    const formattedOperation = {
      reference: operation.reference,
      createdAt: operation.createdAt,
      amountUsd: operation.amountUsd,
      amountVef: operation.amountVef,
      rate: operation.rate,
      feeCount: operation.feeCount,
      account:
        operation.account && operation.account.bank
          ? `${operation.account.bank.name} ${operation.account.number.slice(
              -4
            )}`
          : null,
      beneficiary: operation.beneficiary,
      comment: operation.comment,
      paymentPlan: operation.paymentPlan.map((operationPayment) => {
        return {
          id: operationPayment._id,
          date: operationPayment.date,
          status: operationPayment.status,
          statusName: getOperationPaymentStatusName(operationPayment.status),
          amount: operationPayment.amountUsd,
          points:
            operationPayment.status === "pending"
              ? operationPayment.amountUsd *
                (getDaysDifference(operationPayment.date, new Date()) >= 5
                  ? 2
                  : 1)
              : 0,
        };
      }), // paymentPlan ya está poblado
      // Formatear el campo 'account'
    };

    res.status(200).json({
      success: true,
      message: "Detalles de la operación",
      data: formattedOperation,
    });
  }
);

export const getOperationDetailsById = asyncHandler(
  async (req: Request, res: Response) => {
    const { operationId } = req.params;

    if (!operationId) {
      return res.status(400).json({
        success: false,
        message: "Campo 'operationId' es requerido",
        code: "field_missing",
      });
    }

    const operation = await Operation.findOne({
      _id: operationId,
    })
      .populate("paymentPlan", "date paidAt status amountUsd") // Popula el plan de pago
      .populate("user", "name lastname email")
      .populate({
        // Popula el subcampo 'bank' dentro de 'account'
        path: "account.bank",
        select: "name", // Selecciona solo el campo 'name' del banco
      })
      .lean() // Convertir a objetos JavaScript planos
      .exec();

    if (!operation) {
      return res.status(404).json({
        success: false,
        message: "Operación no encontrada",
        code: "not_found",
      });
    }

    // const config = await Config.find().lean().exec();
    // const paidOnTimePoints = config.find(
    //   (config) => config.key === "paid-on-time-points"
    // )!.value;
    // const paidLatePoints = config.find(
    //   (config) => config.key === "paid-late-points"
    // )!.value;

    // Formatear el campo 'account' después de obtener los datos

    const formattedOperation = {
      reference: operation.reference,
      createdAt: operation.createdAt,
      amountUsd: operation.amountUsd,
      amountVef: operation.amountVef,
      commissionAmount: operation.commissionAmount,
      settledAmount: operation.settledAmount,
      rate: operation.rate,
      feeCount: operation.feeCount,
      account:
        operation.account && operation.account.bank
          ? `${operation.account.bank.name} ${operation.account.number.slice(
              -4
            )}`
          : null,
      beneficiary: operation.beneficiary,
      comment: operation.comment,
      clientName: operation.user.name + " " + operation.user.lastname,
      clientEmail: operation.user.email,
      statusName: getOperationStatusName(operation.status),
      status: operation.status,
      paymentPlan: operation.paymentPlan.map((operationPayment) => {
        return {
          id: operationPayment._id,
          date: operationPayment.date,
          paidAt: operationPayment.paidAt,
          status: operationPayment.status,
          statusName: getOperationPaymentStatusName(operationPayment.status),
          amount: operationPayment.amountUsd,
          points:
            operationPayment.status === "pending"
              ? operationPayment.amountUsd *
                (getDaysDifference(operationPayment.date, new Date()) >= 5
                  ? 2
                  : 1)
              : 0,
        };
      }), // paymentPlan ya está poblado
      // Formatear el campo 'account'
    };

    res.status(200).json({
      success: true,
      message: "Detalles de la operación",
      data: formattedOperation,
    });
  }
);

export const getOperationPaymentsWithTotal = asyncHandler(
  async (req: Request, res: Response) => {
    const userId = req.user!.id; // Obtiene el ID del usuario del request
    const page = parseInt(req.query.page as string) || 1; // Default to page 1 if not provided
    const limit = parseInt(req.query.limit as string) || 10;
    const skip = (page - 1) * limit;

    // Criterio de filtro base por usuario
    const baseFilter = {
      user: new Types.ObjectId(userId),
      status: { $in: ["pending", "overdue", "inArrears"] },
    };

    const totalCount = await OperationPayment.countDocuments(baseFilter);

    // const config = await Config.find().lean().exec();
    // const paidOnTimePoints = config.find(
    //   (config) => config.key === "paid-on-time-points"
    // )!.value;
    // const paidLatePoints = config.find(
    //   (config) => config.key === "paid-late-points"
    // )!.value;

    const aggregationResult = await OperationPayment.aggregate([
      { $match: baseFilter },
      {
        $facet: {
          // Pipeline para obtener el listado de pagos del usuario
          userOperationPayments: [
            // Puedes añadir un filtro adicional aquí si quieres que el listado
            // solo muestre un subconjunto de los pagos del usuario (ej. por estado específico)
            // { $match: { status: { $ne: "completed" } } }, // Ejemplo: Excluir pagos completados del listado del usuario
            {
              $lookup: {
                from: "Operation", // Nombre de la colección de Operation (asegúrate de que sea el correcto)
                localField: "operation", // Campo en OperationPayment que referencia a Operation
                foreignField: "_id", // Campo en Operation que coincide con localField
                as: "operationData", // Nombre del nuevo campo que contendrá los documentos de Operation
              },
            },
            {
              $unwind: {
                path: "$operationData",
                preserveNullAndEmptyArrays: true,
              },
            },
            {
              $project: {
                _id: 0, // Excluye el _id si no lo necesitas
                id: "$_id", // Opcional: renombra _id a id
                date: 1,
                status: 1,
                amountUsd: 1,
                iconUrl: {
                  $concat: [
                    `${env.PUBLIC_API_URL}/icons/`,
                    "$operationData.icon",
                    "-white.png",
                  ],
                },
                statusName: {
                  $switch: {
                    branches: [
                      {
                        case: { $eq: ["$status", "pending"] },
                        then: "Pendiente",
                      },
                      {
                        case: { $eq: ["$status", "paid"] },
                        then: "Pagado",
                      },
                      {
                        case: { $eq: ["$status", "overdue"] },
                        then: "Vencido",
                      },
                      {
                        case: { $eq: ["$status", "inArrears"] },
                        then: "En mora",
                      },
                      // Agrega más casos según los estados que manejes en tu modelo
                    ],
                    default: "Desconocido", // Valor por defecto si el estado no coincide
                  },
                },
                // Incluye otros campos de OperationPayment que necesites mostrar
                // operation: 1, // Opcional: si quieres ver a qué operación pertenece
              },
            },
            {
              $sort: { date: 1 }, // Opcional: Ordena el listado de pagos del usuario
            },
            // Aquí puedes agregar etapas de paginación si es necesario ($skip, $limit)
          ],
          // Pipeline para calcular el total de pagos pendientes del usuario
          pendingTotal: [
            // {
            //   // Filtra solo los pagos con estado "pending" entre los del usuario
            //   $match: { status: "pending" },
            // },
            {
              $group: {
                _id: null, // Agrupa todos los documentos "pending" del usuario en uno solo
                totalPendingAmountUsd: { $sum: "$amountUsd" }, // Calcula la suma de amountUsd
              },
            },
            {
              $project: {
                // Limpia el resultado del total pendiente del usuario
                _id: 0,
                totalPendingAmountUsd: 1,
              },
            },
          ],
        },
      },
    ])
      .skip(skip)
      .limit(limit)
      .exec();

    // El resultado de $facet será un array con un único objeto
    let userOperationPayments = aggregationResult[0].userOperationPayments;
    const totalPendingAmountUsd =
      aggregationResult[0].pendingTotal.length > 0
        ? aggregationResult[0].pendingTotal[0].totalPendingAmountUsd
        : 0; // Asegura que sea 0 si no hay pagos pendientes para este usuario

    // Agrega el campo points a cada pago, igual que en operationDetail
    userOperationPayments = userOperationPayments.map(
      (operationPayment: any) => ({
        ...operationPayment,
        points:
          operationPayment.status === "pending"
            ? operationPayment.amountUsd *
              (getDaysDifference(operationPayment.date, new Date()) >= 5
                ? 2
                : 1)
            : 0,
      })
    );

    res.status(200).json({
      success: true,
      message: "Listado de deudas del usuario y total pendiente",
      data: {
        operationPayments: userOperationPayments, // Ahora se llama userOperationPayments para mayor claridad
        totalAmount: parseFloat(totalPendingAmountUsd.toFixed(2)),
      },
      totalCount: totalCount,
    });
  }
);

export const getOperationPayments = asyncHandler(
  async (req: Request, res: Response) => {
    const page = parseInt(req.query.page as string) || 1; // Default to page 1 if not provided
    const limit = parseInt(req.query.limit as string) || 10;
    const skip = (page - 1) * limit;

    // Criterio de filtro base por usuario
    const baseFilter = {
      status: { $in: ["pending", "paid", "overdue", "inArrears"] },
    };

    const totalCount = await OperationPayment.countDocuments(baseFilter);

    // const config = await Config.find().lean().exec();
    // const paidOnTimePoints = config.find(
    //   (config) => config.key === "paid-on-time-points"
    // )!.value;
    // const paidLatePoints = config.find(
    //   (config) => config.key === "paid-late-points"
    // )!.value;

    const aggregationResult = await OperationPayment.aggregate([
      { $match: baseFilter },
      {
        $facet: {
          // Pipeline para obtener el listado de pagos del usuario
          userOperationPayments: [
            // Puedes añadir un filtro adicional aquí si quieres que el listado
            // solo muestre un subconjunto de los pagos del usuario (ej. por estado específico)
            // { $match: { status: { $ne: "completed" } } }, // Ejemplo: Excluir pagos completados del listado del usuario
            {
              $lookup: {
                from: "Operation", // Nombre de la colección de Operation (asegúrate de que sea el correcto)
                localField: "operation", // Campo en OperationPayment que referencia a Operation
                foreignField: "_id", // Campo en Operation que coincide con localField
                as: "operationData", // Nombre del nuevo campo que contendrá los documentos de Operation
              },
            },
            {
              $lookup: {
                from: "User",
                localField: "user",
                foreignField: "_id",
                as: "userData",
              },
            },
            {
              $unwind: {
                path: "$operationData",
                preserveNullAndEmptyArrays: true,
              },
            },
            {
              $unwind: {
                path: "$userData",
                preserveNullAndEmptyArrays: true,
              },
            },
            {
              $project: {
                _id: 0, // Excluye el _id si no lo necesitas
                id: "$_id", // Opcional: renombra _id a id
                date: 1,
                paidAt: 1,
                status: 1,
                amountUsd: 1,
                iconUrl: {
                  $concat: [
                    `${env.PUBLIC_API_URL}/icons/`,
                    "$operationData.icon",
                    "-white.png",
                  ],
                },
                clientName: {
                  $concat: ["$userData.name", " ", "$userData.lastname"],
                },
                clientEmail: "$userData.email",
                statusName: {
                  $switch: {
                    branches: [
                      {
                        case: { $eq: ["$status", "pending"] },
                        then: "Pendiente",
                      },
                      {
                        case: { $eq: ["$status", "paid"] },
                        then: "Pagado",
                      },
                      {
                        case: { $eq: ["$status", "overdue"] },
                        then: "Vencido",
                      },
                      {
                        case: { $eq: ["$status", "inArrears"] },
                        then: "En mora",
                      },
                      // Agrega más casos según los estados que manejes en tu modelo
                    ],
                    default: "Desconocido", // Valor por defecto si el estado no coincide
                  },
                },
                // Incluye otros campos de OperationPayment que necesites mostrar
                // operation: 1, // Opcional: si quieres ver a qué operación pertenece
              },
            },
            {
              $sort: { paidAt: -1, date: 1 }, // Opcional: Ordena el listado de pagos del usuario
            },
            // Aquí puedes agregar etapas de paginación si es necesario ($skip, $limit)
            {
              $skip: skip,
            },
            {
              $limit: limit,
            },
            // {
            //   $lookup: {
          ],
          // Pipeline para calcular el total de pagos pendientes del usuario
          // pendingTotal: [
          //   // {
          //   //   // Filtra solo los pagos con estado "pending" entre los del usuario
          //   //   $match: { status: "pending" },
          //   // },
          //   {
          //     $group: {
          //       _id: null, // Agrupa todos los documentos "pending" del usuario en uno solo
          //       totalPendingAmountUsd: { $sum: "$amountUsd" }, // Calcula la suma de amountUsd
          //     },
          //   },
          //   {
          //     $project: {
          //       // Limpia el resultado del total pendiente del usuario
          //       _id: 0,
          //       totalPendingAmountUsd: 1,
          //     },
          //   },
          // ],
        },
      },
    ])
      // .skip(skip)
      // .limit(limit)
      .exec();

    // El resultado de $facet será un array con un único objeto
    let userOperationPayments = aggregationResult[0].userOperationPayments;

    // Agrega el campo points a cada pago, igual que en operationDetail
    userOperationPayments = userOperationPayments.map(
      (operationPayment: any) => ({
        ...operationPayment,
        points:
          operationPayment.status === "pending"
            ? operationPayment.amountUsd *
              (getDaysDifference(operationPayment.date, new Date()) >= 5
                ? 2
                : 1)
            : 0,
      })
    );

    res.status(200).json({
      success: true,
      message: "Listado de deudas del usuario y total pendiente",
      data: userOperationPayments,
      totalCount: totalCount,
    });
  }
);

export const payDebt = asyncHandler(async (req: Request, res: Response) => {
  const { body } = req;

  const { error } = payDebtValidationSchema.validate(body, {
    abortEarly: false,
  });

  if (error) {
    return res.status(400).json({
      success: false,
      message: `Error de validación: ${error.details
        .map((d) => d.message)
        .join(", ")}`,
      code: "field_missing",
    });
  }

  const operationPayments = await OperationPayment.find({
    user: req.user!.id,
    status: { $nin: ["void", "paid"] },
    _id: { $in: body.payments.map((c: string) => new Types.ObjectId(c)) },
  });

  if (operationPayments.length === 0) {
    return res.status(404).json({
      success: false,
      message: "No se encontraron las operaciones",
      code: "not_found",
    });
  }

  //const config = await Config.find().lean().exec();
  // const paidOnTimePoints = config.find(
  //   (config) => config.key === "paid-on-time-points"
  // )!.value;
  // const paidLatePoints = config.find(
  //   (config) => config.key === "paid-late-points"
  // )!.value;

  //let amountUsd, amountVef;
  const rate = await getRateS();
  const rateUsd = parseFloat(rate!.usd.toFixed(2));

  for (const operationPayment of operationPayments) {
    operationPayment.rate = rateUsd;
    operationPayment.amountVef = parseFloat(
      (operationPayment.amountUsd * rateUsd).toFixed(2)
    );
    operationPayment.points =
      operationPayment.status === "pending"
        ? operationPayment.amountUsd *
          (getDaysDifference(operationPayment.date, new Date()) >= 5 ? 2 : 1)
        : 0;
    await operationPayment.save();
  }

  const amountUsd = operationPayments.reduce(
    (acc: number, operationPayment) => acc + operationPayment.amountUsd,
    0
  );
  const amountVef = parseFloat((amountUsd * rateUsd).toFixed(2));

  // userOperationPayments = userOperationPayments.map((operationPayment: any) => ({
  //   ...operationPayment,
  //   points: operationPayment.status === "pending" ? paidOnTimePoints : operationPayment.status !== "paid" ? -paidLatePoints : 0,
  // }));

  const expireAt = new Date();
  expireAt.setSeconds(expireAt.getSeconds() + env.OPERATION_EXPIRE_AT);

  // console.log({
  //   operation: operationPayments[0].operation,
  //   operationPayments,
  //   amountUsd,
  //   amountVef,
  //   rate: rateUsd,
  //   points: operationPayments.reduce((acc: number, operationPayment) => acc + operationPayment.points, 0),
  //   expireAt,
  // });

  const payment = new Payment({
    user: req.user!.id,
    operationPayments: body.payments,
    amountUsd,
    amountVef,
    rate: rateUsd,
    points: operationPayments.reduce(
      (acc: number, operationPayment) => acc + operationPayment.points,
      0
    ),
    expireAt,
  });
  const savedPayment = await payment.save();

  const respData = {
    id: savedPayment._id,
    feeCount: savedPayment.operationPayments.length,
    amountUsd: savedPayment.amountUsd,
    amountVef: savedPayment.amountVef,
    rate: savedPayment.rate,
    points: savedPayment.points,
  };

  res.status(200).json({
    success: true,
    message: "Operación creada en espera de confirmación",
    data: respData,
  });
});

export const payDebtConfirmation = asyncHandler(
  async (req: Request, res: Response) => {
    const { body } = req;

    const { error } = payDebtConfirmationValidationSchema.validate(body, {
      abortEarly: false,
    });

    if (error) {
      return res.status(400).json({
        success: false,
        message: `Error de validación: ${error.details
          .map((d) => d.message)
          .join(", ")}`,
        code: "validation",
      });
    }

    const payment = await Payment.findOne({
      user: req.user!.id,
      _id: body.id,
      status: "void",
    });

    if (payment == null) {
      return res.status(404).json({
        success: false,
        message: "No se encontró la operación",
        code: "not_found",
      });
    }

    const user = await User.findById(req.user!.id);

    const now = new Date();
    const todayOperationsCount = await Payment.countDocuments({
      createdAt: {
        $gte: new Date(now.getFullYear(), now.getMonth(), now.getDate()),
      },
    });

    const diferenciaMilisegundos = Math.abs(
      now.getTime() - new Date(1985, 11, 30).getTime()
    );
    const milisegundosPorDia = 1000 * 60 * 60 * 24;
    const diferenciaDias = Math.floor(
      diferenciaMilisegundos / milisegundosPorDia
    );

    payment.internalReference = `${diferenciaDias}${String(
      todayOperationsCount + 1
    ).padStart(5, "0")}`;

    let reference = body.reference;
    if (body.reference !== "000000" && env.TESTING) {
      if (body.paymentType === "mobile" || body.paymentType === "transfer") {
        // const confirmarTransaccionBody = {
        //   dataRequest: {
        //     device: body.device,
        //     securityAuth: {
        //       sessionId: sid,
        //     },
        //     transaction: {
        //       referenceNumber: body.reference,
        //       amount: payment.amountVef,
        //       //accountId: "01340804108041005394",
        //       // // trnDate?: string;
        //       // startDt: body.date,
        //       // endDt: body.date,
        //       trnDate: body.paymentType === "transfer" ? body.date : undefined,
        //       startDt: body.paymentType === "mobile" ? body.date : undefined,
        //       endDt: body.paymentType === "mobile" ? body.date : undefined,
        //       phoneNum: body.phone,
        //       bankId: body.bankCode,
        //     },
        //   },
        // };
        // const resp = await confirmarTransaccion(confirmarTransaccionBody);

        const phone = body.phone.includes("58")
          ? body.phone
          : `58${body.phone.replace(/^0+/, "")}`;

        const data = {
          phone,
          bank: body.bankCode,
          date: body.date,
          reference: body.reference,
        } as env.BancamigaFindPaymentMobileBody;

        const resp = await findPaymentMobile(data);

        if (!resp.success) {
          // loggers.error("Operation.PayDebtConfirmation", {
          //   request: { ...body, user: req.user?.id },
          //   response: resp,
          // });
          return res.status(400).json({
            success: false,
            message: resp.message,
            code: "error",
          });
        }

        reference = resp.data.NroReferencia;
      } else if (body.paymentType === "debit") {
        // const account = await Account.findOne({
        //   user: req.user!.id,
        //   _id: body.account,
        // }).populate("bank");

        // if (!account || account.bank.code !== "0134") {
        //   return res.status(400).json({
        //     success: false,
        //     message: "Debe escoger una cuenta Banesco",
        //     code: "not_found",
        //   });
        // }
        // const consultaCuentaBody = {
        //   dataRequest: {
        //     device: { ...body.device, sid },
        //     // currency?: string;
        //     // customer?: {
        //     //     customerIdType?: string;
        //     //     customerId?: string;
        //     // },
        //     securityAuth: {
        //       otp: body.otp,
        //     },
        //     auth: {
        //       code: body.auth,
        //     },
        //   },
        // };

        // const respConsultacuenta = await consultaCuenta(consultaCuentaBody);
        // if (!respConsultacuenta.success) {
        //   loggers.error("Operation.PayDebtConfirmation", {
        //     request: { ...body, user: req.user?.id },
        //     response: respConsultacuenta,
        //   });
        //   return res.status(400).json({
        //     success: false,
        //     message: `Error consultando la cuenta de débito - ${respConsultacuenta.message}`,
        //     code: "account_error",
        //   });
        // }

        // const debitAccounts = respConsultacuenta.data.dataResponse.filter(
        //   (item: any) => item.accountId.slice(-4) === account.number.slice(-4)
        // );

        // if (debitAccounts.length === 0) {
        //   loggers.error("Operation.PayDebtConfirmation", {
        //     request: { ...body, user: req.user?.id },
        //     response: "No se pudo encontrar la cuenta de débito en Banesco",
        //   });
        //   return res.status(400).json({
        //     success: false,
        //     message: "No se pudo encontrar la cuenta de débito en Banesco",
        //     code: "not_found",
        //   });
        // }

        // const debitAccount = debitAccounts.find(
        //   (item: any) => item.balance >= payment.amountVef
        // );
        // if (!debitAccount) {
        //   loggers.error("Operation.PayDebtConfirmation", {
        //     request: { ...body, user: req.user?.id },
        //     response:
        //       "No se pudo encontrar la cuenta de débito con saldo suficiente en Banesco",
        //   });
        //   return res.status(400).json({
        //     success: false,
        //     message:
        //       "No se pudo encontrar la cuenta de débito con saldo suficiente en Banesco",
        //     code: "not_found",
        //   });
        // }

        // const cargoCuentaBody = {
        //   dataRequest: {
        //     device: body.device,
        //     payment: {
        //       customerId: `${user?.identificationType}${user?.document}`,
        //       accountDebit: debitAccount.accountId,
        //       accountType: debitAccount.accountType,
        //       amount: payment.amountVef,
        //       companyCode: "CAB",
        //       companyId: "J300349454",
        //       concept: "pago",
        //       paymentId: generarNumeroAleatorio14Digitos().toString(),
        //     },
        //     securityAuth: {
        //       otp: body.otp,
        //       sessionId: sid,
        //     },
        //     auth: {
        //       code: body.auth,
        //       token: respConsultacuenta.data.token,
        //     },
        //   },
        // };

        // const respCargoCuenta = await cargoEnCuentaPago(cargoCuentaBody);
        // if (!respCargoCuenta.success) {
        //   loggers.error("Operation.PayDebtConfirmation", {
        //     request: { ...body, user: req.user?.id },
        //     response: respCargoCuenta,
        //   });
        //   return res.status(400).json({
        //     success: false,
        //     message: `Error ejecutando el pago - ${respCargoCuenta.message}`,
        //     code: "payment_error",
        //   });
        // }
        // reference = respCargoCuenta.data.dataResponse.referenceNumber;

        console.log(payment.debitorAccount);
        if (
          !payment.debitorAccount?.bankCode ||
          !payment.debitorAccount?.identificationNumber ||
          !payment.debitorAccount?.phone
        ) {
          return res.status(400).json({
            success: false,
            message: "No se ha generado el código de OTP",
            code: "otp_error",
          });
        }

        const debitOtpBody: env.SypagoDebitOtpBody = {
          internal_id: payment.internalReference,
          account: {
            bank_code: env.SYPAGO_BANK_ACCOUNT_CODE!,
            type: "CNTA",
            number: env.SYPAGO_BANK_ACCOUNT_NUMBER!,
          },
          receiving_user: {
            name: "Débito OTP",
            otp: body.otp,
            document_info: {
              type: payment.debitorAccount.identificationType,
              number: payment.debitorAccount.identificationNumber,
            },
            account: {
              bank_code: payment.debitorAccount.bankCode,
              type: "CELE",
              number: payment.debitorAccount.phone,
            },
          },
          amount: {
            amt: payment.amountVef,
            currency: "VES",
          },
          concept: "pago de cuota por debito OTP",
          notification_urls: {
            web_hook_endpoint: env.SYPAGO_WEBHOOK_URL!,
          },
        };

        const respDebitOtp = await debitOtp(debitOtpBody);
        if (!respDebitOtp.success) {
          return res.status(400).json({
            success: false,
            message: `Error al ejecutar el pago - ${respDebitOtp.message}`,
            code: "payment_error",
          });
        }

        const transaction = await getTransactionResult(
          respDebitOtp.data.transaction_id
        );

        if (!transaction.success) {
          return res.status(400).json({
            success: false,
            message: "Error al obtener la transacción de pago",
            code: "error",
            error: transaction.message,
          });
        }

        if (transaction.data.status !== TransactionStatusCode.ACCP) {
          return res.status(400).json({
            success: false,
            message:
              "La transacción de pago no está aprobada - " +
              getRejectedCodeDescription(transaction.data.rejected_code),
            code: "error",
            error: getStatusDescription(transaction.data.status),
          });
        }

        reference = transaction.data.ref_ibp;
      }
    }

    payment.reference = reference;
    payment.expireAt = undefined;
    payment.status = "confirmed";
    payment.save();

    await registerBalance(
      true,
      payment.amountVef,
      // `Pago de cuotas (${payment.operationPayments.join(", ")})`,
      "Pago de cuotas",
      reference,
      `${user?.name} ${user?.lastname}`
    );

    await OperationPayment.updateMany(
      {
        user: req.user!.id,
        status: { $nin: ["void", "paid"] },
        _id: { $in: payment.operationPayments },
      },
      { $set: { status: "paid", paidAt: new Date() } }
    );

    for (const operationPaymentId of payment.operationPayments) {
      // const pendingOperationPayments = await OperationPayment.exists({ user: req.user!.id, status: "pending", operation: payment.operation });
      const operationPayment = await OperationPayment.findOne({
        user: req.user!.id,
        _id: operationPaymentId,
      });
      const pendingOperationPayments = await OperationPayment.exists({
        user: req.user!.id,
        status: "pending",
        operation: operationPayment?.operation,
      });
      if (!pendingOperationPayments) {
        await Operation.updateOne(
          { user: req.user!.id, _id: operationPayment?.operation },
          { $set: { status: "completed" } }
        );
      }
    }

    user!.points = parseFloat((user!.points + payment.points).toFixed(2));
    await user?.save();

    const respNewLevel = await checkAndUpdateLevel(req.user!.id);

    // const operationPayments = await OperationPayment.find({
    //   user: req.user!.id,
    //   status: { $nin: ["void", "paid"] },
    //   _id: { $in: payment.operationPayments },
    // });

    // operationPayments.forEach(async (operationPayment) => {
    //   operationPayment.status = "paid";
    //   await operationPayment.save();
    // });

    return res.status(200).json({
      success: true,
      message: "Operación confirmada",
      data: {
        newLevel: respNewLevel.success ? respNewLevel.data : undefined,
      },
    });
  }
);

export const requestSypagoOtp = asyncHandler(
  async (req: Request, res: Response) => {
    const { body } = req;

    const { error } = requestOtpValidationSchema.validate(body, {
      abortEarly: false,
    });

    if (error) {
      return res.status(400).json({
        success: false,
        message: `Error de validación: ${error.details
          .map((d) => d.message)
          .join(", ")}`,
        code: "field_missing",
      });
    }

    const payment = await Payment.findOne({
      user: req.user!.id,
      _id: body.id,
      status: "void",
    });

    if (payment == null) {
      return res.status(404).json({
        success: false,
        message: "No se encontró la operación",
        code: "not_found",
      });
    }

    const requestOtpBody: env.SypagoRequestOtpBody = {
      creditor_account: {
        bank_code: env.SYPAGO_BANK_ACCOUNT_CODE!,
        type: "CNTA",
        number: env.SYPAGO_BANK_ACCOUNT_NUMBER!,
      },
      debitor_document_info: {
        type: body.identificationType,
        number: body.identificationNumber,
      },
      debitor_account: {
        bank_code: body.bankCode,
        type: "CELE",
        number: body.phone,
      },
      amount: {
        amt: payment.amountVef,
        currency: "VES",
      },
    };
    const respRequestOtp = await requestOtp(requestOtpBody);

    if (!respRequestOtp.success) {
      return res.status(400).json({
        success: false,
        message: `Ha ocurrido un error al generar el código OTP, por favor verifica tus datos e intenta de nuevo`,
        code: "otp_error",
      });
    }

    payment.debitorAccount = {
      bankCode: body.bankCode,
      identificationType: body.identificationType,
      identificationNumber: body.identificationNumber,
      phone: body.phone,
    };
    await payment.save();

    return res.status(200).json({
      success: true,
      message: "Código de OTP enviado correctamente",
    });
  }
);

export const getAccountChargeLink = asyncHandler(
  async (req: Request, res: Response) => {
    const resp = await cargoEnCuentaLink();

    if (resp.success) {
      return res.status(200).json(resp);
    } else {
      return res.status(400).json(resp);
    }
  }
);

const checkAndUpdateLevel = async (userId: string) => {
  const user = await User.findById(userId);
  if (!user) {
    // throw new Error("Usuario no encontrado");
    return {
      success: false,
      message: "Usuario no encontrado",
    };
  }

  const newLevel = await Level.findOne({
    pointsRequired: { $lte: user.points },
  })
    .sort({ level: -1 })
    .limit(1);

  if (!newLevel) {
    // throw new Error("No se encontraron nuevos niveles configurados");
    return {
      success: false,
      message: "No se encontraron nuevos niveles configurados",
    };
  }

  // Si el nivel ha cambiado, actualizar
  if (user.level === newLevel.level) {
    return {
      success: false,
      message: "No se alcanzo el nuevo nivel",
    };
  }

  const oldLevel = user.level;

  // Actualizar nivel y límite de crédito del usuario
  user.level = newLevel.level;
  user.maxAmount = newLevel.creditLimit;
  user.allowedFeeCount = newLevel.allowedFeeCount;
  user.levelName = newLevel.name;

  await user.save();

  // Registrar el cambio en el historial (opcional)
  await LevelHistory.create({
    user: user._id,
    oldLevel,
    newLevel: newLevel.level,
    pointsAtChange: user.points,
    changedAt: new Date(),
  });

  return {
    success: true,
    data: {
      oldLevel,
      newLevel: newLevel.level,
      maxAmount: newLevel.creditLimit,
      allowedFeeCount: newLevel.allowedFeeCount,
    },
  };
};

export const checkLatePayments = async (req: Request, res: Response) => {
  const today = startOfToday();

  const latePayments = await OperationPayment.find({
    status: { $in: ["pending", "overdue", "inArrears"] },
    date: { $lt: today },
  });

  for (const latePayment of latePayments) {
    latePayment.status = "overdue";
    await latePayment.save();

    const user = await User.findById(latePayment.user);
    if (!user) {
      continue;
    }

    const lastUpdatedAt = user.updatedAt;
    lastUpdatedAt.setHours(0, 0, 0, 0);
    if (today.getTime() == lastUpdatedAt.getTime()) {
      continue;
    }

    user.points =
      user.points - latePayment.amountUsd < 0
        ? 0
        : parseFloat((user.points - latePayment.amountUsd).toFixed(2));
    await user.save();

    await checkAndUpdateLevel(user._id);
  }

  res.status(200).json({
    success: true,
    message: "Pagos vencidos actualizados correctamente",
    data: {
      latePayments: latePayments.map((latePayment) => {
        return {
          id: latePayment._id,
          operation: latePayment.operation,
          amount: latePayment.amountUsd,
          status: latePayment.status,
          statusName: getOperationPaymentStatusName(latePayment.status),
          date: latePayment.date,
        };
      }),
    },
  });
};

export const getAllOperations = async (req: Request, res: Response) => {
  const page = parseInt(req.query.page as string) || 1; // Default to page 1 if not provided
  const limit = parseInt(req.query.limit as string) || 10; // Default to 10 items per page if not provided
  const globalFilter = req.query.globalFilter as string;
  const skip = (page - 1) * limit;

  const filter: any = {};
  if (globalFilter) {
    // Create a case-insensitive regular expression for the search term
    const regex = new RegExp(globalFilter, "i");

    // Use the $or operator to search across multiple fields
    filter.$or = [
      // { "userData.name": { $regex: regex } },
      // { "userData.lastname": { $regex: regex } },
      { "userData.fullName": { $regex: regex } },
      { "userData.email": { $regex: regex } },
    ];
  }

  if (!isNaN(parseFloat(globalFilter)))
    filter.$or.push({ amountUsd: parseFloat(globalFilter) });

  const results = await Operation.aggregate([
    {
      $lookup: {
        from: "User",
        localField: "user",
        foreignField: "_id",
        as: "userData",
      },
    },
    {
      $unwind: "$userData",
    },
    {
      $addFields: {
        "userData.fullName": {
          $concat: ["$userData.name", " ", "$userData.lastname"],
        },
      },
    },
    {
      $match: filter,
    },
    { $sort: { createdAt: -1 } },
    { $skip: skip },
    { $limit: limit },
    {
      $project: {
        _id: 1,
        createdAt: 1,
        amountUsd: 1,
        amountVef: 1,
        rate: 1,
        status: 1,
        user: {
          _id: "$userData._id",
          fullName: "$userData.fullName",
          email: "$userData.email",
        },
      },
    },
  ]);

  console.log(results);

  const formattedResults = results.map((operation) => {
    return {
      id: operation._id,
      reference: operation.reference,
      createdAt: operation.createdAt,
      amount: operation.amountUsd,
      status: {
        id: operation.status,
        name: getOperationStatusName(operation.status),
      },
      amountUsd: operation.amountUsd,
      clientName: operation.user.fullName,
      clientEmail: operation.user.email,
    };
  });

  return res.status(200).json({
    success: true,
    message: "Operations retrieved successfully",
    data: formattedResults,
  });
};

const checkMinBalance = async (amountVef: number): Promise<boolean> => {
  const pastBalances = await Balance.aggregate([
    {
      $match: {
        status: "confirmed",
      },
    },
    {
      $group: {
        _id: null,
        totalAmount: {
          $sum: "$amount",
        },
      },
    },
  ]);

  const configMinBalance = await Config.findOne({
    key: "min-available-balance",
  });
  const minBalance = configMinBalance?.value ?? 0;

  const availableBalance =
    pastBalances.length > 0 ? pastBalances[0].totalAmount : 0;

  if (availableBalance - amountVef < minBalance) {
    return false;
  }
  return true;
};
