import { Response } from "express";
import moment from "moment";
import CONSTANTS from "../config/constants";
import log from "../config/log";
import clientDB from "../models/client.model";
import expenseDB from "../models/expense.model";
import propertyDB from "../models/property.model";
import staffDB from "../models/staff.model";
import vendorDB from "../models/vendor.model";
import CustomRequest from "../types/requestType";
import propertiesTypes from "../schemas/property.schema";
import staffLedgerDB from "../models/staffLedger.model";
import staffBalanceDB from "../models/staffBalance.model";
import { isUserFinanceAdmin, isUserPartner, staffSalaryModulePermission } from "../utils/isUserPartner";
import fsPromises from "fs/promises";
import fs from "fs";
import { logExpenseActivity } from "../utils/logActivity";
import recurringExpenseDB from "../models/recurringExpense.model";
import { isPrivilegedStaff } from "../utils/isPrivilegedStaff";
import landlordDB from "../models/landlord.model";
import clientConfigDB from "../models/clientConfig.model";
import bedDB from "../models/beds.model";
import { setExpenseJournalTallyStatus, setExpensePaymentTallyStatus } from "../utils/setTallyStatus";
import flatDB from "../models/flat.model";
import assetsDB from "../models/assets.model";
import cashfreePayout from "../utils/cashfreePayout";
import { getWalletBalanceAndLimits } from "../utils/walletHelper";
import payoutBeneficiaryDB from "../models/payoutBeneficiary.model";
import autopayDB from "../models/autopay.model";
// import staffCommissionDB from "../models/staffCommission.model";

const expense: any = {};

expense.AddExpense = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "AddExpense";

  const file = req.file as Express.Multer.File;
  const removeTmpImages = async () => {
    try {
      await fsPromises.unlink(file.path);
    } catch (err) {
      log.info(
        `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
          err
        )}]`
      );
    }
  };

  try {
    let {
      amount,
      type,
      repetitionType,
      paidDate,
      propertyId,
      paidBy,
      paidByUserType,
      paidTo,
      paidToUserType,
      description,
      paymentMode,
      repetitionMonths=1,
    } = req.body;

    const userType = req.userType;
    let clientId = req.id;

    amount = Number(amount);
    type = Number(type);
    paidDate = moment(paidDate).format("YYYY-MM-DD HH:mm:ss");
    propertyId = Number(propertyId);
    paidBy = Number(paidBy);
    paidByUserType = Number(paidByUserType);
    paidTo = Number(paidTo);
    paidToUserType = Number(paidToUserType);
    paymentMode = Number(paymentMode);
    let paidByName = "";
    let paidToName = "";
    let nextExpenseDate = null;
    if(CONSTANTS.REPETITION_TYPE.ONE_TIME === Number(repetitionType)){
      repetitionMonths = 0;
    } else {
      nextExpenseDate = moment(paidDate).add(repetitionMonths, 'months').format("YYYY-MM-DD");
    }

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Staff Id [${req.id}], Repetition Months [${repetitionMonths}], No Staff Found`
        );

        if (file) {
          await fsPromises.unlink(file.path);
        }

        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Staff Id [${
          req.id
        }], Paid Date [${paidDate}], Is File Uploaded ${
          file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
        }, Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Paid Date [${paidDate}], Repetition Months [${repetitionMonths}], Is File Uploaded ${
          file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
        }, Client Requested....`
      );
    }

    if (paidByUserType === paidToUserType && paidBy === paidTo) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], You cannot pay yourself`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: "You cannot pay yourself", isSuccess: false });
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], No Client Found`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const property = await propertyDB.getById({ id: propertyId });
    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], No Property Found`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const expenseId = await expenseDB.create({
      type,
      amount,
      clientId,
      paidDate: moment(paidDate).format("YYYY-MM-DD hh:mm:ss"),
      paidByUserType,
      paidBy,
      paidTo,
      paidToUserType,
      description,
      paymentMethod: paymentMode,
      repetitionType,
      noOfMonths: repetitionMonths,
      dueDate: nextExpenseDate,
      isPaid: 1,
    });
    await expenseDB.addProperty({
      expenseId,
      propId: propertyId,
      clientId,
    });

    if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
      log.info(
        `[${C}], [${F}], Staff Id ${paidBy}, amount ${amount} Staff Ledger and Balance Recorded`
      );
      const paidByStaff = await staffDB.getById({ id: paidBy });
      let paidToUser = null;
      if (paidToUserType === CONSTANTS.USER_TYPE.STAFF) {
        paidToUser = await staffDB.getById({ id: paidTo });
      } else {
        paidToUser = await vendorDB.getById({ id: paidTo });
      }

      paidByName = paidByStaff.name;
      paidToName = paidToUser.name;

      await staffLedgerDB.addExpense({
        staffId: paidBy,
        amount: -amount,
        type: CONSTANTS.STAFF_LEDGER_TYPES.EXPENSES,
        mode: paymentMode,
        description: `Expense paid by Staff ${paidByStaff.name} to ${
          paidToUser.name
        } on ${moment().format("YYYY-MM-DD HH:mm:ss")}`,
        expenseId: expenseId,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        dueDate: moment(nextExpenseDate).format("YYYY-MM-DD"),
      });

      const staffExistsInBalance = await staffBalanceDB.getByStaffId({
        staffId: paidBy,
      });

      if (!staffExistsInBalance) {
        await staffBalanceDB.add({
          staffId: paidBy,
          totalCollected: 0,
          totalGivenToOwner: 0,
          totalExpense: amount,
        });
      } else {
        await staffBalanceDB.updateTotalExpense({
          staffId: paidBy,
          amount: amount,
        });
      }
    }

    if (file) {
      const folderName = `expense_${expenseId}`;
      const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
      const fileExtension = file.mimetype.split("/")[1];
      const filename = `expenseBill.${fileExtension}`;

      if (!fs.existsSync(folderPath)) {
        await fsPromises.mkdir(folderPath, { recursive: true });
      }

      const oldPath = `uploads/tmp/${file.filename}`;
      const newPath = `${folderPath}/${filename}`;
      const url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

      await fsPromises.copyFile(oldPath, newPath);

      await expenseDB.updateSupportDoc({
        id: expenseId,
        supportDoc: url,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
      );
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Is File Uploaded ${
        file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
      }, Expense Added Successfully`
    );

    return res.status(200).json({
      msg: "Expense added successfully",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    if (file) {
      await removeTmpImages();
    }

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};
//Pallav Commented 14 Apr, 2026
// expense.AddExpenseX = async (req: CustomRequest, res: Response) => {
//   const C = "Expense Controller";
//   const F = "AddExpenseX";

//   const file = req.file as Express.Multer.File;
//   const removeTmpImages = async () => {
//     try {
//       await fsPromises.unlink(file.path);
//     } catch (err) {
//       log.info(
//         `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
//           err
//         )}]`
//       );
//     }
//   };

//   try {
//     let {
//       amount,
//       type,
//       repetitionType,
//       paidDate,
//       propertyIds=null,
//       paidBy,
//       paidByUserType,
//       paidTo,
//       paidToUserType,
//       description,
//       paymentMode,
//       repetitionMonths=1,
//       dueDate,
//       isPaid = true,
//     } = req.body;

//     const userType = req.userType;
//     let clientId = req.id;
//     let origDueDate = dueDate;
//     if(dueDate === "2026-12-01")
//       dueDate = "2025-12-01";
//     else if(dueDate === "2026-11-01")
//       dueDate = "2025-11-01";
//     else if(dueDate === "2026-10-01")
//       dueDate = "2025-10-01";

//     log.info(
//       `[${C}], [${F}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}], Description [${description}], Payment Mode [${paymentMode}], Paid Date [${paidDate}], Repetition Months [${repetitionMonths}], Orig Due Date Month [${origDueDate}], Due Date Month [${dueDate}], Is Paid [${isPaid}]`
//     );

//     amount = Number(amount);
//     type = Number(type);
//     paidDate = moment(paidDate).utc();
//     paidDate = paidDate.tz('Asia/Kolkata').format("YYYY-MM-DD HH:mm:ss");
//     // paidDate = moment(paidDate).format("YYYY-MM-DD HH:mm:ss");
//     if (!dueDate || dueDate === null || dueDate === undefined || dueDate === "") {
//       dueDate = paidDate;
//     }

//     if (type === 14 || type === 23 || type === 52 || type === 53) {
//       propertyIds = null; // Salary, Bonus, Travel Allowance, and HRA expenses should not be linked to any property as discussed(2026-03-02)
//     }

//     paidBy = Number(paidBy);
//     propertyIds = propertyIds !== null ? propertyIds.split(",").map((s:any) => s.trim()).filter((s:any) => s !== "").map(Number) : null;
//     paidByUserType = Number(paidByUserType);
//     paidTo = Number(paidTo);
//     paidToUserType = Number(paidToUserType);
//     paymentMode = Number(paymentMode);
//     let paidByName = "";
//     let paidToName = "";
//     let nextExpenseDate = null;
//     if(CONSTANTS.REPETITION_TYPE.ONE_TIME === Number(repetitionType)){
//       repetitionMonths = 0;
//     } else {
//       nextExpenseDate = moment(paidDate).add(repetitionMonths, 'months').format("YYYY-MM-DD");
//     }

//     if (userType === CONSTANTS.USER_TYPE.STAFF) {
//       const staff = await staffDB.getById({ id: req.id });
//       if (!staff) {
//         log.info(
//           `[${C}], [${F}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Staff Id [${req.id}], Repetition Months [${repetitionMonths}], Is Paid [${isPaid}], No Staff Found`
//         );

//         if (file) {
//           await fsPromises.unlink(file.path);
//         }

//         return res
//           .status(400)
//           .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
//       }

//       clientId = staff.clientId;

//       log.info(
//         `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Is Paid [${isPaid}], Staff Id [${
//           req.id
//         }], Paid Date [${paidDate}], Is File Uploaded ${
//           file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
//         }, Staff Requested....`
//       );
//     } else {
//       log.info(
//         `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Paid Date [${paidDate}], Repetition Months [${repetitionMonths}], Is Paid [${isPaid}], Is File Uploaded ${
//           file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
//         }, Client Requested....`
//       );
//     }

//     if (paidByUserType === paidToUserType && paidBy === paidTo) {
//       log.info(
//         `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Is Paid [${isPaid}], Payment Mode [${paymentMode}], You cannot pay yourself`
//       );

//       if (file) {
//         await fsPromises.unlink(file.path);
//       }

//       return res
//         .status(400)
//         .json({ msg: "You cannot pay yourself", isSuccess: false });
//     }

//     const client = await clientDB.getById({ id: clientId });

//     if (!client) {
//       log.info(
//         `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], No Client Found`
//       );

//       if (file) {
//         await fsPromises.unlink(file.path);
//       }

//       return res
//         .status(400)
//         .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
//     }

//     if (paidByUserType === CONSTANTS.USER_TYPE.CLIENT) {
//       paidByName = client?.name;
//     }

//     let expenseId = null;
//     let url;

//     // if (file) {
//     //   const folderName = `expense_${expenseId}`;
//     //   const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
//     //   const fileExtension = file.mimetype.split("/")[1];
//     //   const filename = `expenseBill.${fileExtension}`;

//     //   if (!fs.existsSync(folderPath)) {
//     //     await fsPromises.mkdir(folderPath, { recursive: true });
//     //   }

//     //   const oldPath = `uploads/tmp/${file.filename}`;
//     //   const newPath = `${folderPath}/${filename}`;
//     //   url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

//     //   await fsPromises.copyFile(oldPath, newPath);

//     //   log.info(
//     //     `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
//     //   );
//     // }

//     if (propertyIds && propertyIds.length > 0) {
//       const dividedAmount = Math.floor(amount / propertyIds.length);
//       const leftOverAmount = amount - (dividedAmount * propertyIds.length);
//       let count = 1;
//       let totalBeds = 0;
//       let amountDivided = 0;
//       let dividingByBeds = false;

//       const isExpenseDivideByBedsEnabled = await clientConfigDB.getClientConfig({
//         clientId,
//         provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
//         type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EXPENSE_ALLOCATION_BY_BEDS,
//       });
      
//       if(isExpenseDivideByBedsEnabled && Number(isExpenseDivideByBedsEnabled.value) === 1){
//         totalBeds = await bedDB.getBedCountByPropIds({
//           propIds: propertyIds,
//         });
//         dividingByBeds = true;
//       }

//       // log.info(`Amount [${amount}], Divided Amount [${dividedAmount}], LeftOver Amount [${leftOverAmount}]`);
//       let referenceId = null;

//       for (let propId of propertyIds) {
//         // log.info(`Prop Id [${propId}], Number propId [${Number(propId)}]`);

//         const property = await propertyDB.getById({ id: Number(propId) });
//         if (!property) {
//           log.info(
//             `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], No Property Found`
//           );
//           if (file) {
//             await fsPromises.unlink(file.path);
//           }
//           return res
//             .status(400)
//             .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
//         }

//         let propAmount = dividedAmount;
//         if (dividingByBeds) {
//           const propBedCount = await bedDB.getBedCountByPropIds({
//             propIds: [property.id],
//           });
//           propAmount = Math.floor((propBedCount / totalBeds) * amount);
//           amountDivided += propAmount;
//         }
        
//         if (count === propertyIds.length) {
//           if (dividingByBeds) {
//             propAmount += (amount - amountDivided);
//           } else {
//             propAmount += leftOverAmount;
//           }
//         }

//         expenseId = await expenseDB.create({
//           type,
//           amount: propAmount,
//           clientId,
//           paidDate: paidDate,
//           paidByUserType,
//           paidBy,
//           paidTo,
//           paidToUserType,
//           description,
//           paymentMethod: paymentMode,
//           repetitionType,
//           noOfMonths: repetitionMonths,
//           dueDate: dueDate ? dueDate : nextExpenseDate,
//           isPaid: isPaid ? 1 : 0,
//         });
//         //log.info(`Expense created with id 111: ${expenseId}`);
    
//         await expenseDB.addProperty({
//           expenseId: expenseId,
//           propId: Number(propId),
//           clientId: clientId,
//         });

//         if (!isPaid) {
//           await logExpenseActivity(
//             req.userType!,
//             Number(req.id)!,
//             Number(req.parentClientId!),
//             Number(req.platform),
//             CONSTANTS.ACTIVITY_TYPES.ADD_UNPAID_EXPENSE,
//             paidBy,
//             paidByUserType,
//             paidTo,
//             paidToUserType,
//             type,
//             amount,
//             paymentMode,
//             propId,
//           );
//         } else {
//           await logExpenseActivity(
//             req.userType!,
//             Number(req.id)!,
//             Number(req.parentClientId!),
//             Number(req.platform),
//             CONSTANTS.ACTIVITY_TYPES.ADD_EXPENSE,
//             paidBy,
//             paidByUserType,
//             paidTo,
//             paidToUserType,
//             type,
//             amount,
//             paymentMode,
//             propId,
//           );
//         }

//         if (file) {
//           const folderName = `expense_${expenseId}`;
//           const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
//           const fileExtension = file.mimetype.split("/")[1];
//           const filename = `expenseBill.${fileExtension}`;

//           if (!fs.existsSync(folderPath)) {
//             await fsPromises.mkdir(folderPath, { recursive: true });
//           }

//           const oldPath = `uploads/tmp/${file.filename}`;
//           const newPath = `${folderPath}/${filename}`;
//           url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

//           await fsPromises.copyFile(oldPath, newPath);

//           log.info(
//             `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
//           );
//           await expenseDB.updateSupportDoc({
//             id: expenseId,
//             supportDoc: url,
//           });
//         }

//         if (Number(repetitionType) === 2 && type !== 14) {
//           const recurringExpenseId: any = await recurringExpenseDB.create({
//             type,
//             amount: propAmount,
//             clientId,
//             propId: Number(propId),
//             paidDate,
//             paidByUserType,
//             paidBy,
//             paidTo,
//             paidToUserType,
//             description,
//             paymentMethod: paymentMode,
//             noOfMonths: repetitionMonths,
//             dueDate: nextExpenseDate,
//             expenseCycle: moment(paidDate).date(),
//             referenceId: referenceId || null,
//           });

//           if (count === 1) {
//             referenceId = recurringExpenseId;
//             await recurringExpenseDB.updateReferenceId({
//               id: recurringExpenseId,
//               referenceId: recurringExpenseId,
//             });
//           }

//           await expenseDB.updateRecurringExpenseId({
//             id: expenseId,
//             recurringExpenseId: recurringExpenseId
//           });

//           await logExpenseActivity(
//             req.userType!,
//             Number(req.id)!,
//             Number(req.parentClientId!),
//             Number(req.platform),
//             CONSTANTS.ACTIVITY_TYPES.ADD_RECURRING_EXPENSE,
//             paidBy,
//             paidByUserType,
//             paidTo,
//             paidToUserType,
//             type,
//             amount,
//             paymentMode,
//             propId,
//           );
//         }
//         count += 1;
//       }
//     } else {
//       expenseId = await expenseDB.create({
//         type,
//         amount: amount,
//         clientId,
//         paidDate: paidDate,
//         paidByUserType,
//         paidBy,
//         paidTo,
//         paidToUserType,
//         description,
//         paymentMethod: paymentMode,
//         repetitionType,
//         noOfMonths: repetitionMonths,
//         dueDate: dueDate ? dueDate : nextExpenseDate,
//         isPaid: isPaid ? 1 : 0,
//       });

//       if (!isPaid) {
//         await logExpenseActivity(
//           req.userType!,
//           Number(req.id)!,
//           Number(req.parentClientId!),
//           Number(req.platform),
//           CONSTANTS.ACTIVITY_TYPES.ADD_UNPAID_EXPENSE,
//           paidBy,
//           paidByUserType,
//           paidTo,
//           paidToUserType,
//           type,
//           amount,
//           paymentMode,
//         );
//       } else {
//         await logExpenseActivity(
//           req.userType!,
//           Number(req.id)!,
//           Number(req.parentClientId!),
//           Number(req.platform),
//           CONSTANTS.ACTIVITY_TYPES.ADD_EXPENSE,
//           paidBy,
//           paidByUserType,
//           paidTo,
//           paidToUserType,
//           type,
//           amount,
//           paymentMode,
//         );
//       }
//       //log.info(`Expense created with id 222: ${expenseId}`);
//       if (file) {
//         const folderName = `expense_${expenseId}`;
//         const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
//         const fileExtension = file.mimetype.split("/")[1];
//         const filename = `expenseBill.${fileExtension}`;

//         if (!fs.existsSync(folderPath)) {
//           await fsPromises.mkdir(folderPath, { recursive: true });
//         }

//         const oldPath = `uploads/tmp/${file.filename}`;
//         const newPath = `${folderPath}/${filename}`;
//         url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

//         await fsPromises.copyFile(oldPath, newPath);

//         log.info(
//           `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
//         );
//         await expenseDB.updateSupportDoc({
//           id: expenseId,
//           supportDoc: url,
//         });
//       }

//       if (Number(repetitionType) === 2 && type !== 14) {
//         const recurringExpenseId = await recurringExpenseDB.create({
//           type,
//           amount,
//           clientId,
//           propId: null,
//           paidDate,
//           paidByUserType,
//           paidBy,
//           paidTo,
//           paidToUserType,
//           description,
//           paymentMethod: paymentMode,
//           noOfMonths: repetitionMonths,
//           dueDate: nextExpenseDate,
//           expenseCycle: moment(paidDate).date(),
//           referenceId: null,
//         });
//         await recurringExpenseDB.updateReferenceId({
//           id: recurringExpenseId,
//           referenceId: recurringExpenseId,
//         });
//         await expenseDB.updateRecurringExpenseId({
//           id: expenseId,
//           recurringExpenseId: recurringExpenseId
//         });
//         await logExpenseActivity(
//           req.userType!,
//           Number(req.id)!,
//           Number(req.parentClientId!),
//           Number(req.platform),
//           CONSTANTS.ACTIVITY_TYPES.ADD_RECURRING_EXPENSE,
//           paidBy,
//           paidByUserType,
//           paidTo,
//           paidToUserType,
//           type,
//           amount,
//           paymentMode,
//         );
//       }
//     }

//     if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
//       log.info(
//         `[${C}], [${F}], Staff Id ${paidBy}, amount ${amount} Staff Ledger and Balance Recorded`
//       );
//       const paidByStaff = await staffDB.getById({ id: paidBy });
//       let paidToUser = null;
//       if (paidToUserType === CONSTANTS.USER_TYPE.STAFF) {
//         paidToUser = await staffDB.getById({ id: paidTo });
//       } else {
//         paidToUser = await vendorDB.getById({ id: paidTo });
//       }

//       paidByName = paidByStaff.name;
//       paidToName = paidToUser.name;

//       await staffLedgerDB.addExpense({
//         staffId: paidBy,
//         amount: -amount,
//         type: CONSTANTS.STAFF_LEDGER_TYPES.EXPENSES,
//         mode: paymentMode,
//         description: `Expense paid by Staff ${paidByStaff.name} to ${
//           paidToUser.name
//         } on ${moment().format("YYYY-MM-DD HH:mm:ss")}`,
//         expenseId: expenseId,
//         paidDate: moment(paidDate).format("YYYY-MM-DD"),
//         dueDate: dueDate ? dueDate : nextExpenseDate,
//       });

//       const staffExistsInBalance = await staffBalanceDB.getByStaffId({
//         staffId: paidBy,
//       });

//       if (!staffExistsInBalance) {
//         await staffBalanceDB.add({
//           staffId: paidBy,
//           totalCollected: 0,
//           totalGivenToOwner: 0,
//           totalExpense: amount,
//         });
//       } else {
//         await staffBalanceDB.updateTotalExpense({
//           staffId: paidBy,
//           amount: amount,
//         });
//       }
//     }

//     if (paidToUserType === CONSTANTS.USER_TYPE.STAFF && (type === 14 || type === 23 || type === 52 || type === 53) ) {
//       // If the expense is a staff salary, bonus, travel allowance, or HRA log it in ledger

//       let staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.SALARY;
//       let staffLedgerDescription = `Salary`;

//       if (type === 23) {
//         staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.TRAVEL_ALLOWANCE;
//         staffLedgerDescription = `Travel Allowance`;
//       } 
//       else if (type === 52) {
//         staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.BONUS;
//         staffLedgerDescription = `Bonus`;
//       } 
//       else if (type === 53) {
//         staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.HRA;
//         staffLedgerDescription = `HRA`;
//       }

//       await staffLedgerDB.addExpense({
//         staffId: paidTo,
//         amount: amount,
//         type: staffLedgerType,
//         mode: paymentMode,
//         description: staffLedgerDescription + ` paid by ${paidByName}`,
//         expenseId: expenseId,
//         paidDate: moment(paidDate).format("YYYY-MM-DD"),
//         dueDate: dueDate ? dueDate : nextExpenseDate,
//       });
//       await staffLedgerDB.addExpense({
//         staffId: paidTo,
//         amount: -amount,
//         type: staffLedgerType,
//         mode: paymentMode,
//         description: staffLedgerDescription + ` paid by ${paidByName}`,
//         expenseId: expenseId,
//         paidDate: moment(paidDate).format("YYYY-MM-DD"),
//         dueDate: dueDate ? dueDate : nextExpenseDate,
//       });
//     }

//     log.info(
//       `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Is Paid [${isPaid}], Is File Uploaded ${
//         file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
//       }, Expense Added Successfully`
//     );

//     return res.status(200).json({
//       msg: "Expense added successfully",
//       isSuccess: true,
//     });
//   } catch (error: any) {
//     log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

//     if (file) {
//       await removeTmpImages();
//     }

//     return res.status(500).json({
//       msg: CONSTANTS.MSG.ERROR_MESSAGE,
//       isSuccess: false,
//     });
//   }
// };

expense.AddExpenseX = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "AddExpenseX";

  const file = req.file as Express.Multer.File;
  const removeTmpImages = async () => {
    try {
      await fsPromises.unlink(file.path);
    } catch (err) {
      log.info(
        `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
          err
        )}]`
      );
    }
  };

  try {
    let {
      amount,
      type,
      repetitionType,
      paidDate,
      propertyIds=null,
      flatIds=null,
      paidBy,
      paidByUserType,
      paidTo,
      paidToUserType,
      description,
      paymentMode,
      repetitionMonths=1,
      dueDate,
      isPaid = true,
      paymentCycle = null, //Advance or at month end
      paymentAccountNo,
      paymentAccountName,
      assetIds=null, //only one id will come
      expenseNature = null,
      expenseTitle=null,
    } = req.body;

    const userType = req.userType;
    let clientId = req.id;
    let origDueDate = dueDate;
    if(dueDate === "2026-12-01")
      dueDate = "2025-12-01";
    else if(dueDate === "2026-11-01")
      dueDate = "2025-11-01";
    else if(dueDate === "2026-10-01")
      dueDate = "2025-10-01";

    log.info(
      `[${C}], [${F}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}], Description [${description}], Payment Mode [${paymentMode}], Paid Date [${paidDate}], Repetition Months [${repetitionMonths}], Orig Due Date Month [${origDueDate}], Due Date Month [${dueDate}], Is Paid [${isPaid}], Payment Cycle [${paymentCycle}], Payment Account No [${paymentAccountNo}], Payment Account Name [${paymentAccountName}], Asset Id [${assetIds}], Expense Nature [${expenseNature}], Expense Title [${expenseTitle}]`
    );

    if(!expenseNature) {
      expenseNature = CONSTANTS.EXPENSE_NATURE.OPERATING
    }

    amount = Number(amount);
    type = Number(type);
    paidDate = moment(paidDate);
    paidDate = `${paidDate.format("YYYY-MM-DD")} ${moment().format("HH:mm:ss")}`;
    // paidDate = moment(paidDate).format("YYYY-MM-DD HH:mm:ss");
    if (!dueDate || dueDate === null || dueDate === undefined || dueDate === "") {
      dueDate = paidDate;
    }

    if (type === 14 || type === 23 || type === 52 || type === 53) {
      propertyIds = null; // Salary, Bonus, Travel Allowance, and HRA expenses should not be linked to any property as discussed(2026-03-02)
    }

    paidBy = Number(paidBy);
    propertyIds = propertyIds !== null ? propertyIds.split(",").map((s:any) => s.trim()).filter((s:any) => s !== "").map(Number) : null;
    // assetIds = assetIds !== null ? assetIds.split(",").map((s:any) => s.trim()).filter((s:any) => s !== "").map(Number) : null;
    flatIds = flatIds !== null ? flatIds.split(",").map((s:any) => s.trim()).filter((s:any) => s !== "").map(Number) : null;
    paidByUserType = Number(paidByUserType);
    paidTo = Number(paidTo);
    paidToUserType = Number(paidToUserType);
    paymentMode = Number(paymentMode);
    let paidByName = "";
    let paidToName = "";
    let nextExpenseDate = null;
    let recurrenceDate = null;
    let prevExpenseDate = null;
    if(CONSTANTS.REPETITION_TYPE.ONE_TIME === Number(repetitionType)){
      repetitionMonths = 0;
    } else {
      if(paymentCycle) {
        if(Number(paymentCycle) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
          recurrenceDate = moment(paidDate).add(repetitionMonths, 'months').format("YYYY-MM-DD");
          prevExpenseDate = moment(paidDate).subtract(repetitionMonths, 'months').format("YYYY-MM-DD");
        } else {
          recurrenceDate = moment(paidDate).add(repetitionMonths, 'months').format("YYYY-MM-DD");
          nextExpenseDate = moment(paidDate).add(repetitionMonths, 'months').format("YYYY-MM-DD");
        }
      } else {
        recurrenceDate = moment(paidDate).add(repetitionMonths, 'months').format("YYYY-MM-DD");
        nextExpenseDate = moment(paidDate).add(repetitionMonths, 'months').format("YYYY-MM-DD");
      }
    }

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Staff Id [${req.id}], Repetition Months [${repetitionMonths}], Is Paid [${isPaid}], Expense Nature [${expenseNature}], No Staff Found`
        );

        if (file) {
          await fsPromises.unlink(file.path);
        }

        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Is Paid [${isPaid}], Expense Nature [${expenseNature}], Staff Id [${
          req.id
        }], Paid Date [${paidDate}], Is File Uploaded ${
          file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
        }, Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Paid Date [${paidDate}], Repetition Months [${repetitionMonths}], Is Paid [${isPaid}], Expense Nature [${expenseNature}], Is File Uploaded ${
          file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
        }, Client Requested....`
      );
    }

    if (paidByUserType === paidToUserType && paidBy === paidTo) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Is Paid [${isPaid}], Payment Mode [${paymentMode}], Expense Nature [${expenseNature}], You cannot pay yourself`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: "You cannot pay yourself", isSuccess: false });
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Expense Nature [${expenseNature}], No Client Found`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (paidByUserType === CONSTANTS.USER_TYPE.CLIENT) {
      paidByName = client?.name;
    }

    let expenseId = null;
    let url;

    if (flatIds && flatIds.length > 0) {
      const dividedAmount = Math.floor(amount / flatIds.length);
      const leftOverAmount = amount - (dividedAmount * flatIds.length);
      let count = 1;
      let totalBeds = 0;
      let amountDivided = 0;
      let dividingByBeds = false;

      const isExpenseDivideByBedsEnabled = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EXPENSE_ALLOCATION_BY_BEDS,
      });
      
      if(isExpenseDivideByBedsEnabled && Number(isExpenseDivideByBedsEnabled.value) === 1){
        totalBeds = await bedDB.getBedCountByFlatIds({
          flatIds: flatIds,
        });
        dividingByBeds = true;
      }

      let referenceId = null;

      for (let flatId of flatIds) {
        const flat = await flatDB.getById({id: flatId})
        if (!flat) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Flat Id [${flatId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], No Flat Found`
          );
          if (file) {
            await fsPromises.unlink(file.path);
          }
          return res
            .status(400)
            .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
        }

        const property = await propertyDB.getById({ id: Number(flat.propId) });
        if (!property) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${flat.propId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Expense Nature [${expenseNature}], No Property Found`
          );
          if (file) {
            await fsPromises.unlink(file.path);
          }
          return res
            .status(400)
            .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
        }

        let flatAmount = dividedAmount;
        if (dividingByBeds && Number(totalBeds) > 0) {
          const flatBedCount = await bedDB.getBedCountByFlatIds({
            flatIds: [flat.id],
          });
          flatAmount = Math.floor((flatBedCount / totalBeds) * amount);
          amountDivided += flatAmount;
        }
        
        if (count === flatIds.length) {
          if (dividingByBeds && Number(totalBeds) > 0) {
            flatAmount += (amount - amountDivided);
          } else {
            flatAmount += leftOverAmount;
          }
        }
        flatAmount = Number(flatAmount) && Number(flatAmount) > 0 ? Number(flatAmount) : 0;
        if(paymentCycle && Number(paymentCycle) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
          expenseId = await expenseDB.create({
            type,
            amount: flatAmount,
            clientId,
            paidDate: paidDate,
            paidByUserType,
            paidBy,
            paidTo,
            paidToUserType,
            description,
            paymentMethod: paymentMode,
            repetitionType,
            noOfMonths: repetitionMonths,
            dueDate: prevExpenseDate,
            isPaid: isPaid ? 1 : 0,
            paymentAccountNo: paymentAccountNo || null,
            paymentAccountName: paymentAccountName || null,
            assetId: assetIds,
            expenseNature: expenseNature || null,
            expenseTitle: expenseTitle || null,
          });
        } else {
          expenseId = await expenseDB.create({
            type,
            amount: flatAmount,
            clientId,
            paidDate: paidDate,
            paidByUserType,
            paidBy,
            paidTo,
            paidToUserType,
            description,
            paymentMethod: paymentMode,
            repetitionType,
            noOfMonths: repetitionMonths,
            dueDate: dueDate ? dueDate : nextExpenseDate,
            isPaid: isPaid ? 1 : 0,
            paymentAccountNo: paymentAccountNo || null,
            paymentAccountName: paymentAccountName || null,
            assetId: assetIds,
            expenseNature: expenseNature || null,
            expenseTitle: expenseTitle || null,
          });
        }
        //log.info(`Expense created with id 111: ${expenseId}`);
    
        await expenseDB.addProperty({
          expenseId: expenseId,
          propId: Number(flat.propId),
          flatId: flatId,
          clientId: clientId,
        });

        await setExpenseJournalTallyStatus(
          Number(clientId),
          Number(expenseId),
          CONSTANTS.TALLY_STATUS.PENDING,
        );

        if (!isPaid) {
          await logExpenseActivity(
            req.userType!,
            Number(req.id)!,
            Number(req.parentClientId!),
            Number(req.platform),
            CONSTANTS.ACTIVITY_TYPES.ADD_UNPAID_EXPENSE,
            paidBy,
            paidByUserType,
            paidTo,
            paidToUserType,
            type,
            amount,
            paymentMode,
            flat.propId,
          );
        } else {
          await logExpenseActivity(
            req.userType!,
            Number(req.id)!,
            Number(req.parentClientId!),
            Number(req.platform),
            CONSTANTS.ACTIVITY_TYPES.ADD_EXPENSE,
            paidBy,
            paidByUserType,
            paidTo,
            paidToUserType,
            type,
            amount,
            paymentMode,
            flat.propId,
          );
          
          await setExpensePaymentTallyStatus(
            Number(clientId),
            Number(expenseId),
            CONSTANTS.TALLY_STATUS.PENDING,
          );
        }

        if (file) {
          const folderName = `expense_${expenseId}`;
          const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
          const fileExtension = file.mimetype.split("/")[1];
          const filename = `expenseBill.${fileExtension}`;

          if (!fs.existsSync(folderPath)) {
            await fsPromises.mkdir(folderPath, { recursive: true });
          }

          const oldPath = `uploads/tmp/${file.filename}`;
          const newPath = `${folderPath}/${filename}`;
          url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

          await fsPromises.copyFile(oldPath, newPath);

          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
          );
          await expenseDB.updateSupportDoc({
            id: expenseId,
            supportDoc: url,
          });
        }

        if (Number(repetitionType) === 2 && type !== 14) {
          let recurringExpenseId = null;
          if(paymentCycle) {
            if(Number(paymentCycle) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
              recurringExpenseId = await recurringExpenseDB.createNew({
                type,
                amount: flatAmount,
                clientId,
                propId: Number(flat.propId),
                flatId: flatId,
                paidDate,
                paidByUserType,
                paidBy,
                paidTo,
                paidToUserType,
                description,
                paymentMethod: paymentMode,
                noOfMonths: repetitionMonths,
                dueDate: moment(paidDate).format("YYYY-MM-DD"),
                recurrenceDate: recurrenceDate,
                expenceCycleType: Number(paymentCycle),
                expenseCycle: moment(paidDate).date(),
                referenceId: referenceId || null,
                assetId: assetIds,
                expenseNature: expenseNature || null,
                expenseTitle: expenseTitle || null,
              });
            } else {
                recurringExpenseId = await recurringExpenseDB.createNew({
                type,
                amount: flatAmount,
                clientId,
                propId: Number(flat.propId),
                flatId: flatId,
                paidDate,
                paidByUserType,
                paidBy,
                paidTo,
                paidToUserType,
                description,
                paymentMethod: paymentMode,
                noOfMonths: repetitionMonths,
                dueDate: nextExpenseDate,
                recurrenceDate: recurrenceDate,
                expenceCycleType: Number(paymentCycle),
                expenseCycle: moment(paidDate).date(),
                referenceId: referenceId || null,
                assetId: assetIds,
                expenseNature: expenseNature || null,
                expenseTitle: expenseTitle || null,
              });
            }
          } else {
            recurringExpenseId = await recurringExpenseDB.createNew({
                type,
                amount: flatAmount,
                clientId,
                propId: Number(flat.propId),
                flatId: flatId,
                paidDate,
                paidByUserType,
                paidBy,
                paidTo,
                paidToUserType,
                description,
                paymentMethod: paymentMode,
                noOfMonths: repetitionMonths,
                dueDate: nextExpenseDate,
                recurrenceDate: recurrenceDate,
                expenceCycleType: paymentCycle ? Number(paymentCycle): CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.ADVANCE,
                expenseCycle: moment(paidDate).date(),
                referenceId: referenceId || null,
                assetId: assetIds,
                expenseNature: expenseNature || null,
                expenseTitle: expenseTitle || null,
              });
          }

          if (count === 1) {
            referenceId = recurringExpenseId;
            await recurringExpenseDB.updateReferenceId({
              id: recurringExpenseId,
              referenceId: recurringExpenseId,
            });
          }

          await expenseDB.updateRecurringExpenseId({
            id: expenseId,
            recurringExpenseId: recurringExpenseId
          });

          await logExpenseActivity(
            req.userType!,
            Number(req.id)!,
            Number(req.parentClientId!),
            Number(req.platform),
            CONSTANTS.ACTIVITY_TYPES.ADD_RECURRING_EXPENSE,
            paidBy,
            paidByUserType,
            paidTo,
            paidToUserType,
            type,
            amount,
            paymentMode,
            flat.propId,
          );
        }
        count += 1;
      }
    } else if (propertyIds && propertyIds.length > 0) {
      const dividedAmount = Math.floor(amount / propertyIds.length);
      const leftOverAmount = amount - (dividedAmount * propertyIds.length);
      let count = 1;
      let totalBeds = 0;
      let amountDivided = 0;
      let dividingByBeds = false;

      const isExpenseDivideByBedsEnabled = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EXPENSE_ALLOCATION_BY_BEDS,
      });
      
      if(isExpenseDivideByBedsEnabled && Number(isExpenseDivideByBedsEnabled.value) === 1){
        totalBeds = await bedDB.getBedCountByPropIds({
          propIds: propertyIds,
        });
        dividingByBeds = true;
      }

      // log.info(`Amount [${amount}], Divided Amount [${dividedAmount}], LeftOver Amount [${leftOverAmount}]`);
      let referenceId = null;

      for (let propId of propertyIds) {
        // log.info(`Prop Id [${propId}], Number propId [${Number(propId)}]`);

        const property = await propertyDB.getById({ id: Number(propId) });
        if (!property) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], No Property Found`
          );
          if (file) {
            await fsPromises.unlink(file.path);
          }
          return res
            .status(400)
            .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
        }

        let propAmount = dividedAmount;
        if (dividingByBeds && Number(totalBeds) > 0) {
          const propBedCount = await bedDB.getBedCountByPropIds({
            propIds: [property.id],
          });
          propAmount = Math.floor((propBedCount / totalBeds) * amount);
          amountDivided += propAmount;
        }
        
        if (count === propertyIds.length) {
          if (dividingByBeds && Number(totalBeds) > 0) {
            propAmount += (amount - amountDivided);
          } else {
            propAmount += leftOverAmount;
          }
        }
        propAmount = Number(propAmount) && Number(propAmount) > 0 ? Number(propAmount) : 0;
        if(paymentCycle && Number(paymentCycle) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
          expenseId = await expenseDB.create({
            type,
            amount: propAmount,
            clientId,
            paidDate: paidDate,
            paidByUserType,
            paidBy,
            paidTo,
            paidToUserType,
            description,
            paymentMethod: paymentMode,
            repetitionType,
            noOfMonths: repetitionMonths,
            dueDate: prevExpenseDate,
            isPaid: isPaid ? 1 : 0,
            paymentAccountNo: paymentAccountNo || null,
            paymentAccountName: paymentAccountName || null,
            assetId: assetIds,
            expenseNature: expenseNature || null,
            expenseTitle: expenseTitle || null,
          });
        } else {
          expenseId = await expenseDB.create({
            type,
            amount: propAmount,
            clientId,
            paidDate: paidDate,
            paidByUserType,
            paidBy,
            paidTo,
            paidToUserType,
            description,
            paymentMethod: paymentMode,
            repetitionType,
            noOfMonths: repetitionMonths,
            dueDate: dueDate ? dueDate : nextExpenseDate,
            isPaid: isPaid ? 1 : 0,
            paymentAccountNo: paymentAccountNo || null,
            paymentAccountName: paymentAccountName || null,
            assetId: assetIds,
            expenseNature: expenseNature || null,
            expenseTitle: expenseTitle || null,
          });
        }
        //log.info(`Expense created with id 111: ${expenseId}`);
    
        await expenseDB.addProperty({
          expenseId: expenseId,
          propId: Number(propId),
          clientId: clientId,
        });

        await setExpenseJournalTallyStatus(
          Number(clientId),
          Number(expenseId),
          CONSTANTS.TALLY_STATUS.PENDING,
        );

        if (!isPaid) {
          await logExpenseActivity(
            req.userType!,
            Number(req.id)!,
            Number(req.parentClientId!),
            Number(req.platform),
            CONSTANTS.ACTIVITY_TYPES.ADD_UNPAID_EXPENSE,
            paidBy,
            paidByUserType,
            paidTo,
            paidToUserType,
            type,
            amount,
            paymentMode,
            propId,
          );
        } else {
          await logExpenseActivity(
            req.userType!,
            Number(req.id)!,
            Number(req.parentClientId!),
            Number(req.platform),
            CONSTANTS.ACTIVITY_TYPES.ADD_EXPENSE,
            paidBy,
            paidByUserType,
            paidTo,
            paidToUserType,
            type,
            amount,
            paymentMode,
            propId,
          );
          
          await setExpensePaymentTallyStatus(
            Number(clientId),
            Number(expenseId),
            CONSTANTS.TALLY_STATUS.PENDING,
          );
        }

        if (file) {
          const folderName = `expense_${expenseId}`;
          const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
          const fileExtension = file.mimetype.split("/")[1];
          const filename = `expenseBill.${fileExtension}`;

          if (!fs.existsSync(folderPath)) {
            await fsPromises.mkdir(folderPath, { recursive: true });
          }

          const oldPath = `uploads/tmp/${file.filename}`;
          const newPath = `${folderPath}/${filename}`;
          url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

          await fsPromises.copyFile(oldPath, newPath);

          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
          );
          await expenseDB.updateSupportDoc({
            id: expenseId,
            supportDoc: url,
          });
        }

        if (Number(repetitionType) === 2 && type !== 14) {
          let recurringExpenseId = null;
          if(paymentCycle) {
            if(Number(paymentCycle) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
              recurringExpenseId = await recurringExpenseDB.createNew({
                type,
                amount: propAmount,
                clientId,
                propId: Number(propId),
                paidDate,
                paidByUserType,
                paidBy,
                paidTo,
                paidToUserType,
                description,
                paymentMethod: paymentMode,
                noOfMonths: repetitionMonths,
                dueDate: moment(paidDate).format("YYYY-MM-DD"),
                recurrenceDate: recurrenceDate,
                expenceCycleType: Number(paymentCycle),
                expenseCycle: moment(paidDate).date(),
                referenceId: referenceId || null,
                assetId: assetIds,
                expenseNature: expenseNature || null,
                expenseTitle: expenseTitle || null,
              });
            } else {
                recurringExpenseId = await recurringExpenseDB.createNew({
                type,
                amount: propAmount,
                clientId,
                propId: Number(propId),
                paidDate,
                paidByUserType,
                paidBy,
                paidTo,
                paidToUserType,
                description,
                paymentMethod: paymentMode,
                noOfMonths: repetitionMonths,
                dueDate: nextExpenseDate,
                recurrenceDate: recurrenceDate,
                expenceCycleType: Number(paymentCycle),
                expenseCycle: moment(paidDate).date(),
                referenceId: referenceId || null,
                assetId: assetIds,
                expenseNature: expenseNature || null,
                expenseTitle: expenseTitle || null,
              });
            }
          } else {
            recurringExpenseId = await recurringExpenseDB.createNew({
                type,
                amount: propAmount,
                clientId,
                propId: Number(propId),
                paidDate,
                paidByUserType,
                paidBy,
                paidTo,
                paidToUserType,
                description,
                paymentMethod: paymentMode,
                noOfMonths: repetitionMonths,
                dueDate: nextExpenseDate,
                recurrenceDate: recurrenceDate,
                expenceCycleType: paymentCycle ? Number(paymentCycle): CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.ADVANCE,
                expenseCycle: moment(paidDate).date(),
                referenceId: referenceId || null,
                assetId: assetIds,
                expenseNature: expenseNature || null,
                expenseTitle: expenseTitle || null,
              });
          }

          if (count === 1) {
            referenceId = recurringExpenseId;
            await recurringExpenseDB.updateReferenceId({
              id: recurringExpenseId,
              referenceId: recurringExpenseId,
            });
          }

          await expenseDB.updateRecurringExpenseId({
            id: expenseId,
            recurringExpenseId: recurringExpenseId
          });

          await logExpenseActivity(
            req.userType!,
            Number(req.id)!,
            Number(req.parentClientId!),
            Number(req.platform),
            CONSTANTS.ACTIVITY_TYPES.ADD_RECURRING_EXPENSE,
            paidBy,
            paidByUserType,
            paidTo,
            paidToUserType,
            type,
            amount,
            paymentMode,
            propId,
          );
        }
        count += 1;
      }
    } else {
      if(paymentCycle && Number(paymentCycle) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
        expenseId = await expenseDB.create({
            type,
            amount: amount,
            clientId,
            paidDate: paidDate,
            paidByUserType,
            paidBy,
            paidTo,
            paidToUserType,
            description,
            paymentMethod: paymentMode,
            repetitionType,
            noOfMonths: repetitionMonths,
            dueDate: prevExpenseDate,
            isPaid: isPaid ? 1 : 0,
            paymentAccountNo: paymentAccountNo || null,
            paymentAccountName: paymentAccountName || null,
            assetId: assetIds,
            expenseNature: expenseNature || null,
            expenseTitle: expenseTitle || null,
          });
      } else {
        expenseId = await expenseDB.create({
          type,
          amount: amount,
          clientId,
          paidDate: paidDate,
          paidByUserType,
          paidBy,
          paidTo,
          paidToUserType,
          description,
          paymentMethod: paymentMode,
          repetitionType,
          noOfMonths: repetitionMonths,
          dueDate: dueDate ? dueDate : nextExpenseDate,
          isPaid: isPaid ? 1 : 0,
          paymentAccountNo: paymentAccountNo || null,
          paymentAccountName: paymentAccountName || null,
          assetId: assetIds,
          expenseNature: expenseNature || null,
          expenseTitle: expenseTitle || null,
        });
      }

      await setExpenseJournalTallyStatus(
        Number(clientId),
        Number(expenseId),
        CONSTANTS.TALLY_STATUS.PENDING,
      );

      if (!isPaid) {
        await logExpenseActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId!),
          Number(req.platform),
          CONSTANTS.ACTIVITY_TYPES.ADD_UNPAID_EXPENSE,
          paidBy,
          paidByUserType,
          paidTo,
          paidToUserType,
          type,
          amount,
          paymentMode,
        );
      } else {
        await logExpenseActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId!),
          Number(req.platform),
          CONSTANTS.ACTIVITY_TYPES.ADD_EXPENSE,
          paidBy,
          paidByUserType,
          paidTo,
          paidToUserType,
          type,
          amount,
          paymentMode,
        );
        
        await setExpensePaymentTallyStatus(
          Number(clientId),
          Number(expenseId),
          CONSTANTS.TALLY_STATUS.PENDING,
        );
      }
      //log.info(`Expense created with id 222: ${expenseId}`);
      if (file) {
        const folderName = `expense_${expenseId}`;
        const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
        const fileExtension = file.mimetype.split("/")[1];
        const filename = `expenseBill.${fileExtension}`;

        if (!fs.existsSync(folderPath)) {
          await fsPromises.mkdir(folderPath, { recursive: true });
        }

        const oldPath = `uploads/tmp/${file.filename}`;
        const newPath = `${folderPath}/${filename}`;
        url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

        await fsPromises.copyFile(oldPath, newPath);

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
        );
        await expenseDB.updateSupportDoc({
          id: expenseId,
          supportDoc: url,
        });
      }

      if (Number(repetitionType) === 2 && type !== 14) {
        let recurringExpenseId = null;
          if(paymentCycle) {
            if(Number(paymentCycle) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
              recurringExpenseId = await recurringExpenseDB.createNew({
                type,
                amount: amount,
                clientId,
                propId: null,
                paidDate,
                paidByUserType,
                paidBy,
                paidTo,
                paidToUserType,
                description,
                paymentMethod: paymentMode,
                noOfMonths: repetitionMonths,
                dueDate: moment(paidDate).format("YYYY-MM-DD"),
                recurrenceDate: recurrenceDate,
                expenceCycleType: Number(paymentCycle),
                expenseCycle: moment(paidDate).date(),
                referenceId: null,
                assetId: assetIds,
                expenseNature: expenseNature || null,
                expenseTitle: expenseTitle || null,
              });
            } else {
                recurringExpenseId = await recurringExpenseDB.createNew({
                type,
                amount: amount,
                clientId,
                propId: null,
                paidDate,
                paidByUserType,
                paidBy,
                paidTo,
                paidToUserType,
                description,
                paymentMethod: paymentMode,
                noOfMonths: repetitionMonths,
                dueDate: nextExpenseDate,
                recurrenceDate: recurrenceDate,
                expenceCycleType: Number(paymentCycle),
                expenseCycle: moment(paidDate).date(),
                referenceId: null,
                assetId: assetIds,
                expenseNature: expenseNature || null,
                expenseTitle: expenseTitle || null,
              });
            }
          } else {
            recurringExpenseId = await recurringExpenseDB.create({
              type,
              amount,
              clientId,
              propId: null,
              paidDate,
              paidByUserType,
              paidBy,
              paidTo,
              paidToUserType,
              description,
              paymentMethod: paymentMode,
              noOfMonths: repetitionMonths,
              dueDate: nextExpenseDate,
              expenseCycle: moment(paidDate).date(),
              referenceId: null,
              assetId: assetIds,
              expenseNature: expenseNature || null,
            });
          }
        await recurringExpenseDB.updateReferenceId({
          id: recurringExpenseId,
          referenceId: recurringExpenseId,
        });
        await expenseDB.updateRecurringExpenseId({
          id: expenseId,
          recurringExpenseId: recurringExpenseId
        });
        await logExpenseActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId!),
          Number(req.platform),
          CONSTANTS.ACTIVITY_TYPES.ADD_RECURRING_EXPENSE,
          paidBy,
          paidByUserType,
          paidTo,
          paidToUserType,
          type,
          amount,
          paymentMode,
        );
      }
    }

    if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
      log.info(
        `[${C}], [${F}], Staff Id ${paidBy}, amount ${amount} Staff Ledger and Balance Recorded`
      );
      const paidByStaff = await staffDB.getById({ id: paidBy });
      let paidToUser = null;
      if (paidToUserType === CONSTANTS.USER_TYPE.STAFF) {
        paidToUser = await staffDB.getById({ id: paidTo });
      } else {
        paidToUser = await vendorDB.getById({ id: paidTo });
      }

      paidByName = paidByStaff.name;
      paidToName = paidToUser.name;

      await staffLedgerDB.addExpense({
        staffId: paidBy,
        amount: -amount,
        type: CONSTANTS.STAFF_LEDGER_TYPES.EXPENSES,
        mode: paymentMode,
        description: `Expense paid by Staff ${paidByStaff.name} to ${
          paidToUser.name
        } on ${moment().format("YYYY-MM-DD HH:mm:ss")}`,
        expenseId: expenseId,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        dueDate: dueDate ? dueDate : nextExpenseDate,
      });

      const staffExistsInBalance = await staffBalanceDB.getByStaffId({
        staffId: paidBy,
      });

      if (!staffExistsInBalance) {
        await staffBalanceDB.add({
          staffId: paidBy,
          totalCollected: 0,
          totalGivenToOwner: 0,
          totalExpense: amount,
        });
      } else {
        await staffBalanceDB.updateTotalExpense({
          staffId: paidBy,
          amount: amount,
        });
      }
    }

    if (paidToUserType === CONSTANTS.USER_TYPE.STAFF && (type === 14 || type === 23 || type === 52 || type === 53) ) {
      // If the expense is a staff salary, bonus, travel allowance, or HRA log it in ledger

      let staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.SALARY;
      let staffLedgerDescription = `Salary`;

      if (type === 23) {
        staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.TRAVEL_ALLOWANCE;
        staffLedgerDescription = `Travel Allowance`;
      } 
      else if (type === 52) {
        staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.BONUS;
        staffLedgerDescription = `Bonus`;
      } 
      else if (type === 53) {
        staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.HRA;
        staffLedgerDescription = `HRA`;
      }

      await staffLedgerDB.addExpense({
        staffId: paidTo,
        amount: amount,
        type: staffLedgerType,
        mode: paymentMode,
        description: staffLedgerDescription + ` paid by ${paidByName}`,
        expenseId: expenseId,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        dueDate: dueDate ? dueDate : nextExpenseDate,
      });
      await staffLedgerDB.addExpense({
        staffId: paidTo,
        amount: -amount,
        type: staffLedgerType,
        mode: paymentMode,
        description: staffLedgerDescription + ` paid by ${paidByName}`,
        expenseId: expenseId,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        dueDate: dueDate ? dueDate : nextExpenseDate,
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Repetition Type [${repetitionType}], Amount [${amount}], Property Id [${propertyIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Payment Mode [${paymentMode}], Is Paid [${isPaid}], Is File Uploaded ${
        file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
      }, Expense Added Successfully`
    );

    return res.status(200).json({
      msg: "Expense added successfully",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    if (file) {
      await removeTmpImages();
    }

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.AddUnpaidExpense = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "AddUnpaidExpense";

  const file = req.file as Express.Multer.File;
  const removeTmpImages = async () => {
    try {
      await fsPromises.unlink(file.path);
    } catch (err) {
      log.info(
        `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
          err
        )}]`
      );
    }
  };

  try {
    let {
      amount,
      type,
      propertyIds=null,
      flatIds=null,
      paidBy,
      paidByUserType,
      paidTo,
      paidToUserType,
      description,
      dueDate,
      assetIds=null, //only 1 id will come
      expenseNature = null,
      expenseTitle=null,
    } = req.body;

    const userType = req.userType;
    let clientId = req.id;

    log.info(
      `[${C}], [${F}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}], Description [${description}], Due Date Month [${dueDate}], Asset Ids [${assetIds}], Expense Nature [${expenseNature}], Expense Title [${expenseTitle}]`
    );
    
    if(!expenseNature) {
      expenseNature = CONSTANTS.EXPENSE_NATURE.OPERATING;
    }

    amount = Number(amount);
    type = Number(type);
    dueDate = moment(dueDate).utc();
    dueDate = dueDate.tz('Asia/Kolkata').format("YYYY-MM-DD HH:mm:ss");

    if (type === 14 || type === 23 || type === 52 || type === 53) {
      propertyIds = null; // Salary, Bonus, Travel Allowance, and HRA expenses should not be linked to any property as discussed(2026-03-02)
    }

    paidBy = Number(paidBy);
    propertyIds = propertyIds !== null ? propertyIds.split(",").map((s:any) => s.trim()).filter((s:any) => s !== "").map(Number) : null;
    // assetIds = assetIds !== null ? assetIds.split(",").map((s:any) => s.trim()).filter((s:any) => s !== "").map(Number) : null;
    flatIds = flatIds !== null ? flatIds.split(",").map((s:any) => s.trim()).filter((s:any) => s !== "").map(Number) : null;
    paidByUserType = Number(paidByUserType);
    paidTo = Number(paidTo);
    paidToUserType = Number(paidToUserType);
    let paidByName = "";
    let paidToName = "";

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Staff Id [${req.id}], No Staff Found`
        );

        if (file) {
          await fsPromises.unlink(file.path);
        }

        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}], Expense Nature [${expenseNature}], Description [${description}], Staff Id [${
          req.id
        }], Is File Uploaded ${
          file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
        }, Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}], Expense Nature [${expenseNature}], Description [${description}], Is File Uploaded ${
          file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
        }, Client Requested....`
      );
    }

    if (paidByUserType === paidToUserType && paidBy === paidTo) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}], Expense Nature [${expenseNature}], Description [${description}], You cannot pay yourself`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: "You cannot pay yourself", isSuccess: false });
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Flat Ids [${flatIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], No Client Found`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (paidByUserType === CONSTANTS.USER_TYPE.CLIENT) {
      paidByName = client?.name;
    }

    let expenseId = null;
    let url;

    if (flatIds && flatIds.length > 0) {
      // log.info(`1.`);
      const dividedAmount = Math.floor(amount / flatIds.length);
      const leftOverAmount = amount - (dividedAmount * flatIds.length);
      let count = 1;
      let totalBeds = 0;
      let amountDivided = 0;

      let dividingByBeds = false;

      const isExpenseDivideByBedsEnabled = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EXPENSE_ALLOCATION_BY_BEDS,
      });
      
      if(isExpenseDivideByBedsEnabled && Number(isExpenseDivideByBedsEnabled.value) === 1){
        totalBeds = await bedDB.getBedCountByFlatIds({
          flatIds: flatIds,
        });
        dividingByBeds = true;
      }

      for (let flatId of flatIds) {

        const flat = await flatDB.getById({ id: Number(flatId) });
        if (!flat) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Flat Id [${flatId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], No Flat Found`
          );
          if (file) {
            await fsPromises.unlink(file.path);
          }
          return res
            .status(400)
            .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
        }

        // log.info(`Flat [${JSON.stringify(flat)}]`);

        const property = await propertyDB.getById({ id: Number(flat.propId) });
        if (!property) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Flat Id [${flatId}], Property Id [${flat.propId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], No Property Found`
          );
          if (file) {
            await fsPromises.unlink(file.path);
          }
          return res
            .status(400)
            .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
        }

        let flatAmount = dividedAmount;

        if (dividingByBeds && Number(totalBeds) > 0) {
          const flatBedCount = await bedDB.getBedCountByFlatIds({
            flatIds: [flat.id],
          });
          flatAmount = Math.floor((flatBedCount / totalBeds) * amount);
          amountDivided += flatAmount;
        }
        
        if (count === flatIds.length) {
          if (dividingByBeds && Number(totalBeds) > 0) {
            flatAmount += (amount - amountDivided);
          } else {
            flatAmount += leftOverAmount;
          }
        }

        flatAmount = Number(flatAmount) && Number(flatAmount) > 0 ? Number(flatAmount) : 0;

        expenseId = await expenseDB.create({
          type,
          amount: flatAmount,
          clientId,
          paidDate: null,
          paidByUserType,
          paidBy,
          paidTo,
          paidToUserType,
          description,
          paymentMethod: 1,
          repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
          noOfMonths: 0,
          dueDate: dueDate,
          isPaid: 0,
          assetId: assetIds,
          expenseNature: expenseNature,
          expenseTitle: expenseTitle || null,
        });
    
        await expenseDB.addProperty({
          expenseId: expenseId,
          propId: Number(flat.propId),
          flatId: Number(flatId),
          clientId: clientId,
        });

        await logExpenseActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId!),
          Number(req.platform),
          CONSTANTS.ACTIVITY_TYPES.ADD_UNPAID_EXPENSE,
          paidBy,
          paidByUserType,
          paidTo,
          paidToUserType,
          type,
          amount,
          1, //payment method
          flat.propId,
        );

        if (file) {
          const folderName = `expense_${expenseId}`;
          const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
          const fileExtension = file.mimetype.split("/")[1];
          const filename = `expenseBill.${fileExtension}`;

          if (!fs.existsSync(folderPath)) {
            await fsPromises.mkdir(folderPath, { recursive: true });
          }

          const oldPath = `uploads/tmp/${file.filename}`;
          const newPath = `${folderPath}/${filename}`;
          url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

          await fsPromises.copyFile(oldPath, newPath);

          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
          );
          await expenseDB.updateSupportDoc({
            id: expenseId,
            supportDoc: url,
          });
        }
        
        await setExpenseJournalTallyStatus(
          Number(clientId),
          Number(expenseId),
          CONSTANTS.TALLY_STATUS.PENDING,
        );
        count += 1;
      }
    } else if (propertyIds && propertyIds.length > 0) {
      const dividedAmount = Math.floor(amount / propertyIds.length);
      const leftOverAmount = amount - (dividedAmount * propertyIds.length);
      let count = 1;
      let totalBeds = 0;
      let amountDivided = 0;
      let dividingByBeds = false;

      const isExpenseDivideByBedsEnabled = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EXPENSE_ALLOCATION_BY_BEDS,
      });
      
      if(isExpenseDivideByBedsEnabled && Number(isExpenseDivideByBedsEnabled.value) === 1){
        totalBeds = await bedDB.getBedCountByPropIds({
          propIds: propertyIds,
        });
        dividingByBeds = true;
      }

      for (let propId of propertyIds) {

        const property = await propertyDB.getById({ id: Number(propId) });
        if (!property) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propId}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], No Property Found`
          );
          if (file) {
            await fsPromises.unlink(file.path);
          }
          return res
            .status(400)
            .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
        }

        let propAmount = dividedAmount;
        if (dividingByBeds && Number(totalBeds) > 0) {
          const propBedCount = await bedDB.getBedCountByPropIds({
            propIds: [property.id],
          });
          propAmount = Math.floor((propBedCount / totalBeds) * amount);
          amountDivided += propAmount;
        }
        
        if (count === propertyIds.length) {
          if (dividingByBeds && Number(totalBeds) > 0) {
            propAmount += (amount - amountDivided);
          } else {
            propAmount += leftOverAmount;
          }
        }
        propAmount = Number(propAmount) && Number(propAmount) > 0 ? Number(propAmount) : 0;

        expenseId = await expenseDB.create({
          type,
          amount: propAmount,
          clientId,
          paidDate: null,
          paidByUserType,
          paidBy,
          paidTo,
          paidToUserType,
          description,
          paymentMethod: 1,
          repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
          noOfMonths: 0,
          dueDate: dueDate,
          isPaid: 0,
          assetId: assetIds,
          expenseNature: expenseNature,
          expenseTitle: expenseTitle || null,
        });
    
        await expenseDB.addProperty({
          expenseId: expenseId,
          propId: Number(propId),
          clientId: clientId,
        });

        await logExpenseActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId!),
          Number(req.platform),
          CONSTANTS.ACTIVITY_TYPES.ADD_UNPAID_EXPENSE,
          paidBy,
          paidByUserType,
          paidTo,
          paidToUserType,
          type,
          amount,
          1, //payment method
          propId,
        );

        if (file) {
          const folderName = `expense_${expenseId}`;
          const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
          const fileExtension = file.mimetype.split("/")[1];
          const filename = `expenseBill.${fileExtension}`;

          if (!fs.existsSync(folderPath)) {
            await fsPromises.mkdir(folderPath, { recursive: true });
          }

          const oldPath = `uploads/tmp/${file.filename}`;
          const newPath = `${folderPath}/${filename}`;
          url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

          await fsPromises.copyFile(oldPath, newPath);

          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
          );
          await expenseDB.updateSupportDoc({
            id: expenseId,
            supportDoc: url,
          });
        }
        
        await setExpenseJournalTallyStatus(
          Number(clientId),
          Number(expenseId),
          CONSTANTS.TALLY_STATUS.PENDING,
        );
        count += 1;
      }
    } else {
      expenseId = await expenseDB.create({
        type,
        amount: amount,
        clientId,
        paidDate: null,
        paidByUserType,
        paidBy,
        paidTo,
        paidToUserType,
        description,
        paymentMethod: 1,
        repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
        noOfMonths: 0,
        dueDate: dueDate,
        isPaid: 0,
        assetId: assetIds,
        expenseNature: expenseNature,
        expenseTitle: expenseTitle || null,
      });

      await logExpenseActivity(
        req.userType!,
        Number(req.id)!,
        Number(req.parentClientId!),
        Number(req.platform),
        CONSTANTS.ACTIVITY_TYPES.ADD_UNPAID_EXPENSE,
        paidBy,
        paidByUserType,
        paidTo,
        paidToUserType,
        type,
        amount,
        1, //payment method
      );
      
      if (file) {
        const folderName = `expense_${expenseId}`;
        const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
        const fileExtension = file.mimetype.split("/")[1];
        const filename = `expenseBill.${fileExtension}`;

        if (!fs.existsSync(folderPath)) {
          await fsPromises.mkdir(folderPath, { recursive: true });
        }

        const oldPath = `uploads/tmp/${file.filename}`;
        const newPath = `${folderPath}/${filename}`;
        url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

        await fsPromises.copyFile(oldPath, newPath);

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
        );
        await expenseDB.updateSupportDoc({
          id: expenseId,
          supportDoc: url,
        });
      }
      
      await setExpenseJournalTallyStatus(
        Number(clientId),
        Number(expenseId),
        CONSTANTS.TALLY_STATUS.PENDING,
      );
    }
    
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Amount [${amount}], Property Id [${propertyIds}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Paid To [${paidTo}], Paid To UserType [${paidToUserType}] Description [${description}], Is File Uploaded ${
        file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
      }, Expense Added Successfully`
    );

    return res.status(200).json({
      msg: "Expense added successfully",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    if (file) {
      await removeTmpImages();
    }

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.EditExpense = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "EditExpense";

  const file = req.file as Express.Multer.File;
  const removeTmpImages = async () => {
    try {
      await fsPromises.unlink(file.path);
    } catch (err) {
      log.info(
        `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
          err
        )}]`
      );
    }
  };

  try {
    let {
      expenseId,
      amount,
      type,
      paidDate,
      propertyId,
      flatId=null,
      paidBy,
      paidByUserType,
      paidToUserType,
      paidTo,
      description,
      paymentMode,
      repetitionType = 0,
      expenseNature = null,
      expenseTitle = null,
    } = req.body;

    amount = Number(amount);
    type = Number(type);
    //paidDate = moment(paidDate).format("YYYY-MM-DD HH:mm:ss");
    paidDate = `${moment(paidDate).format("YYYY-MM-DD")} ${moment().format("HH:mm:ss")}`;
    propertyId = Number(propertyId);
    flatId = Number(flatId) || null;
    paidBy = Number(paidBy);
    paidByUserType = Number(paidByUserType);
    paidTo = Number(paidTo);
    paidToUserType = Number(paidToUserType);
    paymentMode = Number(paymentMode);
    expenseId = Number(expenseId);

    const userType = req.userType;
    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Expense Id [${expenseId}], Type [${type}], Amount [${amount}], Property Id [${propertyId}], FlatId [${flatId}], Paid By [${paidBy}], Paid By User Type [${paidByUserType}], Paid To User Type [${paidToUserType}], Paid To [${paidTo}], Description [${description}], Payment Mode [${paymentMode}], Staff Id [${req.id}], Expense Nature [${expenseNature}], Expense Title [${expenseTitle}], No Staff Found`
        );

        if (file) {
          await fsPromises.unlink(file.path);
        }

        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Type [${type}], Amount [${amount}], Property Id [${propertyId}], FlatId [${flatId}], Paid By [${paidBy}], Paid By User Type [${paidByUserType}], Paid To [${paidTo}], Paid To User Type [${paidToUserType}], Description [${description}], Payment Mode [${paymentMode}], Staff Id [${
          req.id
        }], Paid Date [${paidDate}], Expense Nature [${expenseNature}], Expense Title [${expenseTitle}], Is File Uploaded ${
          file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
        }, Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Type [${type}], Amount [${amount}], Property Id [${propertyId}], FlatId [${flatId}], Paid By [${paidBy}], Paid By User Type [${paidByUserType}], Paid To [${paidTo}], Paid To User Type [${paidToUserType}], Description [${description}], Payment Mode [${paymentMode}], Paid Date [${paidDate}], Expense Nature [${expenseNature}], Expense Title [${expenseTitle}], Is File Uploaded ${
          file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
        }, Client Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Type [${type}], Amount [${amount}], Property Id [${propertyId}], FlatId [${flatId}], Paid By [${paidBy}], Paid By User Type [${paidByUserType}], Paid To [${paidTo}], Description [${description}], Payment Mode [${paymentMode}], No Client Found`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const expense = await expenseDB.getById({ id: expenseId });
    if (!expense) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Type [${type}], Amount [${amount}], Property Id [${propertyId}], Paid By [${paidBy}], Paid By User Type [${paidByUserType}], Paid To [${paidTo}], Description [${description}], Payment Mode [${paymentMode}], Is File Uploaded ${
          file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
        }, No Expense Found`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    // const linkedProperties = await expenseDB.getProperties({ expenseId });

    // if (linkedProperties) {
    //   const isAlreadyLinked = linkedProperties.some(
    //     (property: { id: number }) => property.id === propertyId
    //   );

    //   if (isAlreadyLinked) {
    //     log.info(
    //       `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Type [${type}], Amount [${amount}], Property Id [${propertyId}], Paid By [${paidBy}], Paid By User Type [${paidByUserType}], Paid To [${paidTo}], Description [${description}], Payment Mode [${paymentMode}], Property Already Linked`
    //     );
    //     return res.status(400).json({
    //       msg: "Expense is already linked with this property",
    //       isSuccess: false,
    //     });
    //   }
    // }

    const property = await propertyDB.getById({ id: propertyId });
    const flat = await flatDB.getById({ id: flatId });
    //Commented by Sukhbir in 16th Jan 2025
    //await expenseDB.removeProperty({ clientId, propId: propertyId, expenseId });

    let changeInPaidByStaff = false;

    log.info(`Expense Paid by [${expense.paidBy}], New Paid by [${paidBy}]`);

    if (
      expense.paidByUserType === CONSTANTS.USER_TYPE.STAFF &&
      expense.paidBy !== paidBy
    ) {
      changeInPaidByStaff = true;
    } else {
      changeInPaidByStaff = false;
    }

    if (changeInPaidByStaff) {
      // await staffLedgerDB.deleteExpense({
      //   staffId: expense.paidBy,
      //   expenseId: expenseId,
      // });

      if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
        await staffLedgerDB.updateStaffByExpenseId({
          expenseId: expenseId,
          staffId: paidBy,
        });
      } else {
        await staffLedgerDB.deleteLedgerByExpenseId({
          expenseId: expenseId,
        });
      }

      // subtracting the amount from StaffBalances upon deleting the expense
      await staffBalanceDB.updateTotalExpense({
        staffId: paidBy,
        amount: -expense.amount,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Type [${type}], Amount [${amount}], Deleted the expense from StaffId [${expense.paidBy}]`
      );

      if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
        const paidByStaff = await staffDB.getById({ id: paidBy });
        let paidToUser = null;
        if (paidToUserType === CONSTANTS.USER_TYPE.STAFF) {
          paidToUser = await staffDB.getById({ id: paidTo });
        } else {
          paidToUser = await vendorDB.getById({ id: paidTo });
        }

        // await staffLedgerDB.addExpense({
        //   staffId: paidBy,
        //   amount: -amount,
        //   type: CONSTANTS.STAFF_LEDGER_TYPES.EXPENSES,
        //   mode: paymentMode,
        //   description: `Expense paid by Staff ${paidByStaff.name} to ${
        //     paidToUser.name
        //   } on ${moment().format("YYYY-MM-DD HH:mm:ss")}`,
        //   expenseId: expenseId,
        //   paidDate: moment(paidDate).format("YYYY-MM-DD"),
        //   dueDate: expense.dueDate,
        // });

        const staffExistsInBalance = await staffBalanceDB.getByStaffId({
          staffId: paidBy,
        });

        if (!staffExistsInBalance) {
          await staffBalanceDB.add({
            staffId: paidBy,
            totalCollected: 0,
            totalGivenToOwner: 0,
            totalExpense: amount,
          });
        } else {
          await staffBalanceDB.updateTotalExpense({
            staffId: paidBy,
            amount: amount,
          });
        }

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${paidBy}], amount ${amount}, Staff ledger and balance recorded for new paidBy staff`
        );
      }
    }

    if (expense.paidToUserType === CONSTANTS.USER_TYPE.STAFF && expense.paidToUserType !== paidToUserType) {
      if (expense.type === 14 ||
        expense.type === 23 ||
        expense.type === 52 ||
        expense.type === 53
      ) {
        if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
          await staffLedgerDB.updateStaffByExpenseId({
            expenseId: expenseId,
            staffId: paidBy,
          });
        } else {
          await staffLedgerDB.deleteLedgerByExpenseId({
            expenseId: expenseId,
          });
        }
      }
    }

    // if (expense.balance !== amount && !changeInPaidByStaff) {
    if (expense.amount !== amount && paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
      await staffLedgerDB.updateExpenseAmount({
        staffId: paidBy,
        expenseId: expenseId,
        amount: amount,
      });
      const amountDiff = amount - expense.balance;
      await staffBalanceDB.updateTotalExpense({
        staffId: paidBy,
        amount: amountDiff,
      });
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${paidBy}], New Amount ${amount}, Old Amount [${expense.amount}], Updated the Amount in Staff Ledger`
      );
    }

    let finalAmt = amount;

    if (expense.balance !== amount && expense.balance !== expense.amount) {
      finalAmt = expense.amount + (amount - expense.balance);
    }

    await expenseDB.update({
      type,
      amount: finalAmt,
      balance: amount,
      paidDate: moment(paidDate).format("YYYY-MM-DD HH:mm:ss"),
      paidByUserType,
      paidToUserType,
      paidBy,
      paidTo,
      description,
      paymentMethod: paymentMode,
      id: expenseId,
      expenseTitle: expenseTitle || null,
    });

    if(expenseNature) {
      await expenseDB.updateExpenseNature ({
        expenseNature,
        id: expenseId
      });
    }
    await logExpenseActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId!),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.EDIT_EXPENSE,
      paidBy,
      paidByUserType,
      paidTo,
      paidToUserType,
      type,
      amount,
      paymentMode,
      propertyId,
    );


    if (property) {
      const expensePropertyRecord = await expenseDB.getPropertyByExpenseId({
        expenseId,
      });
  
      if (expensePropertyRecord) {
        await expenseDB.updateProperty({
          clientId,
          propId: propertyId,
          flatId: Number(flatId) || null,
          expenseId,
        });
      } else {
        await expenseDB.addProperty({
          expenseId: expenseId,
          propId: Number(propertyId),
          flatId: Number(flatId) || null,
          clientId: clientId,
        });
      }
    }

    if (file) {
      const folderName = `expense_${expenseId}`;
      const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
      const fileExtension = file.mimetype.split("/")[1];
      const filename = `expenseBill.${fileExtension}`;

      if (!fs.existsSync(folderPath)) {
        await fsPromises.mkdir(folderPath, { recursive: true });
      }

      const oldPath = `uploads/tmp/${file.filename}`;
      const newPath = `${folderPath}/${filename}`;
      const url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

      await fsPromises.copyFile(oldPath, newPath);

      await expenseDB.updateSupportDoc({
        id: expenseId,
        supportDoc: url,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
      );
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Type [${type}], Amount [${amount}], Property Id [${propertyId}], Flat Id [${flatId}], Paid By [${paidBy}], Paid By User Type [${paidByUserType}], Paid To [${paidTo}], Description [${description}], Payment Mode [${paymentMode}], Is File Uploaded ${
        file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
      }, Expense Updated Successfully`
    );

    return res.status(200).json({
      msg: "Expense Updated Successfully",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    await removeTmpImages();

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

//Have to remove this after sometime, latest version of this api is below
expense.ListForClient = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "ListForClient";

  try {
    const { pageNum, propId, m, s, y } = req.query;

    const userType = req.userType;

    // let clientId = req.id;

    log.info(
      `[${C}], [${F}], Page Num [${pageNum}], Prop Id [${propId}], Staff Id [${req.id}], Month [${m}], Year [${y}], Search Val [${s}]`
    );

    const month = Number(m) || 0;
    const year = Number(y) || 0;

    if (!month || month > 12 || !year) {
      log.info(
        `[${C}], [${F}], Page Num [${pageNum}], Prop Id [${propId}], Staff Id [${req.id}], Month [${month}], Year [${year}], Search Val [${s}], Not a Valid Month or Year`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let date = moment()
      .month(month - 1)
      .year(year)
      .format("YYYY-MM-DD");

    let list = [];
    let curMonthExpense = 0;
    let previousMonthExpense = 0;
    let diff = 0;

    const limit = 10;

    // let isPartner = false;

    // if (userType === CONSTANTS.USER_TYPE.STAFF) {
    //   const staff = await staffDB.getById({ id: req.id });
    //   if (!staff) {
    //     log.info(`[${C}], [${F}], Staff Id [${req.id}] No Staff Found`);
    //     return res
    //       .status(400)
    //       .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    //   }
    //   isPartner = staff.role === CONSTANTS.STAFF_ROLES.PARTNER ? true : false;
    //   clientId = staff.clientId;
    // }

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Page Num [${pageNum}], Prop Id [${propId}], Month [${month}], Year [${year}], Search Val [${s}], ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }...`
      );

      const client = await clientDB.getById({ id: clientId });

      if (!client) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Prop Id [${propId}], Month [${month}], Year [${year}], Search Val [${s}], No Client Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (s && s !== "undefined" && s !== "null" && s !== " ") {
        list = await expenseDB.getSearchResult({
          clientId,
          pageNum: pageNum || 1,
          limit,
          searchVal: s,
        });
      } else {
        list = await expenseDB.getByClientId({
          clientId,
          pageNum: pageNum || 1,
          limit,
          month: date,
        });

        curMonthExpense = await expenseDB.getTotalByClientId({
          clientId,
          month: date,
        });

        previousMonthExpense = await expenseDB.getTotalByClientId({
          clientId,
          month: moment(date).subtract(1, "month").format("YYYY-MM-DD"),
        });
        if (previousMonthExpense) {
          diff =
            ((curMonthExpense - previousMonthExpense) / previousMonthExpense) *
            100;
        } else {
          if (!curMonthExpense) diff = 0;
          else diff = 100;
        }
      }
    } else {
      const staffId = req.id;
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Prop Id [${propId}], Month [${month}], Year [${year}], Search Val [${s}], Staff Id [${staffId}], Staff Requested....`
      );

      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Page Num [${pageNum}], Prop Id [${propId}], Staff Id [${staffId}], Month [${month}], Year [${year}], Search Val [${s}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId,
      });

      if (staffLinkedProps) {
        const propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");

        if (s && s !== "undefined" && s !== "null" && s !== " ") {
          list = await expenseDB.getSearchResultForStaff({
            clientId,
            pageNum: pageNum || 1,
            limit,
            searchVal: s,
            propertiesIds,
          });
        } else {
          list = await expenseDB.getByClientIdForStaff({
            clientId,
            pageNum: Number(pageNum),
            limit,
            propertiesIds,
            month: date,
          });
          curMonthExpense = await expenseDB.getTotalByClientIdForStaff({
            clientId,
            month: date,
            propertiesIds,
          });
          previousMonthExpense = await expenseDB.getTotalByClientIdForStaff({
            clientId,
            month: moment(date).subtract(1, "month").format("YYYY-MM-DD"),
            propertiesIds,
          });
          if (previousMonthExpense) {
            diff =
              ((curMonthExpense - previousMonthExpense) /
                previousMonthExpense) *
              100;
          } else {
            if (!curMonthExpense) diff = 0;
            else diff = 100;
          }
        }
      }
    }

    if (!list) list = [];

    for (const expense of list) {
      const propList = await expenseDB.getProperties({
        expenseId: expense?.id,
      });
      // if (!propList) {
      //   log.info(
      //     `[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Prop Id [${propId}], Expense Id [${expense.id}], Month [${month}], Year [${year}], Search Val [${s}], No Property Found`
      //   );
      // }

      expense.propertyList = propList || [];
      
      const flatList = await expenseDB.getFlats({
        expenseId: expense?.id,
      });

      expense.flatList = flatList || [];

    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Prop Id [${propId}], Month [${month}], Year [${year}], Search Val [${s}], Expense List Sent Successfully`
    );

    return res.status(200).json({
      msg: "Expense list has been sent successfully",
      data: {
        list,
        totalExpense: Number(curMonthExpense) || 0,
        previousMonthExpense: Number(previousMonthExpense) || 0,
        diff: Number(diff.toFixed(2)) || 0,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

//To support latest version with Date Range
expense.ListForClientX = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "ListForClientX";

  try {
    let { pageNum, propId, s, filter, status, filterVal, startDate, endDate, propFilters, catFilters, typeFilters, staffFilters, sortBy="DD" } = req.query;

    const userType = req.userType;

    if (catFilters && typeof catFilters === 'string') {
      catFilters = catFilters.split(',').map(v => v.trim()).filter(Boolean);
    }
    if (typeFilters && typeof typeFilters === 'string') {
      typeFilters = typeFilters.split(',').map(v => v.trim()).filter(Boolean);
    }
    if (propFilters && typeof propFilters === 'string') {
      propFilters = propFilters.split(',').map(v => v.trim()).filter(Boolean);
    }
    if (staffFilters && typeof staffFilters === 'string') {
      staffFilters = staffFilters.split(',').map(v => v.trim()).filter(Boolean);
    }

    log.info(
      `[${C}], [${F}], Page Num [${pageNum}], Prop Id [${propId}], Req Id [${req.id}], Start Date [${startDate}], End Date [${endDate}], Search Val [${s}], Filter [${filter}], Status [${status}], Filter Value [${filterVal}], Prop Filters [${propFilters}], Category Filters [${catFilters}], Type Filters [${typeFilters}], Staff Filter [${staffFilters}], Sort By [${sortBy}]`
    );

    let list = [];
    let initialValues = [];
    let totalExpense = 0;
    let propList = [];
    let staffList = [];
    let unPaidExist = false;
    let recurringExist = false;
    let recurringExpenseCount = 0;
    let unPaidExpenseCount = 0;
    let securityExpenseAmount = 0;

    const limit = 10;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    //To make sortBy as PD by default for all Clients expect 113(tushar) -- 2026-07-31 after siash issue for due date confusion
    if (String(sortBy) !== "DD" && Number(clientId) !== 113) {
      sortBy = "PD";
    }

    let { staffSalaryPermission } = await staffSalaryModulePermission(
      Number(userType),
      Number(req.id)
    );

    let { isFinanceAdmin } = await isUserFinanceAdmin(
      Number(userType),
      Number(req.id)
    );

    let isStaffAllowed = false;
    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({
        id: req.id
      });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Search Val [${s}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      isStaffAllowed = await isPrivilegedStaff(staff.role, 1);
    }

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner || isFinanceAdmin || isStaffAllowed) {
      log.info(
        `[${C}], [${F}], ${
          isFinanceAdmin ? `Finance Admin Id [${req.id}]` : isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Page Num [${pageNum}], Prop Id [${propId}], Search Val [${s}], ${
          isFinanceAdmin ? `Finance Admin Requesting` : isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );

      const client = await clientDB.getById({ id: clientId });
      if (!client) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Prop Id [${propId}], Start Date [${startDate}], End Date [${endDate}], Search Val [${s}], Filter [${filter}], No Client Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      recurringExpenseCount = await recurringExpenseDB.getCountByClientIdAndPageNum({
        clientId
      });
      unPaidExpenseCount = await expenseDB.getCountUnMarkedByClientId({
        clientId
      });
      if (s && s !== "undefined" && s !== "null" && s !== " ") {
        list = await expenseDB.getSearchResult({
          clientId,
          pageNum: pageNum || 1,
          limit,
          searchVal: s,
        });
      } else if (status === "RE") {
        //recurring expenses
        list = await recurringExpenseDB.getByClientIdAndPageNumAndCategory({
          clientId,
          categoryFilter: catFilters || [],
          pageNum: pageNum || 1,
          limit,
        });
        // log.info(`Recurring List [${JSON.stringify(list)}]`);
      } else if (status === "UM") {
        //un-marked expense
        if (filter !== '' && Number(filter) === 0) {
          list = await expenseDB.getUnMarkedByClientIdAndOthers({
            clientId: clientId,
            pageNum: pageNum || 1,
            limit: limit,
          });
        } else if (filter !== '' && Number(filter)) {
          list = await expenseDB.getUnMarkedByTypeAndClientId({
            clientId: clientId,
            type: Number(filter),
            pageNum: pageNum || 1,
            limit: limit,
          });
        } else if (catFilters) {
          list = await expenseDB.getUnMarkedByCategoryAndClientId({
            clientId: clientId,
            categoryFilter: catFilters || [],
            startDate,
            endDate,
            pageNum: pageNum || 1,
            limit: limit,
          });
        } else {
          list = await expenseDB.getUnMarkedByClientIdAndDateFilter({
            clientId: clientId,
            startDate,
            endDate,
            pageNum: pageNum || 1,
            limit: limit,
          });
        }
      } else if (filter) {
        if (filter !== '' && Number(filter) === 0) {
          list = await expenseDB.getByClientIdAndOthers({
            clientId: clientId,
            pageNum: pageNum || 1,
            limit: limit,
            startDate: startDate,
            endDate: endDate,
          });
        } else {
          list = await expenseDB.getByTypeAndClientId({
            clientId: clientId,
            type: Number(filter),
            pageNum: pageNum || 1,
            limit: limit,
            startDate: startDate,
            endDate: endDate,
          });
        }
      } else if (typeFilters) {
        //Building Security hardcoded in typeFilters
        list = await expenseDB.getSecurityExpensesByClientId({
          clientId,
          limit,
          pageNum: pageNum || 1,
        });
      } else {
        list = await expenseDB.getByClientIdandDateRangeAndFilters({
          clientId,
          startDate: startDate,
          endDate: endDate,
          limit,
          pageNum: pageNum || 1,
          propIds: propFilters || null,
          categories: catFilters || null,
          types: typeFilters || null,
          staffs: staffFilters || null,
          sortBy,
        });
      }

      if (status === "RE") {
        totalExpense = 0;
        initialValues = await expenseDB.getInitialValuesForListForUnMarked({
          clientId,
        });
      } else if (status === "UM") {
        totalExpense = await expenseDB.getTotalUnMarkedByClientId({
            clientId,
        });
        initialValues = await expenseDB.getInitialValuesForListForUnMarked({
          clientId,
        });
      } else {
        totalExpense = await expenseDB.getTotalByClientIdandDateRangeAndFilters({
          clientId,
          startDate,
          endDate,
          propIds: propFilters || null,
          categories: catFilters || null,
          types: typeFilters || null,
          staffs: staffFilters || null,
          sortBy,
        });

        if(Number(clientId) === 93) log.info(JSON.stringify(totalExpense));

        initialValues = await expenseDB.getInitialValuesByClientIdandDateRangeAndFilters({
          clientId,
          startDate,
          endDate,
          propIds: propFilters || null,
        });
      } 
      propList = await propertyDB.getActivePropIdsByClientId({
        clientId,
        status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
      });
      staffList = await staffDB.getActiveByClientId({
        clientId,
      });

      unPaidExist = await expenseDB.isUnPaidExistByClientId({
        clientId
      });
      recurringExist = await recurringExpenseDB.isUnPaidExistByClientId({
        clientId
      });

      // securityExpenseAmount = await expenseDB.getTotalByClientIdAndType({
      //   clientId,
      //   type: 43,
      // });
      securityExpenseAmount = await expenseDB.getTotalByClientIdAndTypeForLandlord({
        clientId,
        type: 43,
      });
    } else {
      const staffId = req.id;
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Prop Id [${propId}], Start Date [${startDate}], End Date [${endDate}], Search Val [${s}], Staff Id [${staffId}], Staff Requested.....`
      );

      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Page Num [${pageNum}], Prop Id [${propId}], Staff Id [${staffId}], Start Date [${startDate}], End Date [${endDate}], Search Val [${s}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId,
      });

      if (staffLinkedProps) {
        const propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");

          recurringExpenseCount = await recurringExpenseDB.getCountByClientIdAndPageNumForStaff({
            clientId,
            propertiesIds
          });
          unPaidExpenseCount = await expenseDB.getCountUnMarkedByClientIdAndDateRangeAndPropFilterForStaff({
            clientId,
            propertiesIds
          });
        if (s && s !== "undefined" && s !== "null" && s !== " ") {
          list = await expenseDB.getSearchResultForStaff({
            clientId,
            pageNum: pageNum || 1,
            limit,
            searchVal: s,
            propertiesIds,
          });
        } else if (status === "RE") {
          //recurring expenses
          list = await recurringExpenseDB.getByClientIdAndPageNumAndCategoryForStaff({
            clientId,
            pageNum: pageNum || 1,
            limit,
            propertiesIds,
            categoryFilter: catFilters || [],
          });
        } else if (status === "UM") {
          if (filter !== '' && Number(filter) === 0) {
            list = await expenseDB.getUnMarkedByClientIdAndOthersForStaff({
              clientId: clientId,
              pageNum: pageNum || 1,
              limit: limit,
              propertiesIds: propertiesIds,
            });
          } else if (filter !== '' && Number(filter)) {
            list = await expenseDB.getUnMarkedByTypeAndClientIdForStaff({
              clientId: clientId,
              type: Number(filter),
              pageNum: pageNum || 1,
              limit: limit,
              propertiesIds: propertiesIds,
            });
          }  else if (catFilters) {
            list = await expenseDB.getUnMarkedByCategoryAndClientIdForStaff({
              clientId: clientId,
              categoryFilter: catFilters || [],
              startDate,
              endDate,
              pageNum: pageNum || 1,
              limit: limit,
              propertiesIds: propertiesIds,
            });
          } else {
            list = await expenseDB.getUnMarkedByClientIdAndDateRangeAndPropFilterForStaff({
              clientId: clientId,
              startDate,
              endDate,
              pageNum: pageNum || 1,
              limit: limit,
              propertiesIds: propertiesIds,
            });
          }
        } else if (filter) {
        if (filter !== '' && Number(filter) === 0) {
          list = await expenseDB.getByClientIdAndOthersForStaff({
            clientId: clientId,
            pageNum: pageNum || 1,
            limit: limit,
            startDate: startDate,
            endDate: endDate,
            propertiesIds: propertiesIds,
          });
        } else {
          list = await expenseDB.getByTypeAndClientIdForStaff({
            clientId: clientId,
            type: Number(filter),
            pageNum: pageNum || 1,
            limit: limit,
            startDate: startDate,
            endDate: endDate,
            propertiesIds: propertiesIds,
          });
        }
      } else if (typeFilters) {
        //Building Security hardcoded in typeFilters
        list = await expenseDB.getSecurityExpensesByClientIdForStaff({
          clientId,
          limit,
          pageNum: pageNum || 1,
          propertiesIds: propertiesIds,
        });
      } else {
        list = await expenseDB.getByClientIdandDateRangeAndFiltersForStaff({
          clientId,
          startDate: startDate,
          endDate: endDate,
          limit,
          pageNum: pageNum || 1,
          propIds: propFilters || null,
          categories: catFilters || null,
          propertiesIds,
          types: typeFilters || null,
          staffs: staffFilters || null,
        });
      }

      propList = staffLinkedProps;
      staffList = await staffDB.getActiveByClientId({
        clientId,
      });

      if (status === "RE") {
        totalExpense = 0;
        initialValues = await expenseDB.getInitialValuesForListStaffUnMarked({
          clientId,
          propertiesIds,
        });
      } else if (status === "UM") {
        totalExpense = await expenseDB.getUnMarkedTotalByClientIdForStaff({
          clientId,
          propertiesIds,
        });
        initialValues = await expenseDB.getInitialValuesForListStaffUnMarked({
          clientId,
          propertiesIds,
        });
      } else {
        totalExpense = await expenseDB.getTotalByClientIdandDateRangeAndFiltersForStaff({
          clientId,
          startDate,
          endDate,
          propIds: propFilters || null,
          categories: catFilters || null,
          propertiesIds,
          types: typeFilters || null,
          staffs: staffFilters || null,
        });
        initialValues = await expenseDB.getInitialValuesByClientIdandDateRangeAndFiltersForStaff({
          clientId,
          startDate,
          endDate,
          propIds: propFilters || null,
          propertiesIds,
        });
      }

      unPaidExist = await expenseDB.isUnPaidExistByClientIdForStaff({
        clientId,
        propertiesIds: propertiesIds,
      });
      recurringExist = await recurringExpenseDB.isUnPaidExistByClientIdForStaff({
        clientId,
        propertiesIds: propertiesIds,
      });
      // securityExpenseAmount = await expenseDB.getTotalByClientIdAndTypeForStaff({
      //   clientId,
      //   type: 43,
      //   propertiesIds: propertiesIds,
      // });
      securityExpenseAmount = await expenseDB.getTotalByClientIdAndTypeForStaffForLandlord({
        clientId,
        type: 43,
        propertiesIds: propertiesIds,
      });
      }
    }

    const categoryList = initialValues.map(
      (item: { expenseCategoryId: number; amount: string; expenseCategoryName: string }) => {
        const { expenseCategoryId, expenseCategoryName, ...rest } = item;
        return {
          ...rest,
          name: expenseCategoryName,
          id: expenseCategoryId
        };
      }
    );

    if (!list) list = [];

    for (const expense of list) {
      if (status === "RE") {
        expense.propertyList = expense?.propName ? [{id: expense.propId, name: expense.propName}] : [];
        expense.flatList = expense?.flatName ? [{id: expense.flatId, name: expense.flatName}] : [];
      } else {
        const propList = await expenseDB.getProperties({
          expenseId: expense?.id,
        });

        const flatList = await expenseDB.getFlats({
          expenseId: expense?.id,
        });
  
        expense.propertyList = propList || [];
        expense.flatList = flatList || [];
      }

      if (!staffSalaryPermission) {
        list = list.filter((expense: any) => Number(expense.type) !== 14);
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Prop Id [${propId}], Start Date [${startDate}], End Date [${endDate}], Search Val [${s}], Total Expense [${totalExpense}], Filter [${filter}], Status [${status}], Filter Value [${filterVal}], Expense List Sent Successfully`
    );

    return res.status(200).json({
      msg: "Expense list has been sent successfully",
      data: {
        list: list || [],
        initialValues,
        staffList,
        categoryList,
        propList,
        totalExpense: Number(totalExpense) || 0,
        unPaidExist,
        recurringExist,
        recurringExpenseCount,
        unPaidExpenseCount,
        securityExpenseAmount,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.ListStats = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "ListStats";

  try {
    const { filter, filterVal, startDate, endDate } = req.query;

    log.info(`[${C}], [${F}], Filter [${filter}], Filter Val [${filterVal}], Start Date [${startDate}], End Date [${endDate}]`);

    const userType = req.userType;

    let initialValues = [];
    let totalExpense = [];
    let isStaffAllowed = false;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({
        id: req.id,
      });
      isStaffAllowed = await isPrivilegedStaff(staff.role, 1);
    }

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    let { isFinanceAdmin } = await isUserFinanceAdmin(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner || isFinanceAdmin || isStaffAllowed) {
      log.info(
        `[${C}], [${F}], ${isFinanceAdmin ? `Finance Admin Id [${req.id}]` : isPartner ? `Partner Id [${req.id}]` : isStaffAllowed ? `Staff Id [${req.id}]` : `Client Id [${clientId}]`}, Filter [${filter}], Filter Val [${filterVal}], ${isFinanceAdmin ? "Finance Admin" : isPartner ? "Partner" : isStaffAllowed ? "Staff" : "Client"} Requesting....`
      );

      if (filter === "PR") {
        initialValues = await expenseDB.getInitialValuesForListPropFilter({
          clientId,
          startDate,
          endDate,
          propIds: filterVal,
        });
        totalExpense = await expenseDB.getTotalByClientIdAndDateRangeAndPropFilter({
            clientId,
            startDate,
            endDate,
            propIds: filterVal,
        });
      } else {
        initialValues = await expenseDB.getInitialValuesForList({
          clientId,
          startDate,
          endDate,
        });
        totalExpense = await expenseDB.getTotalByClientIdAndDateRange({
            clientId,
            startDate,
            endDate,
        });
      }
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Unauthorized Access`
        );
        return res.status(400).json({ 
            msg: "Unauthorized Access", 
            isSuccess: false 
          });
      }

      clientId = staff.clientId;
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Filter [${filter}], Filter Val [${filterVal}], Staff Id [${req.id}], Staff Role [${staff.role}], ${staff.role === CONSTANTS.STAFF_ROLES.ADMIN ? "Admin" : "Warden"} Requested.....`
      );

      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId: staff.id,
      });

      if (staffLinkedProps) {
        const propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");

        if (filter === "PR") {
          initialValues = await expenseDB.getInitialValuesForListStaffPropFilter({
            clientId,
            startDate,
            endDate,
            propertiesIds: propertiesIds,
            propIds: filterVal,
          });
          totalExpense = await expenseDB.getTotalByClientIdAndDateRangeAndPropFilterForStaff({
            clientId,
            propertiesIds,
            startDate,
            endDate,
            propIds: filterVal,
          });
        } else {
          initialValues = await expenseDB.getInitialValuesForListStaff({
            clientId,
            startDate,
            endDate,
            propertiesIds,
          });
          totalExpense = await expenseDB.getTotalByClientIdAndDateRangeForStaff({
            clientId,
            propertiesIds,
            startDate,
            endDate,
          });
        }
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Filter [${filter}], Filter Val [${filterVal}], Start Date [${startDate}], End Date [${endDate}], Initial Values Sent Successfully`
    );

    return res.status(200).json({
      msg: "Initial values sent successfully.",
      data: {
        initialValues,
        totalExpense: Number(totalExpense) || 0,
      },
      isSuccess: true,
    });

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.ToggleProperty = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "ToggleProperty";

  try {
    const { toggleValue, propId, expenseId } = req.body;

    const userType = req.userType;

    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Toggle Value [${toggleValue}], Property Id [${propId}], expenseId [${expenseId}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Toggle Value [${toggleValue}], Property Id [${propId}], expenseId [${expenseId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Toggle Value [${toggleValue}], Property Id [${propId}], expenseId [${expenseId}], Client Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Toggle Value [${toggleValue}], Property Id [${propId}], expenseId [${expenseId}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const property = await propertyDB.getById({ id: propId });
    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], expenseId [${expenseId}], No Property Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const expense = await expenseDB.getById({ id: expenseId });

    if (!expense) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], expenseId [${expenseId}], No Expense Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (toggleValue === CONSTANTS.TOGGLES.LINK) {
      const isExists = await expenseDB.getByPropAndType({
        clientId,
        propId,
        type: expense.type,
      });

      if (isExists) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], expenseId [${expenseId}], Already Linked to this property.`
        );

        return res.status(400).json({
          msg: `Property already linked`,
          isSuccess: false,
        });
      }

      await expenseDB.addProperty({ clientId, propId, expenseId });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], expenseId [${expenseId}], Property linked Successfully`
      );
    } else {
      await expenseDB.removeProperty({ clientId, propId, expenseId });
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], expenseId [${expenseId}], Property unlinked Successfully`
      );
    }

    return res.status(200).json({
      msg: `Property ${
        toggleValue === CONSTANTS.TOGGLES.LINK ? "linked" : "unlinked"
      } successfully`,
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.ExpenseDetail = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "ExpenseDetail";

  try {
    const { expenseId } = req.params;

    const userType = req.userType;

    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Expense Id [${expenseId}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Client Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const expense = await expenseDB.getById({ id: expenseId });
    if (!expense) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], No Expense Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let expenseCategories = await expenseDB.getExpenseCategories();

    for (let category of expenseCategories) {
      const items = await expenseDB.getExpenseTypes({
        categoryId: category.id,
      });
      category.items = items || [];
    }

    const clientObj = {
      id: client.id,
      userType: CONSTANTS.USER_TYPE.CLIENT,
      name: client.name,
      mobile: client.mobile,
    };

    let users = await staffDB.getActiveByClientId({ clientId });
    if (!users) users = [];

    if (users) users = [clientObj, ...users];
    else users = [clientObj];

    let vendors = await vendorDB.getByClientId({ clientId });
    if (!vendors) vendors = [];

    vendors = [...users, ...vendors];

    const propertyList = await propertyDB.getAllActiveByClientId({ clientId });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Expense Details Sent Successfully`
    );

    return res.status(200).json({
      msg: "Expense details has been sent successfully",
      data: {
        expense,
        expenseCategories,
        users,
        vendors,
        propertyList,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.InitialValues = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "InitialValues";

  try {
    const userType = req.userType;
    // let clientId = req.id;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Client Requested....`);
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], No Client Found`);
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let expenseCategories = await expenseDB.getExpenseCategories();

    //expenseCategories = expenseCategories.filter((cat: { id: number }) => cat.id !== 4); //Skipping Staffs & wages category, as discussed(2026-02-24), now staff expense will be added through staff details screen 

    for (let category of expenseCategories) {
      let items = await expenseDB.getExpenseTypes({
        categoryId: category.id,
      });
      // if (items) items = items.filter((item: { id: number }) => item.id !== 54);
      category.items = items || [];
    }

    const clientObj = {
      id: client.id,
      userType: CONSTANTS.USER_TYPE.CLIENT,
      name: client.name,
      mobile: client.mobile,
    };

    const parentClient = await clientDB.getParentByClientId({
      id: clientId,
    });
    let parentClientObj:any = [];
    if (parentClient) {
      parentClientObj = [{
        id: parentClient.id,
        userType: CONSTANTS.USER_TYPE.CLIENT,
        name: parentClient.name,
        mobile: parentClient.mobile,
      }];
    }

    let users = await staffDB.getActiveByClientId({ clientId });
    if (!users) users = [];

    let vendors = await vendorDB.getByClientId({ clientId });
    if (!vendors) vendors = [];

    vendors = [...users, ...vendors];

    if (users) users = [clientObj, ...parentClientObj, ...users];
    else users = [clientObj, ...parentClientObj,];

    let propertyList = await propertyDB.getAllActiveByClientId({ clientId });

    if (userType === CONSTANTS.USER_TYPE.STAFF && !isPartner) {
      propertyList = await propertyDB.getAllActiveByClientIdForStaff({
        clientId,
        staffId: req.id,
      });
    }

    if (propertyList && propertyList.length > 0) {
      for (let property of propertyList) {
        const flats = await flatDB.getByPropId({ propId: property.id });
        property.flats = flats || [];
      }
    }

    const assets = await assetsDB.getByClientIdAndFilters({
      clientId,
      status: CONSTANTS.ASSETS_STATUS.ACTIVE,
      type: [CONSTANTS.ASSET_CATEGORIES.VEHICLE],
    });

    if (assets && assets.length > 0) {
      for (let asset of assets) {
        const vehicleNumInfo = await assetsDB.getParticularAssetInfoByAssetId({
          assetId: asset.id,
          type: CONSTANTS.ASSET_INFO_TYPES.VEHICLE_NUMBER,
        });
        if (vehicleNumInfo) {
          asset.vehicleNum = vehicleNumInfo?.value;
        } 
        else {
          asset.vehicleNum = null;
        }
      }
    }

    let landlords = await landlordDB.getByClientIdX({
      clientId,
    });
    
    if (!landlords) landlords = [];
    vendors = [...landlords, ...vendors];

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Expense Details Sent Successfully`
    );

    return res.status(200).json({
      msg: "Expense details has been sent successfully",
      data: {
        expenseCategories,
        users,
        vendors,
        landlords: landlords || [],
        assets: assets || [],
        propertyList,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.Delete = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "Delete";

  try {
    const { propId, expenseId } = req.body;

    log.info(`[${C}], [${F}], Expense Id [${expenseId}], Prop Id [${propId}]`);

    const userType = req.userType;

    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], ExpenseId [${expenseId}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Prop Id [${propId}], Client Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (propId && Number(propId)) {
      const property = await propertyDB.getById({ id: propId });
      if (!property) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Expense Id [${expenseId}], No Property Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
    }

    const expense = await expenseDB.getById({ id: expenseId });

    if (!expense) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Expense Id [${expenseId}], No Expense Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await staffLedgerDB.deleteExpense({
      staffId: expense.paidBy,
      expenseId: expenseId,
    });

    await staffLedgerDB.deleteExpense({
      staffId: expense.paidTo,
      expenseId: expenseId,
    });

    await staffBalanceDB.updateTotalExpense({
      staffId: expense.paidBy,
      amount: -expense.amount,
    });

    // if (Number(expense.type) === 54) {
    //   const commission = await staffCommissionDB.getById({ id: expense.recurringExpenseId });
      
    //   if (commission) {
    //     await staffLedgerDB.updateInitalAmount({
    //       commissionId: commission.id,
    //       amount: -Number(commission.amount) - Number(expense.amount),
    //     });        
  
    //     await staffCommissionDB.updateAmount({
    //       id: commission.id,
    //       amount: Number(commission.amount) - Number(expense.amount),
    //     });
    //   }
    // }

    if (propId && Number(propId)) {
      await expenseDB.removeProperty({ clientId, propId, expenseId });
    }
    await expenseDB.delete({ clientId, expenseId });

    await logExpenseActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId!),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.DELETE_EXPENSE,
      expense?.paidBy,
      expense?.paidByUserType,
      expense?.paidTo,
      expense?.paidToUserType,
      expense?.type,
      expense?.amount,
      expense?.paymentMethod,
      propId,
    );

    return res.status(200).json({
      msg: `Expense Deleted successfully`,
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.DeleteUnPaidExpenses = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "DeleteUnPaidExpenses";

  try {
    const { propId, expenseId } = req.body;

    log.info(`[${C}], [${F}], Expense Id [${expenseId}], Prop Id [${propId}]`);

    const userType = req.userType;

    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], ExpenseId [${expenseId}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Prop Id [${propId}], Client Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (propId && Number(propId)) {
      const property = await propertyDB.getById({ id: propId });
      if (!property) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Expense Id [${expenseId}], No Property Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
    }

    const expense = await expenseDB.getUnpaidById({ id: expenseId });

    if (!expense) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Expense Id [${expenseId}], No Expense Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await staffLedgerDB.deleteExpense({
      staffId: expense.paidBy,
      expenseId: expenseId,
    });

    await staffLedgerDB.deleteExpense({
      staffId: expense.paidTo,
      expenseId: expenseId,
    });

    // await staffBalanceDB.updateTotalExpense({
    //   staffId: expense.paidBy,
    //   amount: -expense.amount,
    // });

    if (propId && Number(propId)) {
      await expenseDB.removeProperty({ clientId, propId, expenseId });
    }
    await expenseDB.delete({ clientId, expenseId });

    await staffLedgerDB.deleteLedgerByExpenseId({
      expenseId: expenseId,
    });

    log.info(
          `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}, Expense Deleted successfully`
    );
    await logExpenseActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId!),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.DELETE_EXPENSE,
      expense?.paidBy,
      expense?.paidByUserType,
      expense?.paidTo,
      expense?.paidToUserType,
      expense?.type,
      expense?.amount,
      expense?.paymentMethod,
      propId,
      expense?.isPaid
    );

    return res.status(200).json({
      msg: `Expense Deleted successfully`,
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.ListForWeb = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "ListForWeb";

  try {
    let { startDate, endDate, propId, s, filter, typeFilter, t, status, sortBy="DD" } = req.query;

    const userType = req.userType;
    let pageNum = 1;
    //let date = moment().format("YYYY-MM-DD");
    let list = [];
    let securityExpenseAmount = 0;

    if (typeFilter && typeof typeFilter === 'string') {
      typeFilter = typeFilter.split(',').map(v => v.trim()).filter(Boolean);
    }

    log.info(
      `[${C}], [${F}], Page Num [${pageNum}], Prop Id [${propId}], Id [${req.id}], Start Date [${startDate}], End Date [${endDate}], Search Val [${s}], Filter [${filter}], Sub Filter [${typeFilter}], T [${t}], Status [${status}], Sort By [${sortBy}]`
    );

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    //To make sortBy as PD by default for all Clients expect 113(tushar) -- 2026-07-31 after siash issue for due date confusion
    if (String(sortBy) !== "DD" && Number(clientId) !== 113) {
      sortBy = "PD";
    }

    let { staffSalaryPermission } = await staffSalaryModulePermission(
      Number(userType),
      Number(req.id)
    );

    const limit = 10e9;
    let totalExpense = 0;
    let recurringExpenseCount = 0;
    let unPaidExpenseCount = 0;
    let expenseStats: any = {};

    let isStaffAllowed = false;
    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({
        id: req.id
      });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Search Val [${s}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      isStaffAllowed = await isPrivilegedStaff(staff.role, 1);
    }

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner || isStaffAllowed) {
      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Page Num [${pageNum}], Prop Id [${propId}], Search Val [${s}], ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );

      const client = await clientDB.getById({ id: clientId });

      if (!client) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Prop Id [${propId}], Search Val [${s}], No Client Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (Number(propId) && Number(propId) !== 0) {
        if (s && s !== "undefined" && s !== "null" && s !== " ") {
          if (t === "1") {
            list = await expenseDB.getByPaidByName({
              propId,
              searchVal: s,
            });
          } else if (t === "2") {
            list = await expenseDB.getByPaidToName({
              propId,
              searchVal: s,
            });
          } else {
            let searchVal;
            if (s.toString().toLowerCase() === "upi") {
              searchVal = CONSTANTS.TRANSACTION_MODES.UPI;
            } else if (s.toString().toLowerCase() === "card") {
              searchVal = CONSTANTS.TRANSACTION_MODES.CARD;
            } else if (
              s.toString().toLowerCase() === "netbanking" ||
              s.toString().toLowerCase() === "net banking" ||
              s.toString().toLowerCase() === "net_banking"
            ) {
              searchVal = CONSTANTS.TRANSACTION_MODES.NET_BANKING;
            } else {
              searchVal = CONSTANTS.TRANSACTION_MODES.OFFLINE;
            }
            list = await expenseDB.getByPaymentMethod({
              propId,
              searchVal: searchVal,
            });
          }
        } else if (filter === "RE") {
          //recurring expenses
          if(Array.isArray(typeFilter) && typeFilter.length > 0) {
            list = await recurringExpenseDB.getByClientIdAndPropIdAndFiltersAndPageNum({
              clientId,
              propId,
              pageNum: pageNum || 1,
              limit: limit,
              typeFilter: typeFilter,
            });
          } else {
            list = await recurringExpenseDB.getByClientIdAndPropIdAndPageNum({
              clientId,
              propId,
              pageNum: pageNum || 1,
              limit: limit,
            });
          }
          // log.info(`Recurring List [${JSON.stringify(list)}]`);
        } else if (filter === "UM") {
          //un-marked expense
          // if (filter !== '' && Number(filter) === 0) {
          //   list = await expenseDB.getUnMarkedByClientIdAndPropIdAndOthers({
          //     clientId: clientId,
          //     propId,
          //     pageNum: pageNum || 1,
          //     limit: limit,
          //   });
          // } else if (filter !== '' && Number(filter)) {
          //   list = await expenseDB.getUnMarkedByTypeAndClientIdAndPropId({
          //     clientId: clientId,
          //     propId,
          //     type: Number(filter),
          //     pageNum: pageNum || 1,
          //     limit: limit,
          //   });
          // } else {
            if (Array.isArray(typeFilter) && typeFilter.length > 0) {
              list = await expenseDB.getUnMarkedByClientIdAndPropIdAndFilters({
                clientId: clientId,
                propId,
                pageNum: pageNum || 1,
                limit: limit,
                typeFilter: typeFilter,
              });
            } else {
              list = await expenseDB.getUnMarkedByClientIdAndPropId({
                clientId: clientId,
                propId,
                pageNum: pageNum || 1,
                limit: limit,
              });
            }
          // }
        }  else if (filter === "SE") {
          //Building Security hardcoded in typeFilters
          list = await expenseDB.getSecurityExpensesByPropId({
            clientId,
            propId,
            limit,
            pageNum: pageNum || 1,
          });
        } else {
          if (filter === "CME") {
            //Cur Month Expenses
            list = await expenseDB.getByPropIdForCurMonth({ propId });
          } else if (filter === "TE") {
            // Today's expenses
            list = await expenseDB.getByPropIdForToday({ propId });
          } else if (filter === "CRE") {
            // Cur Month Rent Expenses
            list = await expenseDB.getByPropIdAndTypeForCurMonth({ propId, categoryId: CONSTANTS.EXPENSE_CATEGORIES.RENT_UTILITIES });
          } else if (filter === "CSE") {
            // Cur Month Salary Expenses
            list = await expenseDB.getByPropIdAndTypeForCurMonth({ propId, categoryId: CONSTANTS.EXPENSE_CATEGORIES.STAFF_WAGES });
          } else if (filter === "CFE") {
            // Cur Month Food Expenses
            list = await expenseDB.getByPropIdAndTypeForCurMonth({ propId, categoryId: CONSTANTS.EXPENSE_CATEGORIES.FOOD_SUPPLIES });
          } else if (filter === "BRE") {
            list = await expenseDB.getByPropIdAndDateRangeAndCategoryFilter({
              propId,
              limit: 10e9,
              pageNum: 1,
              startDate,
              endDate,
              categories: `${CONSTANTS.EXPENSE_CATEGORIES.RENT_UTILITIES}`
            });
          } else if (filter === "VE") {
            list = await expenseDB.getVendorExpenseByPropIdAndDateRange({
              propId,
              limit: 10e9,
              pageNum: 1,
              startDate,
              endDate,
            });
          } else if (filter === "EMIE") {
            list = await expenseDB.getEMIExpenseByPropIdAndDateRange({
              propId,
              limit: 10e9,
              pageNum: 1,
              startDate,
              endDate,
            });
          } else if (filter === "SSE") { //Staff Salary Expense
            list = await expenseDB.getByPropIdAndDateRangeAndCategoryFilter({
              propId,
              limit: 10e9,
              pageNum: 1,
              startDate,
              endDate,
              categories: `${CONSTANTS.EXPENSE_CATEGORIES.STAFF_WAGES}`
            });
          } if (startDate && endDate && Array.isArray(typeFilter) && typeFilter.length > 0) {
            list = await expenseDB.getByPropIdandDateRangeAndTypeFilter({
              propId,
              startDate,
              endDate,
              typeFilter: typeFilter,
              sortBy: sortBy,
            });
          } if (startDate && endDate) {
            list = await expenseDB.getByPropIdandDateRange({
              propId,
              startDate,
              endDate,
              sortBy: sortBy,
            });
          } else {
            list = await expenseDB.getByPropId({ propId });
          }
        }
        if (status === "RE") {
          totalExpense = 0;
          // initialValues = [];
        } else if (status === "UM") {
          totalExpense = await expenseDB.getTotalUnMarkedByClientIdAndPropId({
              clientId,
              propId,
          });
          // initialValues = await expenseDB.getInitialValuesForListForUnMarked({
          //   clientId,
          // });
        }

        recurringExpenseCount = await recurringExpenseDB.getCountByClientIdAndPropId({
          clientId,
          propId,
        });
        
        unPaidExpenseCount = await expenseDB.getCountUnMarkedByClientIdAndPropId({
          clientId,
          propId,
        });

        // expenseStats = await expenseDB.getExpenseStatsForWeb({ clientId, propId });
        expenseStats = await expenseDB.getExpenseStatsByPropIdAndDateRangeForWeb({ propId, startDate, endDate, sortBy });
      } else {
        if (s && s !== "undefined" && s !== "null" && s !== " ") {
          if(filter === "RE") {
              list = await recurringExpenseDB.getByClientIdAndREFilter({
                clientId,
                searchVal: s,
                searchType: Number(t),
              });
          } else if(filter === "UM") {
            if(1 === Number(t)) {
               list = await expenseDB.getUnMarkedByClientIdBypaidByName({
                 clientId,
                 searchVal: s,
               });
              } else if (2 === Number(t)) {
              list = await expenseDB.getUnMarkedByClientIdBypaidToMobile({
                clientId,
                searchVal: s,
              });
            } else {
              list = await expenseDB.getUnMarkedByClientId({
              clientId: clientId,
              pageNum: pageNum || 1,
              limit: limit,
              });
            }
          } 
          else {
            if (t === "1") {
              list = await expenseDB.getByPaidByNameByClientId({
                clientId,
                searchVal: s,
              });
            } else if (t === "2") {
              list = await expenseDB.getByPaidToNameByClientId({
                clientId,
                searchVal: s,
              });
            } else if (Number(t) === 4) {
              list = await expenseDB.getByPropNameByClientId({
                clientId,
                searchVal: s,
              });
            } else {
              let searchVal;
              if (s.toString().toLowerCase() === "upi") {
                searchVal = CONSTANTS.TRANSACTION_MODES.UPI;
              } else if (s.toString().toLowerCase() === "card") {
                searchVal = CONSTANTS.TRANSACTION_MODES.CARD;
              } else if (
                s.toString().toLowerCase() === "netbanking" ||
                s.toString().toLowerCase() === "net banking" ||
                s.toString().toLowerCase() === "net_banking"
              ) {
                searchVal = CONSTANTS.TRANSACTION_MODES.NET_BANKING;
              } else {
                searchVal = CONSTANTS.TRANSACTION_MODES.OFFLINE;
              }
              list = await expenseDB.getByClientIdAndPaymentMethod({
                clientId,
                searchVal: searchVal,
              });
            }
          }
        } else if (filter === "RE") {
          //recurring expenses
          if (Array.isArray(typeFilter) && typeFilter.length > 0) {
            list = await recurringExpenseDB.getByClientIdAndFiltersAndPageNum({
              clientId,
              pageNum: pageNum || 1,
              limit: limit,
              typeFilter: typeFilter,
            });
          } else {
            list = await recurringExpenseDB.getByClientIdAndPageNum({
              clientId,
              pageNum: pageNum || 1,
              limit: limit,
            });
          }
          // log.info(`Recurring List [${JSON.stringify(list)}]`);
        } else if (filter === "UM") {
          //un-marked expense
          // if (filter !== '' && Number(filter) === 0) {
          //   list = await expenseDB.getUnMarkedByClientIdAndPropIdAndOthers({
          //     clientId: clientId,
          //     propId,
          //     pageNum: pageNum || 1,
          //     limit: limit,
          //   });
          // } else if (filter !== '' && Number(filter)) {
          //   list = await expenseDB.getUnMarkedByTypeAndClientIdAndPropId({
          //     clientId: clientId,
          //     propId,
          //     type: Number(filter),
          //     pageNum: pageNum || 1,
          //     limit: limit,
          //   });
          // } else {
            if (Array.isArray(typeFilter) && typeFilter.length > 0) {
              list = await expenseDB.getUnMarkedByClientIdAndFilters({
                clientId: clientId,
                pageNum: pageNum || 1,
                limit: limit,
                typeFilter: typeFilter,
              });
            } else {
              list = await expenseDB.getUnMarkedByClientId({
                clientId: clientId,
                pageNum: pageNum || 1,
                limit: limit,
              });
            }
          // }
        } else if (filter === "SE") {
          //Building Security hardcoded in typeFilters
          list = await expenseDB.getSecurityExpensesByClientId({
            clientId,
            limit,
            pageNum: pageNum || 1,
          });
        } else {
          if (filter === "CME") {
            //Cur Month Expenses
            list = await expenseDB.getByClientIdForCurMonth({ clientId });
          } else if (filter === "TE") {
            // Today's expenses
            list = await expenseDB.getByClientIdForToday({ clientId });
          } else if (filter === "CRE") {
            // Cur Month Rent Expenses
            list = await expenseDB.getByClientIdAndTypeForCurMonth({ clientId, categoryId: CONSTANTS.EXPENSE_CATEGORIES.RENT_UTILITIES });
          } else if (filter === "CSE") {
            // Cur Month Salary Expenses
            list = await expenseDB.getByClientIdAndTypeForCurMonth({ clientId, categoryId: CONSTANTS.EXPENSE_CATEGORIES.STAFF_WAGES });
          } else if (filter === "CFE") {
            // Cur Month Food Expenses
            list = await expenseDB.getByClientIdAndTypeForCurMonth({ clientId, categoryId: CONSTANTS.EXPENSE_CATEGORIES.FOOD_SUPPLIES });
          } else if (filter === "BRE") {
            list = await expenseDB.getByClientIdAndDateRangeAndCategoryFilter({
              clientId,
              limit: 10e9,
              pageNum: 1,
              startDate,
              endDate,
              categories: `${CONSTANTS.EXPENSE_CATEGORIES.RENT_UTILITIES}`,
              type: 1, //Rent Type
            });
          } else if (filter === "VE") {
            list = await expenseDB.getVendorExpenseByClientIdAndDateRange({
              clientId,
              limit: 10e9,
              pageNum: 1,
              startDate,
              endDate,
            });
          } else if (filter === "EMIE") {
            list = await expenseDB.getEMIExpenseByClientIdAndDateRange({
              clientId,
              limit: 10e9,
              pageNum: 1,
              startDate,
              endDate,
              categories: `${CONSTANTS.EXPENSE_CATEGORIES.RENT_UTILITIES}`
            });
          } else if (filter === "SSE") { //Staff Salary Expense
            list = await expenseDB.getByClientIdAndDateRangeAndCategoryFilter({
              clientId,
              limit: 10e9,
              pageNum: 1,
              startDate,
              endDate,
              categories: `${CONSTANTS.EXPENSE_CATEGORIES.STAFF_WAGES}`,
              type: 14 //Salary
            });
          } else if (startDate && endDate && Array.isArray(typeFilter) && typeFilter.length > 0) {
            list = await expenseDB.getByClientIdandDateRangeAndTypeFilter({
              clientId,
              limit: limit,
              pageNum: 1,
              startDate: startDate,
              endDate: endDate,
              typeFilter: typeFilter,
              sortBy: sortBy,
            });
          } else if (startDate && endDate) {
            list = await expenseDB.getByClientIdandDateRange({
              clientId,
              limit: limit,
              pageNum: 1,
              startDate: startDate,
              endDate: endDate,
              sortBy: sortBy,
            });
          } else {
            list = await expenseDB.getByClientIdForWeb({ clientId });
          }
        }
        if (status === "RE") {
          totalExpense = 0;
          // initialValues = [];
        } else if (status === "UM") {
          totalExpense = await expenseDB.getTotalUnMarkedByClientId({
              clientId,
          });
          // initialValues = await expenseDB.getInitialValuesForListForUnMarked({
          //   clientId,
          // });
        }

        recurringExpenseCount = await recurringExpenseDB.getCountByClientIdAndPageNum({
          clientId,
          limit: limit,
          pageNum: 1,
        });
        
        unPaidExpenseCount = await expenseDB.getCountUnMarkedByClientId({
          clientId,
        });

        // expenseStats = await expenseDB.getExpenseStatsByClientIdForWeb({ clientId });
        expenseStats = await expenseDB.getExpenseStatsByClientIdAndDateRangeForWeb({
          clientId, 
          startDate, 
          endDate,
          sortBy,
        });
      }
    } else {
      const staffId = req.id;
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Prop Id [${propId}], Search Val [${s}], Staff Id [${staffId}], Staff Requested....`
      );

      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Page Num [${pageNum}], Prop Id [${propId}], Staff Id [${staffId}], Search Val [${s}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      clientId = staff.clientId;
      if (startDate && endDate) {
        list = await expenseDB.getByPropIdandDateRange({
          propId,
          startDate,
          endDate,
          sortBy: sortBy,
        });
      } else {
        list = await expenseDB.getByPropId({ propId });
      }
    }

    if (!list) list = [];
    for (const expense of list) {
      if (filter === "RE") {
        expense.propertyList = expense?.propName ? [{id: expense.propId, name: expense.propName}] : [];
        expense.flatList = expense?.flatName ? [{id: expense.flatId, name: expense.flatName}] : [];
        let asset = null;
        if (expense.assetId) {
          asset = await assetsDB.getById({
            id: expense.assetId,
          });
          const vehicleNumInfo = await assetsDB.getParticularAssetInfoByAssetId({
            assetId: asset.id,
            type: CONSTANTS.ASSET_INFO_TYPES.VEHICLE_NUMBER,
          });
          if (vehicleNumInfo) {
            asset.vehicleNum = vehicleNumInfo?.value;
          } 
          else {
            asset.vehicleNum = null;
          }
        } 

        expense.asset = asset;

        const beneficiaryRecords = await payoutBeneficiaryDB.getAllByUserIdAndUserType({
          userId: Number(expense.paidTo),
          userType: Number(expense.paidToUserType),
          clientId,
        });

        let isPayoutEnabled = 0;
        const isPayoutEnabledConfig = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_PAYOUT_ENABLED,
        });
        if (isPayoutEnabledConfig && Number(isPayoutEnabledConfig.value)) {
          isPayoutEnabled = Number(isPayoutEnabledConfig.value);
        }

        let canConvertToAutopay = 0;

        if (beneficiaryRecords && beneficiaryRecords.length > 0 && Number(isPayoutEnabled) === 1) {
          canConvertToAutopay = 1;
        }

        expense.canConvertToAutopay = canConvertToAutopay;
        expense.beneficiaryRecords = beneficiaryRecords;

        if (Number(expense.isAutopay) === 1) {
          const autopay = await autopayDB.getById({
            id: expense?.autopayId,
          });

          if (autopay) {
            expense.currentBeneficiaryId = autopay?.beneficiaryId;
          } else {
            expense.currentBeneficiaryId = null;
          }
        } else {
          expense.currentBeneficiaryId = null;
        }

      } else {
        if(filter === "UM") {
          let accountDetails : any  = false;
          accountDetails = await payoutBeneficiaryDB.getAllByUserIdAndUserType({
            userId: Number(expense.paidTo),
            userType: Number(expense.paidToUserType),
            clientId
          });
          if(accountDetails && accountDetails.length > 0) {
            accountDetails.forEach((accountDetail: any) => {
              accountDetail.isLastPaymentOnline =  !!accountDetail?.lastPaidAmount;
            });
          }
          expense.canPayOnline = accountDetails ? true : false;
          expense.accounts = accountDetails || [];
        }
        const propList = await expenseDB.getProperties({
          expenseId: expense?.id,
        });
  
        expense.propertyList = propList || [];
        
        const flatList = await expenseDB.getFlats({
          expenseId: expense?.id,
        });        
        expense.flatList = flatList || [];

        let asset = null;
        if (expense.assetId) {
          asset = await assetsDB.getById({
            id: expense.assetId,
          });
          const vehicleNumInfo = await assetsDB.getParticularAssetInfoByAssetId({
            assetId: asset.id,
            type: CONSTANTS.ASSET_INFO_TYPES.VEHICLE_NUMBER,
          });
          if (vehicleNumInfo) {
            asset.vehicleNum = vehicleNumInfo?.value;
          } 
          else {
            asset.vehicleNum = null;
          }
        } 

        expense.asset = asset;
      }

      if (!staffSalaryPermission) {
        list = list.filter((expense: any) => Number(expense.type) !== 14);
      }
    }
    // if(filter === "UM" && s && s !== "undefined" && s !== "null" && s !== " ")    {
    //   for (let i = 0; i < list.length; i++) {
    //     if (list[i].propertyList[0].name.toLowerCase().includes(s)) {
    //         list.splice(i, 1);
    //         i--; // important to adjust index
    //     }
    //   }
    // }

    securityExpenseAmount = await expenseDB.getTotalByClientIdAndTypeForLandlord({
      clientId,
      type: 43,
    });

    expenseStats.securityExpenseAmount = securityExpenseAmount;

    if (!staffSalaryPermission) {
      expenseStats.staffSalaryExpense = 0;
    }

    const pendingExpenseAmount = await expenseDB.getTotalUnMarkedByClientId({
      clientId,
    });

    expenseStats.pendingExpenseAmount = pendingExpenseAmount || 0;

    if(filter === "UM") {
      const isPayoutEnabledConfig = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_PAYOUT_ENABLED,
      });
      let isPayoutEnabled = 0;
      if (isPayoutEnabledConfig && Number(isPayoutEnabledConfig.value)) {
        isPayoutEnabled = Number(isPayoutEnabledConfig.value);
      }
      expenseStats.balance = 0;
      if (isPayoutEnabled != 0) {
        let balance = await cashfreePayout.getWalletBalance(C, F, clientId);
        if (!balance.error) {
          expenseStats.balance = Number(balance?.availableBalance);
        } else {
          expenseStats.balance = 0;
        }
        let payoutData = await getWalletBalanceAndLimits({
          clientId,
          userType: Number(userType),
          staffId: userType === CONSTANTS.USER_TYPE.STAFF ? Number(req.id) : null,
        });
        expenseStats.payoutData = payoutData;
        expenseStats.balance = payoutData?.walletBalance || expenseStats.payoutBalance;
      }
    }
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Prop Id [${propId}], Search Val [${s}], Expense List Sent Successfully`
    );

    return res.status(200).json({
      msg: "Expense list has been sent successfully",
      data: {
        list,
        summary: expenseStats || [],
        totalExpense,
        recurringExpenseCount,
        unPaidExpenseCount,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.Pay = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "Pay";

  const file = req.file as Express.Multer.File;
  const removeTmpImages = async () => {
    try {
      await fsPromises.unlink(file.path);
    } catch (err) {
      log.info(
        `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
          err
        )}]`
      );
    }
  };

  try {
    let { expenseId, mode, paidDate, amount, paidBy, paidByUserType } = req.body;

    paidDate = moment(paidDate).format("YYYY-MM-DD HH:mm:ss");
    mode = Number(mode);
    expenseId = Number(expenseId);

    log.info(
      `[${C}], [${F}], Expense Id [${expenseId}], Amount [${amount}], Payment Mode [${mode}], Paid Date [${paidDate}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Is File Uploaded ${file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"},`
    );

    const userType = req.userType;
    let paidByName = "";

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById ({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);

        if (file) {
          await fsPromises.unlink(file.path);
        }

        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Only Admin, Warden, Back Office and Finance Admin Allowed`);
        return res.status(400).json({
          msg: "Unauthorised Access",
          isSuccess: false,
        });
      }

      log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin/Warden Requesting`)
    }

    const client = await clientDB.getById({
      id: clientId,
    });

    const expense = await expenseDB.getById({
      id: expenseId,
    });

    const expenseProperty = await expenseDB.getExpensePropertyById({id: expenseId});

    if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({
        id: paidBy,
      });
      paidByName = staff.name;
    } else if (paidByUserType === CONSTANTS.USER_TYPE.CLIENT) {
      const client = await clientDB.getById({
        id: paidBy,
      });
      paidByName = client.name;
    } else if (paidByUserType === CONSTANTS.USER_TYPE.VENDOR) {
      const vendor = await vendorDB.getById({
        id: paidBy,
      });
      paidByName = vendor.name;
    }

    await expenseDB.markPaid({
      id: expenseId,
      paidDate: paidDate,
      paymentMethod: mode,
      amount: amount,
      paidBy: paidBy,
      paidByUserType: paidByUserType,
    });

    if (file) {
      const folderName = `expense_${expenseId}`;
      const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
      const fileExtension = file.mimetype.split("/")[1];
      const filename = `expenseBill.${fileExtension}`;

      if (!fs.existsSync(folderPath)) {
        await fsPromises.mkdir(folderPath, { recursive: true });
      }

      const oldPath = `uploads/tmp/${file.filename}`;
      const newPath = `${folderPath}/${filename}`;
      const url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

      await fsPromises.copyFile(oldPath, newPath);

      await expenseDB.updateSupportDoc({
        id: expenseId,
        supportDoc: url,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
      );
    }

    if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
      log.info(
        `[${C}], [${F}], Staff Id ${paidBy}, amount ${amount} Staff Ledger and Balance Recorded`
      );
      const paidByStaff = await staffDB.getById({ id: paidBy });
      let paidToUser = null;
      if (expense.paidToUserType === CONSTANTS.USER_TYPE.STAFF) {
        paidToUser = await staffDB.getById({ id: expense.paidTo });
      } else {
        paidToUser = await vendorDB.getById({ id: expense.paidTo });
      }

      // paidByName = paidByStaff.name;
      // paidToName = paidToUser.name;

      await staffLedgerDB.addExpense({
        staffId: paidBy,
        amount: amount,
        type: CONSTANTS.STAFF_LEDGER_TYPES.EXPENSES,
        mode: mode,
        description: `Expense paid by Staff ${paidByStaff.name} to ${
          paidToUser.name
        } on ${moment().format("YYYY-MM-DD HH:mm:ss")}`,
        expenseId: expenseId,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        dueDate: expense.dueDate
      });

      const staffExistsInBalance = await staffBalanceDB.getByStaffId({
        staffId: paidBy,
      });

      if (!staffExistsInBalance) {
        await staffBalanceDB.add({
          staffId: paidBy,
          totalCollected: 0,
          totalGivenToOwner: 0,
          totalExpense: amount,
        });
      } else {
        await staffBalanceDB.updateTotalExpense({
          staffId: paidBy,
          amount: amount,
        });
      }
    }

    if (expense.paidToUserType === CONSTANTS.USER_TYPE.STAFF && expense.type === 14) {
      // If the expense is a staff salary, log it in ledger
      await staffLedgerDB.addExpense({
        staffId: expense.paidTo,
        amount: amount,
        type: CONSTANTS.STAFF_LEDGER_TYPES.SALARY,
        mode: mode,
        description: `Salary paid by ${paidByName}`,
        expenseId: expenseId,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        dueDate: expense.dueDate
      });
    }

    //log.info(`Expense [${JSON.stringify(expense)}]`);

    await logExpenseActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId!),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.MARKED_EXPENSE,
      paidBy,
      paidByUserType,
      expense?.paidTo,
      expense?.paidToUserType,
      expense?.type,
      amount,
      mode,
      expenseProperty?.propId || null,
    );

    log.info(`[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Payment Mode [${mode}], Paid Date [${paidDate}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Expense Marked Paid Successfully`);

    return res.status(200).json({
      msg: "Expense marked paid successfully",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    if (file) {
      await removeTmpImages();
    }

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.PayMultiple = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "PayMultiple";

  const file = req.file as Express.Multer.File;
  const removeTmpImages = async () => {
    try {
      await fsPromises.unlink(file.path);
    } catch (err) {
      log.info(
        `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
          err
        )}]`
      );
    }
  };

  try {
    let { expenseIds, mode, paidDate, amount, paidBy, paidByUserType } = req.body;

    paidDate = moment(paidDate).format("YYYY-MM-DD HH:mm:ss");
    mode = Number(mode);

    if (expenseIds && typeof expenseIds === 'string') {
      expenseIds = expenseIds.split(',').map(v => v.trim()).filter(Boolean);
    }

    log.info(
      `[${C}], [${F}], Expense Ids [${expenseIds}], Amount [${amount}], Payment Mode [${mode}], Paid Date [${paidDate}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Is File Uploaded ${file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"},`
    );

    const userType = req.userType;
    let paidByName = "";

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById ({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);

        if (file) {
          await fsPromises.unlink(file.path);
        }

        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Only Admin, Warden, Back Office and Finance Admin Allowed`);
        return res.status(400).json({
          msg: "Unauthorised Access",
          isSuccess: false,
        });
      }

      log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin/Warden Requesting`)
    }

    const client = await clientDB.getById({
      id: clientId,
    });

    const expenses = await expenseDB.getByIds({
      ids: expenseIds,
    });

    if (!expenses) {
      log.info(
        `[${C}], [${F}], Expense Ids [${JSON.stringify(expenseIds)}], No Expenses Found`
      );

      return res.status(400).json({
        msg: "No expenes found",
        isSuccess: false,
      });
    }

    if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({
        id: paidBy,
      });
      paidByName = staff.name;
    } else if (paidByUserType === CONSTANTS.USER_TYPE.CLIENT) {
      const client = await clientDB.getById({
        id: paidBy,
      });
      paidByName = client.name;
    } else if (paidByUserType === CONSTANTS.USER_TYPE.VENDOR) {
      const vendor = await vendorDB.getById({
        id: paidBy,
      });
      paidByName = vendor.name;
    }

    if (expenses && expenses.length > 0) {
      for (let expense of expenses) {
        await expenseDB.markPaid({
          id: expense.id,
          paidDate: paidDate,
          paymentMethod: mode,
          amount: expense.amount,
          paidBy: paidBy,
          paidByUserType: paidByUserType,
        });
    
        if (file) {
          const folderName = `expense_${expense.id}`;
          const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
          const fileExtension = file.mimetype.split("/")[1];
          const filename = `expenseBill.${fileExtension}`;
    
          if (!fs.existsSync(folderPath)) {
            await fsPromises.mkdir(folderPath, { recursive: true });
          }
    
          const oldPath = `uploads/tmp/${file.filename}`;
          const newPath = `${folderPath}/${filename}`;
          const url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;
    
          await fsPromises.copyFile(oldPath, newPath);
    
          await expenseDB.updateSupportDoc({
            id: expense.id,
            supportDoc: url,
          });
    
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expense.id}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
          );
        }
  
        if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
          log.info(
            `[${C}], [${F}], Staff Id ${paidBy}, amount ${amount} Staff Ledger and Balance Recorded`
          );
          const paidByStaff = await staffDB.getById({ id: paidBy });
          let paidToUser = null;
          if (expense.paidToUserType === CONSTANTS.USER_TYPE.STAFF) {
            paidToUser = await staffDB.getById({ id: expense.paidTo });
          } else {
            paidToUser = await vendorDB.getById({ id: expense.paidTo });
          }
    
          // paidByName = paidByStaff.name;
          // paidToName = paidToUser.name;
    
          await staffLedgerDB.addExpense({
            staffId: paidBy,
            amount: expense.amount,
            type: CONSTANTS.STAFF_LEDGER_TYPES.EXPENSES,
            mode: mode,
            description: `Expense paid by Staff ${paidByStaff.name} to ${
              paidToUser.name
            } on ${moment().format("YYYY-MM-DD HH:mm:ss")}`,
            expenseId: expense.id,
            paidDate: moment(paidDate).format("YYYY-MM-DD"),
            dueDate: expense.dueDate
          });
    
          const staffExistsInBalance = await staffBalanceDB.getByStaffId({
            staffId: paidBy,
          });
    
          if (!staffExistsInBalance) {
            await staffBalanceDB.add({
              staffId: paidBy,
              totalCollected: 0,
              totalGivenToOwner: 0,
              totalExpense: expense.amount,
            });
          } else {
            await staffBalanceDB.updateTotalExpense({
              staffId: paidBy,
              amount: expense.amount,
            });
          }
        }
  
        if (expense.paidToUserType === CONSTANTS.USER_TYPE.STAFF && expense.type === 14) {
          // If the expense is a staff salary, log it in ledger
          await staffLedgerDB.addExpense({
            staffId: expense.paidTo,
            amount: expense.amount,
            type: CONSTANTS.STAFF_LEDGER_TYPES.SALARY,
            mode: mode,
            description: `Salary paid by ${paidByName}`,
            expenseId: expense.id,
            paidDate: moment(paidDate).format("YYYY-MM-DD"),
            dueDate: expense.dueDate
          });
        }
    
        log.info(`[${C}], [${F}], Client Id [${clientId}], Expense Id [${expense.id}], Payment Mode [${mode}], Paid Date [${paidDate}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Expense Marked Paid Successfully`);
      }
    }

    return res.status(200).json({
      msg: "Expenses marked paid successfully",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    if (file) {
      await removeTmpImages();
    }

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.EditRecurring = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "EditRecurring";

  try {
    let { 
      amount, 
      paidByUserType, 
      paidToUserType, 
      paidBy, 
      paidTo, 
      description, 
      expenseId, 
      expenseCycle, 
      noOfMonths=null, 
      dueDate,
      paymentCycle = null, //Advance or at month end
      expenseNature = null,
      expenseTitle=null,
    } = req.body;
    let origDuedate = dueDate;
    dueDate = moment(dueDate).utc();
    dueDate = dueDate.tz('Asia/Kolkata').format("YYYY-MM-DD");
    log.info(
      `[${C}], [${F}], Recurring Expense Id [${expenseId}], Amount [${amount}], Paid By User Type [${paidByUserType}], Paid To User Type [${paidToUserType}], Paid By [${paidBy}], Paid To [${paidTo}], Description [${description}], Number Of Months [${noOfMonths}] Recieved dueDate [${origDuedate}], Converted Due Date [${dueDate}], Payment Cycle [${paymentCycle}], Expense Nature [${expenseNature}], Expense Title [${expenseTitle}]`
    );

    const userType = req.userType;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById ({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);

        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Only Admin, Warden, Back Office and Finance Admin Allowed`);
        return res.status(400).json({
          msg: "Unauthorised Access",
          isSuccess: false,
        });
      }

      log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin/Warden Requesting`)
    }

    const recurringExpense = await recurringExpenseDB.getById({
      id: expenseId,
      clientId,
    });
    if (!recurringExpense) {
      log.info(`[${C}], [${F}], Recurring Expense Id [${expenseId}], No Recurring Expense Found`);

      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    if(paymentCycle) {
      await recurringExpenseDB.updateNew({
        amount: amount,
        paidByUserType: paidByUserType,
        paidToUserType: paidToUserType,
        paidBy: paidBy,
        paidTo: paidTo,
        description: description || null,
        id: expenseId,
        noOfMonths: noOfMonths || null,
        dueDate: dueDate,
        expenseCycle: moment(dueDate).date(),
        expenceCycleType: Number(paymentCycle),
        recurrenceDate: moment(dueDate).add(noOfMonths, 'months').format("YYYY-MM-DD"),
        expenseTitle: expenseTitle && String(expenseTitle).trim() !== "" ? expenseTitle : recurringExpense?.expenseTitle || null,
      });
    } else {
      await recurringExpenseDB.update({
        amount: amount,
        paidByUserType: paidByUserType,
        paidToUserType: paidToUserType,
        paidBy: paidBy,
        paidTo: paidTo,
        description: description || null,
        id: expenseId,
        noOfMonths: noOfMonths || null,
        dueDate: dueDate,
        expenseCycle: moment(dueDate).date(),
        expenseTitle: expenseTitle && String(expenseTitle).trim() !== "" ? expenseTitle : recurringExpense?.expenseTitle || null,
      });
    }

    if (expenseNature) {
      await recurringExpenseDB.updateExpenseNature ({
        expenseNature,
        id: expenseId
      });
    }

    if (Number(recurringExpense.noOfMonths) !== Number(noOfMonths)) {
      const dueDate = moment(recurringExpense.dueDate).date();
      const currDate = moment().date();
      if (Number(recurringExpense.noOfMonths) > Number(noOfMonths)) {
        if (dueDate >= currDate) {
          let newDueDate = moment(recurringExpense.paidDate).add(noOfMonths + 1, 'months').format("YYYY-MM-DD");

          await recurringExpenseDB.updateDueDateById({
            id: expenseId,
            dueDate: newDueDate,
            expenseCycle: moment(newDueDate).date(),
          });
        } else {
          let newDueDate = moment(recurringExpense.paidDate).add(noOfMonths, 'months').format("YYYY-MM-DD");

          await recurringExpenseDB.updateDueDateById({
            id: expenseId,
            dueDate: newDueDate,
            expenseCycle: moment(newDueDate).date(),
          });
        }
      } else {
        let newDueDate = moment(recurringExpense.paidDate).add(noOfMonths).format("YYYY-MM-DD");
        await recurringExpenseDB.updateDueDateById({
          id: expenseId,
          dueDate: newDueDate,
          expenseCycle: moment(newDueDate).date(),
        });
      }
    }

    await logExpenseActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId!),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.EDIT_RECURRING_EXPENSE,
      paidBy,
      paidByUserType,
      paidTo,
      paidToUserType,
      recurringExpense.type, //type
      amount,
      recurringExpense.paymentMethod,
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Recurring Expense Id [${expenseId }], Amount [${amount}], Paid By User Type [${paidByUserType}], Paid To User Type [${paidToUserType}], Paid By [${paidBy}], Paid To [${paidTo}], Description [${description}], Number Of Months [${noOfMonths}], Due Date [${dueDate}], Recurring Expense Updated Successfully`
    );

    return res.status(200).json({
      msg: "Recurring expense updated successfully.",
      isSuccess: true,
    });

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.DeleteRecurring = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "DeleteRecurring";

  try {
    const { id } = req.body;

    log.info(`[${C}], [${F}], Recurring Expense Id [${id}]`);

    const userType = req.userType;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById ({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);

        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Only Admin, Warden, Back Office and Finance Admin Allowed`);
        return res.status(400).json({
          msg: "Unauthorised Access",
          isSuccess: false,
        });
      }

      log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin/Warden Requesting`)
    }

    const recurringExpense = await recurringExpenseDB.getById({
      id: id,
      clientId,
    });

    if (!recurringExpense) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Recurring Expense Id [${id}], No Recurring Expense Found With This Id`);

      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }
    
    await recurringExpenseDB.delete({
      id,
      clientId,
    });

    await logExpenseActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId!),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.DELETE_RECURRING_EXPENSE,
      recurringExpense.paidBy,
      recurringExpense.paidByUserType,
      recurringExpense.paidTo,
      recurringExpense.paidToUserType,
      recurringExpense.type, //type
      recurringExpense.amount,
      recurringExpense.paymentMethod,
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Recurring Expense Id [${id}], Recurring Expense Deleted Successfully`
    );

    return res.status(200).json({
      msg: "Recurring expense deleted successfully.",
      isSuccess: true,
    });

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

expense.MarkSecurityAsRefunded = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "MarkSecurityAsRefunded";

  try {
    const { refundedAmount, mode, expenseId } = req.body;

    log.info(
      `[${C}], [${F}], Refunded Amount [${refundedAmount}], Mode [${mode}], Expense [${expenseId}]`
    );

    const userType = req.userType;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById ({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);

        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Only Admin, Warden, Back Office and Finance Admin Allowed`);
        return res.status(400).json({
          msg: "Unauthorised Access",
          isSuccess: false,
        });
      }

      log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin/Warden Requesting`)
    }

    const expense = await expenseDB.getById({
      id: expenseId,
    });

    if (expense.paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
      await staffLedgerDB.updateExpenseAmount({
        staffId: expense.paidBy,
        expenseId: expenseId,
        amount: expense.balance - refundedAmount,
      });
      const amountDiff = expense.balance - refundedAmount;
      await staffBalanceDB.updateTotalExpense({
        staffId: expense.paidBy,
        amount: amountDiff,
      });
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${expense.paidBy}], New Amount ${expense.balance - refundedAmount}, Old Amount [${expense.balance}], Updated the Amount in Staff Ledger`
      );
    }

    await expenseDB.markSecuityAsRefunded({
      id: expenseId,
      balance: refundedAmount,
      description: `, ₹${refundedAmount} Security has been marked as refunded via ${mode} on ${moment().format("YYYY-MM-DD")}`,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Refunded Amount [${refundedAmount}], Mode [${mode}], Expense [${expenseId}], Security Is Marked Refunded Successfully`
    );

    return res.status(200).json({
      msg: "Security is marked refunded successfully.",
      isSuccess: true,
    });

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
}

expense.PayX = async (req: CustomRequest, res: Response) => {
  const C = "Expense Controller";
  const F = "PayX";

  const file = req.file as Express.Multer.File;
  const removeTmpImages = async () => {
    try {
      await fsPromises.unlink(file.path);
    } catch (err) {
      log.info(
        `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
          err
        )}]`
      );
    }
  };

  try {
    let { expenseId, mode, paidDate, amount, paidBy, paidByUserType, isPartialPayment=0, paymentAccountNo = null, paymentAccountName = null } = req.body;

    //paidDate = moment(paidDate).format("YYYY-MM-DD HH:mm:ss");
    //paidDate = moment(paidDate).format("YYYY-MM-DD HH:mm:ss");
    paidDate = `${moment(paidDate).format("YYYY-MM-DD")} ${moment().format("HH:mm:ss")}`;
    mode = Number(mode);
    expenseId = Number(expenseId);

    log.info(
      `[${C}], [${F}], Expense Id [${expenseId}], Amount [${amount}], Payment Mode [${mode}], Paid Date [${paidDate}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Is Partial Payment [${isPartialPayment}], Payment Account No [${paymentAccountNo}], Payment Account Name [${paymentAccountName}], Is File Uploaded ${file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"},`
    );

    const userType = req.userType;
    let paidByName = "";

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById ({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);

        if (file) {
          await fsPromises.unlink(file.path);
        }

        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Only Admin and Warden Allowed`);
        return res.status(400).json({
          msg: "Unauthorised Access",
          isSuccess: false,
        });
      }

      log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin/Warden Requesting`)
    }

    const client = await clientDB.getById({
      id: clientId,
    });

    const expense = await expenseDB.getById({
      id: expenseId,
    });

    if(Number(expense.amount) < Number(amount)) {
      log.info(`[${C}], [${F}], Expense Id [${expenseId}], Expense Amount [${expense.amount}], Paid Amount [${amount}], Paid Amount cannot be greater than expense amount`)
      return res.status(400).json({
          msg: "Paid amount cannot be greater than expense amount.",
          isSuccess: false,
        });
    }
    const expenseProperty = await expenseDB.getExpensePropertyById({id: expenseId});

    if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({
        id: paidBy,
      });
      paidByName = staff.name;
    } else if (paidByUserType === CONSTANTS.USER_TYPE.CLIENT) {
      const client = await clientDB.getById({
        id: paidBy,
      });
      paidByName = client.name;
    } else if (paidByUserType === CONSTANTS.USER_TYPE.VENDOR) {
      const vendor = await vendorDB.getById({
        id: paidBy,
      });
      paidByName = vendor.name;
    }
    //let expenseDetail = expense;
    if(isPartialPayment === 0 && Number(amount) === Number(expense.amount)) {
      log.info(`[${C}], [${F}], Expense Id [${expenseId}], Expense Amount [${expense.amount}], Paid Amount [${amount}], Paying expense fully`)
      await expenseDB.markPaid({
        id: expenseId,
        paidDate: paidDate,
        paymentMethod: mode,
        amount: amount,
        paidBy: paidBy,
        paidByUserType: paidByUserType,
      });
    } else if (Number(amount) === Number(expense.amount)) {
      log.info(`[${C}], [${F}], Expense Id [${expenseId}], Expense Amount [${expense.amount}], Paid Amount [${amount}], flag set partially but paying expense fully`);
      await expenseDB.markPaid({
        id: expenseId,
        paidDate: paidDate,
        paymentMethod: mode,
        amount: amount,
        paidBy: paidBy,
        paidByUserType: paidByUserType,
      });
    } else if(Number(amount)<Number(expense.amount)) {
      log.info(`[${C}], [${F}], Expense Id [${expenseId}], Expense Amount [${expense.amount}], Paid Amount [${amount}], Paying expense partially`);
      let oldExpenseId = expenseId;
      expenseId = await expenseDB.create({
        type: expense?.type,
        amount,
        clientId,
        paidDate: paidDate,
        paidByUserType : paidByUserType,
        paidBy: paidBy,
        paidTo: expense?.paidTo,
        paidToUserType: expense?.paidToUserType,
        description: expense?.description,
        paymentMethod: mode,
        repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
        noOfMonths: 0,
        dueDate: expense?.dueDate,
        isPaid: 1,
        bankRefNum: null,
        expenseTitle: expense?.expenseTitle,
      });
      if(expenseProperty) {
        await expenseDB.addProperty({
          expenseId,
          propId: expenseProperty?.propId,
          flatId: expenseProperty?.flatId,
          clientId,
        });
      }
      //Update balance amount and of the Old
      await expenseDB.updateAmount({
        id: oldExpenseId, 
        amount: Number(expense.amount) - Number(amount)
      });

      //Staff Ledger handling
      await staffLedgerDB.updateExpenseAmount({
        staffId: expense.paidTo,
        expenseId: oldExpenseId,
        amount: Number(expense.amount) - Number(amount),
      });
    }
    
    await setExpensePaymentTallyStatus(
      Number(clientId),
      Number(expenseId),
      CONSTANTS.TALLY_STATUS.PENDING,
    );

    await expenseDB.updateBankDetails({
      id: expenseId,
      paymentAccountNo: paymentAccountNo || null,
      paymentAccountName: paymentAccountName || null,
    });

    // if (expense?.paidToUserType === CONSTANTS.USER_TYPE.STAFF) {
    //   const staffLedgerEntry = await staffLedgerDB.getUnPaidByExpenseId({
    //     expenseId: expenseId,
    //     staffId: expense.paidTo,
    //   });

    //   if (staffLedgerEntry) {
    //     await staffLedgerDB.addExpense({
    //       staffId: staffLedgerEntry.staffId,
    //       amount: amount,
    //       type: staffLedgerEntry.type,
    //       mode: mode,
    //       description: staffLedgerEntry.description,
    //       expenseId: expenseId || null,
    //       paidDate: moment(paidDate).format("YYYY-MM-DD"),
    //       dueDate: expense.dueDate 
    //     });
    //   }
    // }

    if (file) {
      const folderName = `expense_${expenseId}`;
      const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
      const fileExtension = file.mimetype.split("/")[1];
      const filename = `expenseBill.${fileExtension}`;

      if (!fs.existsSync(folderPath)) {
        await fsPromises.mkdir(folderPath, { recursive: true });
      }

      const oldPath = `uploads/tmp/${file.filename}`;
      const newPath = `${folderPath}/${filename}`;
      const url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

      await fsPromises.copyFile(oldPath, newPath);

      await expenseDB.updateSupportDoc({
        id: expenseId,
        supportDoc: url,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
      );
    }

    if (paidByUserType === CONSTANTS.USER_TYPE.STAFF) {
      log.info(
        `[${C}], [${F}], Staff Id ${paidBy}, amount ${amount} Staff Ledger and Balance Recorded`
      );
      const paidByStaff = await staffDB.getById({ id: paidBy });
      let paidToUser = null;
      if (expense.paidToUserType === CONSTANTS.USER_TYPE.STAFF) {
        paidToUser = await staffDB.getById({ id: expense.paidTo });
      } else {
        paidToUser = await vendorDB.getById({ id: expense.paidTo });
      }

      // paidByName = paidByStaff.name;
      // paidToName = paidToUser.name;

      await staffLedgerDB.addExpense({
        staffId: paidBy,
        amount: -amount,
        type: CONSTANTS.STAFF_LEDGER_TYPES.EXPENSES,
        mode: mode,
        description: `Expense paid by Staff ${paidByStaff.name} to ${
          paidToUser.name
        } on ${moment().format("YYYY-MM-DD HH:mm:ss")}`,
        expenseId: expenseId,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        dueDate: expense.dueDate
      });

      const staffExistsInBalance = await staffBalanceDB.getByStaffId({
        staffId: paidBy,
      });

      if (!staffExistsInBalance) {
        await staffBalanceDB.add({
          staffId: paidBy,
          totalCollected: 0,
          totalGivenToOwner: 0,
          totalExpense: amount,
        });
      } else {
        await staffBalanceDB.updateTotalExpense({
          staffId: paidBy,
          amount: amount,
        });
      }
    }

    if (
      expense.paidToUserType === CONSTANTS.USER_TYPE.STAFF 
      && (expense.type === 14 || expense.type === 23 || expense.type === 52 || expense.type === 53)
    ) {
      // If the expense is a staff salary, bonus, travel allowance, hra then log it in ledger

      let staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.SALARY;
      let staffLedgerDescription = `Salary`;

      if (expense.type === 23) {
        staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.TRAVEL_ALLOWANCE;
        staffLedgerDescription = `Travel Allowance`;
      } 
      else if (expense.type === 52) {
        staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.BONUS;
        staffLedgerDescription = `Bonus`;
      } 
      else if (expense.type === 53) {
        staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.HRA;
        staffLedgerDescription = `HRA`;
      }

      if (Number(isPartialPayment) === 1) {
        //due entry
        await staffLedgerDB.addExpense({
          staffId: expense.paidTo,
          amount: -amount,
          type: staffLedgerType,
          mode: mode,
          description: `${staffLedgerDescription}`,
          expenseId: expenseId,
          paidDate: moment(paidDate).format("YYYY-MM-DD"),
          dueDate: expense.dueDate
        });
        //due paid entry
        await staffLedgerDB.addExpense({
          staffId: expense.paidTo,
          amount: amount,
          type: staffLedgerType,
          mode: mode,
          description: `${staffLedgerDescription}`,
          expenseId: expenseId,
          paidDate: moment(paidDate).format("YYYY-MM-DD"),
          dueDate: expense.dueDate
        });
      } else {
        await staffLedgerDB.addExpense({
          staffId: expense.paidTo,
          amount: amount,
          type: staffLedgerType,
          mode: mode,
          description: `${staffLedgerDescription}`,
          expenseId: expenseId,
          paidDate: moment(paidDate).format("YYYY-MM-DD"),
          dueDate: expense.dueDate
        });
      }
    }

    //log.info(`Expense [${JSON.stringify(expense)}]`);

    await logExpenseActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId!),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.MARKED_EXPENSE,
      paidBy,
      paidByUserType,
      expense?.paidTo,
      expense?.paidToUserType,
      expense?.type,
      amount,
      mode,
      expenseProperty?.propId || null,
    );

    log.info(`[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], Payment Mode [${mode}], Paid Date [${paidDate}], Paid By [${paidBy}], Paid By UserType [${paidByUserType}], Expense Marked Paid ${Number(isPartialPayment) === 1  ? `Partially` : `Fully`}`);

    return res.status(200).json({
      msg: "Expense marked paid successfully",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    if (file) {
      await removeTmpImages();
    }

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

export default expense;
