import { Response } from "express";
import log from "../config/log";
import CustomRequest from "../types/requestType";
import { isUserPartner } from "../utils/isUserPartner";
import CONSTANTS from "../config/constants";
import staffDB from "../models/staff.model";
import generatePayOutTransId from "../utils/generatePayoutTransId";
import cashfreePayout from "../utils/cashfreePayout";
import payoutTransactionDB from "../models/payoutTransaction.model";
import payoutBeneficiaryDB from "../models/payoutBeneficiary.model";
import requestsTypes from "../schemas/request.schema";
import vendorDB from "../models/vendor.model";
import { generateBeneficiaryId } from "../utils/helpers";
import expenseDB from "../models/expense.model";
import moment from "moment";
import landlordDB from "../models/landlord.model";
import clientDB from "../models/client.model";
import landlordTransactionDB from "../models/landlordTransaction.model";
import clientLandlordDB from "../models/clientLandlord.model";
import staffLedgerDB from "../models/staffLedger.model";
import { sendWhatsappPayoutDebit, sendWhatsappPayoutReceived } from "../utils/sendWhatsappWithConfig";
import { payoutReciept } from "../utils/PayoutReciept";
import tenantDB from "../models/tenant.model";
import refundExcessPayment from "../utils/refundExcessPayment";
import moveOutDB from "../models/moveOut.model";
import { getWalletBalanceAndLimits, handleRecharge, handleTransferFail, recordTransferInitiate } from "../utils/walletHelper";
import autopayDB from "../models/autopay.model";

const payout: any = {};

const CHARGE_IN_PAISE = 390; // Rs 3.90

payout.AddBeneficiary = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "AddBeneficiary";
  try {
    const userType = req.userType;
    let { name, mobile, accountName, accountNumber, ifsc, upiId = null, type = 1, beneType = CONSTANTS.USER_TYPE.VENDOR } = req.body;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], name [${name}], Mobile [${mobile}], Account Name [${accountName}], Account Number [${accountNumber}], IFSC [${ifsc}], UPI ID [${upiId}], Type [${type}], Beneficiary Type [${beneType}], Request`
    );

    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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );
    }
    let isAccountAlreadyExist = await payoutBeneficiaryDB.getByAccountNumberIfsc({
      clientId,
      accountNum: accountNumber,
      ifsc
    });
    if (isAccountAlreadyExist) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Account Number [${accountNumber}], IFSC [${ifsc}] Already Exist`
      );
      return res.status(400).json({
        msg: `Account Number ${accountNumber} with IFSC ${ifsc} already exist`,
        isSuccess: false,
      });
    }
    const firstName = name.trim().split(" ")[0];
    let beneficiaryId: string;
    let response = await cashfreePayout.fetchBeneficiary(C, F, clientId, accountNumber, ifsc);

    if (response?.isSuccess === false) {
      beneficiaryId = await generateBeneficiaryId({ userName: firstName });
      log.info(`[${C}], [${F}], Client Id [${clientId}], Beneficiary Id [${beneficiaryId}]`);

      response = await cashfreePayout.addBeneficiary(
        C,
        F,
        clientId,
        accountName,
        beneficiaryId,
        accountNumber,
        ifsc,
        upiId,
        type,
        mobile
      );

      if (response?.isSuccess === false) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Resp [${JSON.stringify(response.error)}], Failed to add beneficiary`
        );
        return res.status(400).json({
          msg: response?.error?.message || "Fail to add beneficiary",
          isSuccess: false,
        });
      }
    } else {
      beneficiaryId = response?.data?.beneficiary_id;
      accountName = response?.data?.beneficiary_name;
      log.info(`[${C}], [${F}], Client Id [${clientId}], Fetched Beneficiary Id [${beneficiaryId}], Fetched Beneficiary Name [${accountName}]`);
    }
    let userData: any;
    if (beneType === CONSTANTS.USER_TYPE.LANDLORD) {
      userData = await landlordDB.getByClientIdXAndMobile({ clientId, mobile });
      if (!userData) {
        let landlordData = await landlordDB.getByMobile({ mobile });
        let landlordId: number;
        if (!landlordData) {
          landlordId = await landlordDB.create({ name, mobile, email: null });
        } else {
          landlordId = landlordData?.id;
        }
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], LandlordId [${landlordId}]`
        );
        await clientLandlordDB.create({ clientId, landlordId: landlordId });
        userData = await landlordDB.getByClientIdXAndMobile({ clientId, mobile });
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], LandlordId [${landlordId}], UserData Id [${JSON.stringify(userData)}]`
        );
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Landlord created`
        );
      }
    } else if (beneType === CONSTANTS.USER_TYPE.STAFF) {
      //Yet to be done
      userData = await staffDB.getByMobileAndClientId({ mobile, clientId });
      if (!userData) {
        await staffDB.create({
          clientId,
          role: CONSTANTS.STAFF_ROLES.BACK_OFFICE,
          name,
          mobile,
          gender: 1,
          salary: 0,
          commission: null,
          commissionType: null,
          isOnPayroll: CONSTANTS.STAFF_EMPLOYMENT_TYPE.CONTRACT
        });
        userData = await staffDB.getByMobileAndClientId({ mobile, clientId });
      }
    } else if (beneType === CONSTANTS.USER_TYPE.TENANT) {
      //Yet to be done
    } else {
      userData = await vendorDB.getByMobileAndClientId({ mobile, clientId });

      if (!userData) {
        await vendorDB.create({ mobile, name, clientId, type: 1 });
        userData = await vendorDB.getByMobileAndClientId({ mobile, clientId });
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Vendor created`
        );
      }
    }

    let beneficiaryStatus = response?.data?.beneficiary_status;
    let status = CONSTANTS.BENEFICIARY_STATUS.INITIATED;
    if (beneficiaryStatus === "VERIFIED") {
      status = CONSTANTS.BENEFICIARY_STATUS.VERIFIED;
    } else if (beneficiaryStatus === "INVALID") {
      status = CONSTANTS.BENEFICIARY_STATUS.INVALID;
    } else if (beneficiaryStatus === "CANCELLED") {
      status = CONSTANTS.BENEFICIARY_STATUS.CANCELLED;
    } else if (beneficiaryStatus === "DELETED") {
      status = CONSTANTS.BENEFICIARY_STATUS.DELETED;
    } else if (beneficiaryStatus === "FAILED") {
      status = CONSTANTS.BENEFICIARY_STATUS.FAILED;
    }


    log.info(
      `[${C}], [${F}], Client Id [${clientId}], , UserData Id [${JSON.stringify(userData)}, User Id [${userData?.id}]`
    );
    const beneId = await payoutBeneficiaryDB.add({
      clientId,
      userId: userData?.id,
      userType: beneType,
      bankName: null,
      name,
      holderName: accountName,
      type: type,
      accountNum: type === CONSTANTS.BANK_TYPES.BANK ? accountNumber : null,
      ifsc: type === CONSTANTS.BANK_TYPES.BANK ? ifsc : null,
      upiId: type === CONSTANTS.BANK_TYPES.UPI ? upiId : null,
      cfBeneficiaryId: beneficiaryId,
      status,
      mobile,
    });

    await payoutBeneficiaryDB.updateAddedByById({
      id: beneId,
      addedBy: req.id,
      addedByType: userType,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Beneficiary Status [${beneficiaryStatus}], Beneficiary Added successfully`
    );

    return res.status(200).json({
      msg: "Beneficiary added",
      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,
    });
  }
};

payout.DeleteBeneficiary = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "DeleteBeneficiary";
  try {
    const userType = req.userType;
    let { id } = req.body;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Id [${id}], Request`
    );

    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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );
    }
    let isExist = await payoutBeneficiaryDB.getById({ id });
    if (!isExist) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Id [${id}], Beneficiary Not Found`
      );
      return res.status(400).json({
        msg: `Not a valid request`,
        isSuccess: false,
      });
    }
    let cfBeneficiaryId = isExist?.cfBeneficiaryId;
    //Check If same beneficiary exist with another client
    // let isExistOtherClient = await payoutBeneficiaryDB.getByCfBeneIdExceptClient({ cfBeneficiaryId, clientId });
    // if (isExistOtherClient === false && clientId != 519) {
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Id [${id}], CF BeneficiaryId [${cfBeneficiaryId}], Removing from cashfree`
    );
    const response = await cashfreePayout.removeBeneficiary(
      C,
      F,
      clientId,
      cfBeneficiaryId
    );

    if (response?.isSuccess === false) {
      if (response?.error?.code === 'beneficiary_not_found') {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Resp [${JSON.stringify(response.error)}], Beneficiary Not exist with Cashfree`
        );
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Resp [${JSON.stringify(response.error)}], Failed to remove beneficiary`
        );

        return res.status(400).json({
          msg: response?.error?.message || "Fail to add beneficiary",
          isSuccess: false,
        });
      }
    }
    // } else {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Id [${id}], CF BeneficiaryId [${cfBeneficiaryId}], Same Beneficiary id exist with other client`
    //   );
    // }

    await payoutBeneficiaryDB.updateStatus({
      id,
      status: CONSTANTS.BENEFICIARY_STATUS.DELETED
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Id [${id}], CF BeneficiaryId [${cfBeneficiaryId}], Beneficiary deleted successfully`
    );

    return res.status(200).json({
      msg: "Beneficiary 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,
    });
  }
};
payout.ListBeneficiaries = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "ListBeneficiaries";
  try {
      const platform = req.platform;

      log.info(`[${C}], [${F}], Platform [${platform}]`);
      if(platform === CONSTANTS.TENANT_DEVICE_TYPE.ANDROID || platform === CONSTANTS.TENANT_DEVICE_TYPE.IOS) {
        log.info(`[${C}], [${F}], Platform [${platform}], Sending list for App`);
        return payout.ListBeneficiariesForApp(req, res);
      } else {
        log.info(`[${C}], [${F}], Platform [${platform}], Sending list for web`);
        return payout.ListBeneficiariesForWeb(req, res);
      }
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

payout.ListBeneficiariesForWeb = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "ListBeneficiariesForWeb";
  try {
    const { startDate, endDate, s, t, pageNum = null } = req.query;
    const userType = req.userType;

    let limit = 10;

    log.info(`[${C}], [${F}], StartDate [${startDate}], End Date [${endDate}], Search Value [${s}], Search Type [${t}], Page Num [${pageNum}]`);

    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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );
    }

    let summary: any = {};
    let list: any = [];
    if (s && s !== undefined && s !== "") {
      list = await payoutBeneficiaryDB.getByClientIdWithSearch({
        clientId,
        searchVal: s,
        searchType: t,
        pageNum,
        limit,
      });
    } else {
      list = await payoutBeneficiaryDB.getByClientIdWithDate({
        clientId,
        startDate,
        endDate,
        pageNum,
        limit
      });
    }

    summary = await payoutBeneficiaryDB.getSummaryByClientId({
      clientId
    });


    // let eachBeneficiaryPayment = await payoutTransactionDB.getByAllForEachBeneficiary({ clientId });
    // list = await payoutBeneficiaryDB.getByClientId({
    //   clientId,
    // });

    // const paymentMap = new Map(
    //   eachBeneficiaryPayment.map((item: any) => [
    //     item.payoutBeneficiaryId,
    //     Number(item.totalAmount),
    //   ])
    // );

    // list = list.map((item: any) => ({
    //   ...item,
    //   amount: paymentMap.get(item.id) || 0,
    // }));

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], list sent successfully`
    );

    return res.status(200).json({
      msg: "List sent successfully",
      data: list || [],
      summary: summary,
      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,
    });
  }
};

payout.ListBeneficiariesForApp = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "ListBeneficiariesForApp";
  try {
    const { startDate, endDate, s, pageNum = null } = req.query;
    const userType = req.userType;

    let limit = 10;

    log.info(`[${C}], [${F}], StartDate [${startDate}], End Date [${endDate}], Search Value [${s}], Page Num [${pageNum}]`);

    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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );
    }

    let summary: any = {};
    let list: any = [];
    if (s && s !== undefined && s !== "") {
      list = await payoutBeneficiaryDB.getByClientIdAppSearch ({
        clientId,
        searchVal: s,
        pageNum,
        limit,
      });
    } else {
      list = await payoutBeneficiaryDB.getByClientIdWithDate({
        clientId,
        startDate,
        endDate,
        pageNum,
        limit
      });
    }

    summary = await payoutBeneficiaryDB.getSummaryByClientId({
      clientId
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], list sent successfully`
    );

    return res.status(200).json({
      msg: "List sent successfully",
      data: list || [],
      summary: summary,
      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,
    });
  }
};

payout.Callback = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "Callback";

  try {
    const { data, type } = req.body;
    let requestData = JSON.stringify(req.body);
    // {"data":{"transfer_id":"26052117108354","cf_transfer_id":"2318221156","status":"SUCCESS","status_code":"COMPLETED","status_description":"The transfer has been initiated via the partner bank successfully, hence your account is debited and the request is successfuly processed by the beneficiary bank and has been credited to the end beneficiary.","beneficiary_details":{"beneficiary_id":"GAURAV_31765509","beneficiary_instrument_details":{"bank_account_number":"35506873176","bank_ifsc":"SBIN0005973"}},"transfer_amount":10,"transfer_service_charge":6,"transfer_service_tax":1.08,"transfer_mode":"imps","transfer_utr":"614117630041","fundsource_id":"OXOTEL SPACE HOTEL AND TOURISM PRIVATE LIMITE","added_on":"2026-05-21T17:54:27","updated_on":"2026-05-21T17:54:29"},"event_time":"2026-05-21T17:54:29","type":"TRANSFER_SUCCESS"}
    log.info(`[${C}], [${F}], Request body [${requestData}]`);
    if (type === "TRANSFER_SUCCESS" || type === "TRANSFER_ACKNOWLEDGED" || type === "TRANSFER_SUCCESS" || type === "TRANSFER_FAILED" || type === "TRANSFER_REVERSED" || type === "TRANSFER_REJECTED") {
      log.info(`[${C}], [${F}], Transfer Id [${data.transfer_id}], Status [${data.status}], Status Code [${data?.status_code}]`);

      let transData = await payoutTransactionDB.getByTransId({ transId: data.transfer_id });
      if (!transData) {
        log.info(
          `[${C}], [${F}], Transaction not found for transfer id [${data.transfer_id}]`
        );
        return res.status(400).json({
          msg: "Invalid Request",
          isSuccess: false,
        });
      }

      log.info(`[${C}], [${F}], Trans Data [${JSON.stringify(transData)}]`);

      let tClientId = transData?.clientId;
      let tAmount = transData?.amount;
      let status = CONSTANTS.TRANSFER_STATUS.INITIATED;
      let expenseId = null;
      if (transData?.expenseId) {
        expenseId = Number(transData?.expenseId);
      }
      let paymentDate = moment().format("YYYY-MM-DD HH:mm:ss");
      if ("SUCCESS" === data.status && "COMPLETED" === data?.status_code) {
        //Transfer completed
        status = CONSTANTS.TRANSFER_STATUS.SUCCESS;
        let beneficiary: any;
        let tBeneId = transData?.payoutBeneficiaryId;
        beneficiary = await payoutBeneficiaryDB.getById({ id: tBeneId });
        if (beneficiary?.userType === CONSTANTS.USER_TYPE.TENANT) {
          let tenantId = beneficiary?.userId;
          log.info(`[${C}], [${F}], Client Id [${transData?.clientId}], Tenant Id [${tenantId}], Tenant transfer callback success`);
          let reqData = {
            userType: CONSTANTS.USER_TYPE.CLIENT,
            id: transData?.clientId,
            parentClientId: transData?.clientId,
          }
          if (0 != transData?.doneById && transData?.clientId != transData?.doneById) {
            reqData = {
              userType: CONSTANTS.USER_TYPE.STAFF,
              id: transData?.doneById,
              parentClientId: transData?.clientId,
            }
          }
          let refund = await refundExcessPayment(
            tenantId, 
            tAmount, 
            CONSTANTS.TRANSACTION_MODES.NET_BANKING, 
            moment().format("YYYY-MM-DD hh:mm:ss"), 
            'Kipinn App', 
            transData?.clientId, 
            reqData, 
            data?.transfer_utr || null, 
            beneficiary?.holderName || null, 
            beneficiary?.accountNum || null,
            data.transfer_id,
            CONSTANTS.PAYMENT_GATEWAY.CASHFREE,
          );
          if (false === refund) {
            log.info(`[${C}], [${F}], Client Id [${transData?.clientId}], Tenant Id [${tenantId}], There was issue with record updation`);
          } else {
            log.info(`[${C}], [${F}], Client Id [${transData?.clientId}], Tenant Id [${tenantId}], Refund recorded successfully`);
          }
        } else {
          log.info(`[${C}], [${F}], Client Id [${transData?.clientId}], Expense Id [${transData?.expenseId || 'No Expense Exist'}]`);
          //Implementaion for Recurring Expense
          if (transData?.expenseId) {
            expenseId = Number(transData?.expenseId);
            //beneficiary = await payoutBeneficiaryDB.getById({ id: transData?.payoutBeneficiaryId });
            let expenseDetail = await expenseDB.getById({ id: expenseId });
            log.info(`[${C}], [${F}], Expense Exist [${expenseId}], Amount [${tAmount}], Expense Amount [${expenseDetail.amount}]`);
            if (Number(tAmount) === Number(expenseDetail.amount)) {
              //Update to markpaid, update
              log.info(`[${C}], [${F}], ClientId [${tClientId}], Amount [${tAmount}], Fully Paid Expense`);
              await expenseDB.updateExpenseOnPaid({ id: expenseId, paidDate: paymentDate, paymentMethod: CONSTANTS.TRANSACTION_MODES.NET_BANKING });
              await expenseDB.updateExpenseBankRefNum ({id: expenseId, bankRefNum: data?.transfer_utr ? data?.transfer_utr : null});

            } else if (Number(tAmount) < Number(expenseDetail.amount)) {

              let oldExpenseId = expenseId;
              log.info(`[${C}], [${F}], Client Id [${tClientId}], Amount [${tAmount}], Expense Id [${expenseId}], Partial Paid Expense`);

              expenseId = await expenseDB.create({
                type: expenseDetail?.type,
                amount: transData?.amount,
                clientId: transData?.clientId,
                paidDate: moment().format("YYYY-MM-DD hh:mm:ss"),
                paidByUserType: transData?.doneByType,
                paidBy: transData?.doneById,
                paidTo: expenseDetail?.paidTo,
                paidToUserType: expenseDetail?.paidToUserType,
                description: transData?.remark || expenseDetail?.description,
                paymentMethod: CONSTANTS.TRANSACTION_MODES.NET_BANKING,
                repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
                noOfMonths: 0,
                dueDate: expenseDetail?.dueDate,
                isPaid: 1,
                bankRefNum: data?.transfer_utr ? data?.transfer_utr : null,
              });
              log.info(`[${C}], [${F}], Client Id [${tClientId}], New Expense Id [${expenseId}], Partial Paid Expense`);
              let propId = null;
              let propExpense = await expenseDB.getExpensePropertyById({ id: oldExpenseId });
              if (propExpense) {
                log.info(`[${C}], [${F}], ClientId [${tClientId}], Adding partial expense for property`);
                await expenseDB.addProperty({
                  expenseId,
                  propId: propExpense?.propId,
                  flatId: propExpense?.flatId || null,
                  clientId: transData?.clientId,
                });
                propId = propExpense?.propId;
              }
              log.info(`[${C}], [${F}], Client Id [${tClientId}], Prop Id [${propId}], Total Amount [${expenseDetail.amount}], Partial Amount [${tAmount}], Partial expense update`);
              //Update balance amount and of the Old
              expenseDB.updateAmount({
                id: oldExpenseId,
                amount: Number(expenseDetail.amount) - Number(tAmount)
              });
              //Activity Logs to be added
              if (beneficiary?.userType === CONSTANTS.USER_TYPE.LANDLORD) {
                log.info(`[${C}], [${F}], Client Id [${tClientId}], Adding Transaction for Landlord`);
                let desc = ""
                let expenseType: number = 1;
                if (Number(expenseDetail?.type) === 1) {
                  desc = "Building rent paid to landlord";
                  expenseType = 1;
                } else if (Number(expenseDetail?.type) === 2 || Number(expenseDetail?.type) === 43) {
                  desc = "Building security paid to landlord";
                  expenseType = 43;
                }
                await landlordTransactionDB.add({
                  clientId: tClientId,
                  gId: transData?.transId,
                  landlordId: beneficiary?.userId,
                  propId,
                  amount: tAmount,
                  expenseId,
                  gateway: CONSTANTS.PAYMENT_GATEWAY.CASHFREE,
                  mode: CONSTANTS.TRANSACTION_MODES.NET_BANKING,
                  paymentDate: moment().format("YYYY-MM-DD hh:mm:ss"),
                  description: `${desc} for ${moment(expenseDetail?.dueDate).format("MMM")} Month`,
                  status: 1,
                  type: expenseType,
                  remarks: transData?.remark,
                  bankRefNum: data?.transfer_utr ? data?.transfer_utr : null,
                });
              }
            }
            else {
              log.info(`[${C}], [${F}], Expense Exist [${transData?.expenseId}], Excess Payment`);
            }
          } else if (transData?.expenseType && Number(transData?.expenseType) > 0) {
            //Implementaion for Non recurring expense
            //let tBeneId = transData?.payoutBeneficiaryId;
            let tExpenseType = transData?.expenseType;
            let beneUserId = 0;
            let beneUserType = 0;
            log.info(`[${C}], [${F}], ClientId [${transData?.clientId}], Beneficiary Id [${tBeneId}], Expense Type [${tExpenseType}], Non recurring expense`);

            if (tBeneId) {
              //beneficiary = await payoutBeneficiaryDB.getById({ id: tBeneId });
              beneUserId = beneficiary?.userId || 0;
              beneUserType = beneficiary?.userType || 0;
            } else {
              let vendorId: number;
              let vendor = await vendorDB.getByNameAndClientId({ name: "Payout", clientId: transData?.clientId });
              if (vendor) {
                vendorId = vendor.id;
              } else {
                log.info(`[${C}], [${F}], Client Id [${transData?.clientId}], Creating new beneficiary for quick transfer`);
                vendorId = await vendorDB.create({ mobile: "0000000001", name: "Payout", clientId: transData?.clientId, type: 1 });
              }
              log.info(`[${C}], [${F}], Client Id [${transData?.clientId}], Beneficiary Id [${vendorId}], Quick Transfer`);
              beneUserId = vendorId;
              beneUserType = CONSTANTS.USER_TYPE.VENDOR;
              log.info(`[${C}], [${F}], ClientId [${tClientId}], Paid To [${beneUserId}], Paid To Type [${beneUserType}]`);
            }

            //status = CONSTANTS.TRANSFER_STATUS.SUCCESS;
            //Create expense
            log.info(`[${C}], [${F}], ClientId [${tClientId}], Amount [${tAmount}], Paid To [${beneUserId}], Paid To Type [${beneUserType}], Creating expense`);
            expenseId = await expenseDB.create({
              type: tExpenseType, //For Other Expense. Need to improve it in future
              amount: tAmount,
              clientId: tClientId,
              paidDate: moment().format("YYYY-MM-DD HH:mm:ss"),
              paidByUserType: transData?.doneByType,
              paidBy: transData?.doneById,
              paidTo: beneUserId,
              paidToUserType: beneUserType,
              description: transData?.remark || "",
              paymentMethod: CONSTANTS.TRANSACTION_MODES.NET_BANKING,
              repetitionType: 1,
              noOfMonths: 0,
              dueDate: transData?.createdAt,
              isPaid: 1,
              paymentAccountNo: null,
              paymentAccountName: null,
              bankRefNum: data?.transfer_utr ? data?.transfer_utr : null,
            });
            log.info(`[${C}], [${F}], ClientId [${tClientId}], New Expense Id [${expenseId}], Paid To [${beneUserId}], Paid To UserType [${beneUserType}]`);
            if (beneUserType === CONSTANTS.USER_TYPE.STAFF) {

              let ledgerType = CONSTANTS.STAFF_LEDGER_TYPES.EXPENSES;
              if (14 === Number(tExpenseType)) //Salary
                ledgerType = CONSTANTS.STAFF_LEDGER_TYPES.SALARY
              else if (52 === Number(tExpenseType)) //Bonus
                ledgerType = CONSTANTS.STAFF_LEDGER_TYPES.BONUS
              else if (53 === Number(tExpenseType)) //HRA
                ledgerType = CONSTANTS.STAFF_LEDGER_TYPES.HRA
              else if (23 === Number(tExpenseType)) //Travel Allowance
                ledgerType = CONSTANTS.STAFF_LEDGER_TYPES.TRAVEL_ALLOWANCE

              //Due Entry
              await staffLedgerDB.addExpense({
                staffId: beneUserId,
                amount: -tAmount,
                type: ledgerType,
                mode: CONSTANTS.TRANSACTION_MODES.NET_BANKING,
                description: transData?.remark || "",
                expenseId: expenseId || null,
                paidDate: null,
                dueDate: moment(transData?.createdAt).format("YYYY-MM-DD"),
              });
              //Paid entry
              await staffLedgerDB.addExpense({
                staffId: beneUserId,
                amount: tAmount,
                type: ledgerType,
                mode: CONSTANTS.TRANSACTION_MODES.NET_BANKING,
                description: transData?.remark || "",
                expenseId: expenseId || null,
                paidDate: moment().format("YYYY-MM-DD HH:mm:ss"),
                dueDate: moment(transData?.createdAt).format("YYYY-MM-DD"),
              });
            }
          }
        }

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

        let beneficiaryMobile = beneficiary ?
          beneficiary?.mobile
          : transData?.beneficiaryMobile ? transData?.beneficiaryMobile : null;

        if (beneficiaryMobile) {
          sendWhatsappPayoutReceived(
            beneficiaryMobile, //mobile
            beneficiary?.name || beneficiaryMobile, //name
            tAmount, //amount
            data?.beneficiary_details?.beneficiary_instrument_details?.bank_account_number,//beneficiary account number
            client?.name || "-", //sending account number
            data?.transfer_utr || "-", //transaction utr
            paymentDate, //payment date
            data?.transfer_mode, //payment mode
            tClientId, //client id
          );
        }
        const logo = client?.logo
          ? process.env.RS_LOGO_URI + client.id + "/" + client.logo
          : String(process.env.RS_DEFAULT_LOGO_URI);
        let businessName = client?.name || "";
        businessName = client?.businessName || "";
        let reciept = await payoutReciept({
          logo: logo,
          clientId: transData?.clientId,
          transferId: transData?.transId,
          transferMethod: data?.transfer_mode.toUpperCase(),
          amount: Number(tAmount).toFixed(2),
          businessName: businessName,
          beneficiaryName: transData?.holderName,
          accountNumber: transData?.accountNum,
          ifsc: transData?.ifsc.toUpperCase(),
          utr: data?.transfer_utr ? data?.transfer_utr : null,
          remarks: transData?.remark || '-',
          charges: Number(transData?.vendorChargeDeducted).toFixed(2),
          paymentDate: moment().format("YYYY-MM-DD HH:mm:ss")
        });
        await payoutTransactionDB.updateReceipt({
          id: transData?.id,
          receipt: reciept
        });
      } else if ("ACKNOWLEDGED" === data.status) {
        status = CONSTANTS.TRANSFER_STATUS.ACKNOWLEDGED;
      } else if ("FAILED" === data.status) {
        status = CONSTANTS.TRANSFER_STATUS.FAILED;
        let beneficiary: any;
        let tBeneId = transData?.payoutBeneficiaryId;
        beneficiary = await payoutBeneficiaryDB.getById({ id: tBeneId });
        if (beneficiary?.userType === CONSTANTS.USER_TYPE.TENANT) {
          let tenantId = beneficiary?.userId;
          log.info(`[${C}], [${F}], Client Id [${transData?.clientId}], Tenant Id [${tenantId}], Tenant transfer failed`);
          await moveOutDB.updateRefundStatusToPending({clientId: transData?.clientId, tenantId: tenantId});
        }
      } else if ("REVERSED" === data.status) {
        status = CONSTANTS.TRANSFER_STATUS.REVERSED;
        let beneficiary: any;
        let tBeneId = transData?.payoutBeneficiaryId;
        beneficiary = await payoutBeneficiaryDB.getById({ id: tBeneId });
        if (beneficiary?.userType === CONSTANTS.USER_TYPE.TENANT) {
          let tenantId = beneficiary?.userId;
          log.info(`[${C}], [${F}], Client Id [${transData?.clientId}], Tenant Id [${tenantId}], Tenant transfer reversed`);
          await moveOutDB.updateRefundStatusToPending({clientId: transData?.clientId, tenantId: tenantId});
        }
      } else if ("REJECTED" === data.status) {
        status = CONSTANTS.TRANSFER_STATUS.REJECTED;
        let beneficiary: any;
        let tBeneId = transData?.payoutBeneficiaryId;
        beneficiary = await payoutBeneficiaryDB.getById({ id: tBeneId });
        if (beneficiary?.userType === CONSTANTS.USER_TYPE.TENANT) {
          let tenantId = beneficiary?.userId;
          log.info(`[${C}], [${F}], Client Id [${transData?.clientId}], Tenant Id [${tenantId}], Tenant transfer rejected`);
          await moveOutDB.updateRefundStatusToPending({clientId: transData?.clientId, tenantId: tenantId});
        }
      }
      log.info(`[${C}], [${F}], Client Id [${tClientId}], Trans ID [${transData?.id}], Transfer Id [${data.transfer_id}], Transfer Amount [${data?.transfer_amount}], Transfer Charge [${data?.transfer_service_charge}], Transfer Tax [${data?.transfer_service_tax}], Status [${status}], UTR [${data?.transfer_utr}]`);
      await payoutTransactionDB.updateTransaction({
        id: transData?.id,
        status,
        statusDescription: data?.status_description,
        transferUtr: data?.transfer_utr ? data?.transfer_utr : null,
        charges: data?.transfer_service_charge ? data?.transfer_service_charge : null,
        tax: data?.transfer_service_tax ? data?.transfer_service_tax : null,
        expenseId: expenseId ?? null
      });

      // if ("SUCCESS" === data.status && transData?.mode === CONSTANTS.PAYOUT_MODE.IMPS) {
      //   await recordTransferInitiate({
      //       clientId: tClientId,
      //       userType: Number(transData?.doneByType),
      //       staffId: Number(transData?.doneByType) === CONSTANTS.USER_TYPE.STAFF ? Number(transData?.doneById) : null,
      //       transferAmount: Number(Number(tAmount) + Number(data?.transfer_service_charge || 0) + Number(data?.transfer_service_tax || 0).toFixed(2))
      //   });
      //   await payoutTransactionDB.updateIsWalletAdjusted({
      //     id: transData?.id,
      //     isWalletAdjusted: 1,
      //   });
      // } else if ("SUCCESS" === data.status && transData?.mode === CONSTANTS.PAYOUT_MODE.IMPS && "COMPLETED" !== data?.status_code) {
      //   await recordTransferInitiate({
      //       clientId: tClientId,
      //       userType: Number(transData?.doneByType),
      //       staffId: Number(transData?.doneByType) === CONSTANTS.USER_TYPE.STAFF ? Number(transData?.doneById) : null,
      //       transferAmount: Number(Number(data?.transfer_service_charge || 0) + Number(data?.transfer_service_tax || 0).toFixed(2))
      //   });
      //   await payoutTransactionDB.updateIsWalletAdjusted({
      //     id: transData?.id,
      //     isWalletAdjusted: 1,
      //   });
      // } 
      if ("SUCCESS" === data.status && "COMPLETED" === data?.status_code) {
        await recordTransferInitiate({
            clientId: tClientId,
            userType: Number(transData?.doneByType),
            staffId: Number(transData?.doneByType) === CONSTANTS.USER_TYPE.STAFF ? Number(transData?.doneById) : null,
            transferAmount: Number(Number(data?.transfer_service_charge || 0) + Number(Number(data?.transfer_service_tax || 0).toFixed(2)))
        });
        await payoutTransactionDB.updateIsWalletAdjusted({
          id: transData?.id,
          isWalletAdjusted: 1,
        });
      } else if (transData?.isWalletAdjusted === 1 && ("FAILED" === data.status || "REVERSED" === data.status || "REJECTED" === data.status)) {
        await handleTransferFail({
          clientId: tClientId,
          userType: Number(transData?.doneByType),
          staffId: Number(transData?.doneByType) === CONSTANTS.USER_TYPE.STAFF ? Number(transData?.doneById) : null,
          transferAmount: Number(Number(tAmount).toFixed(2))
        });
      }

      if (transData?.autopayQueueId && Number(transData?.autopayQueueId) > 0) {
        log.info(`[${C}], [${F}], Client Id [${tClientId}], Auto Pay Queue Id [${transData?.autopayQueueId}], Updating Auto pay Queue Status`)
        let autopayQueueStatus = CONSTANTS.AUTOPAY_QUEUE_STATUS.SUCCESS;
        if (data.status === "SUCCESS" && "COMPLETED" === data?.status_code) {
          autopayQueueStatus = CONSTANTS.AUTOPAY_QUEUE_STATUS.SUCCESS;
          //Sending Whatsapp to client regarding payout success
          let client = await clientDB.getById({id: tClientId});
          sendWhatsappPayoutDebit(
            client?.mobile, //mobile
            client?.name,
            tAmount, //amount
            data?.beneficiary_details?.beneficiary_instrument_details?.bank_account_number,//beneficiary account number
            data?.transfer_utr || "-", //transaction utr
            paymentDate, //payment date
            `Autopay`, //payment mode
            tClientId, //client id
          );

        } else if (data.status === "SUCCESS") {
          autopayQueueStatus = CONSTANTS.AUTOPAY_QUEUE_STATUS.IN_PROGRESS;
        } else {
          autopayQueueStatus = CONSTANTS.AUTOPAY_QUEUE_STATUS.FAILED;
        }

        log.info(`[${C}], [${F}], Client Id [${tClientId}], Auto Pay Queue Id [${transData?.autopayQueueId}], Auto Pay Queue Status [${autopayQueueStatus}], Status [${data.status}], Status Code [${data?.status_code}], Status Description [${data?.status_description}]`)

        await autopayDB.updateTransactionStatus({
          id: transData?.autopayQueueId,
          status: autopayQueueStatus,
          description: data?.status_description,
          paidDate: moment().format("YYYY-MM-DD"),
        });

        // let client = await clientDB.getById({id: tClientId});
        // sendWhatsappPayoutDebit(
        //   client?.mobile, //mobile
        //   client?.name,
        //   tAmount, //amount
        //   data?.beneficiary_details?.beneficiary_instrument_details?.bank_account_number,//beneficiary account number
        //   data?.transfer_utr || "-", //transaction utr
        //   paymentDate, //payment date
        //   `Autopay`, //payment mode
        //   tClientId, //client id
        // );

        log.info(`[${C}], [${F}], Auto Pay Queue Status [${autopayQueueStatus}], Client Id [${tClientId}], Auto Pay Queue Id [${transData?.autopayQueueId}], Auto Pay Queue Updated successfully`)
      }

      log.info(`[${C}], [${F}], Client Id [${tClientId}], Callback executed successfully`);
    }
    else if (type === "CREDIT_CONFIRMATION") {
      // payout wallet recharge block
      let fundSourceId = data?.fundsource_id;
      log.info(`[${C}], [${F}], Ledger Balance [${data.ledger_balance}], Amount [${data.amount}], Found Source [${fundSourceId}], UTR [${data.utr}]`);
      let clientId = 5;
      if (fundSourceId === 'THINK STRAIGHT ADVISORY LLP') {
        log.info(`[${C}], [${F}], Client Id [${clientId}]`);
        await payoutTransactionDB.addTopup({
          clientId,
          amount: data.amount,
          ledgerBalance: data.ledger_balance,
          fundSource: fundSourceId,
          utr: data.utr,
        });
      }
      else if (fundSourceId === 'OXOTEL SPACE HOTEL AND TOURISM PRIVATE LIMITE') {
        clientId = 519;
        log.info(`[${C}], [${F}], Client Id [${clientId}]`);
        await payoutTransactionDB.addTopup({
          clientId,
          amount: data.amount,
          ledgerBalance: data.ledger_balance,
          fundSource: fundSourceId,
          utr: data.utr,
        });
      }
      else {
        clientId = 519;
        log.info(`[${C}], [${F}], No Client found`);
      }

      await handleRecharge({
        subWalletId: fundSourceId,
        rechargeAmount: data?.amount,
      });
    }
    else {
      log.info(
        `[${C}], [${F}], Not entertaining this type [${type}]`
      );
    }
    return res.status(200).json({
      msg: "Success",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(200).json({
      msg: "Success",
      isSuccess: false,
    });
  }
};


payout.InitiateTransfer = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "InitiateTransfer";

  try {
    let { id, beneficiaryId, accountName, accountNumber, ifsc, amount, method, remarks, expenseType, mobile, currentBalance = 0.00, deductChargesFromVendor = 0 } = req.body;
    const userType = req.userType;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );
    let isQuickTransfer: boolean;
    if (id && !Number.isNaN(id)) {
      isQuickTransfer = false;
      log.info(
        `[${C}], [${F}], ClientId [${clientId}], Beneficiary Id [${id}], Beneficiary Id (Cf) [${beneficiaryId}], Amount [${amount}], Transfer Mode [${method}], Remark [${remarks}], Expense Type [${expenseType}], Mobile [${mobile}], Current Balance [${currentBalance}], Deduct Charges [${deductChargesFromVendor}], Platform [${req.platform}]`
      );
    } else {
      isQuickTransfer = true;
      log.info(
        `[${C}], [${F}], ClientId [${clientId}], Account Name [${accountName}], Account Number [${accountNumber}], IFSC [${ifsc}], Amount [${amount}], Transfer Mode [${method}], Remark [${remarks}], Expense Type [${expenseType}], Mobile [${mobile}], Current Balance [${currentBalance}], Deduct Charges [${deductChargesFromVendor}], Platform [${req.platform}]`
      );
      if (
        [accountName, accountNumber, ifsc, amount, method]
          .some(v => v === undefined || v === null || String(v).trim() === "")
      ) {
        return res.status(400).json({
          msg: "Parameter missing",
          isSuccess: false,
        });
      }
    }
    //const charges = Number(deductChargesFromVendor) === 1 ? 3.9 : 0;
    let transferAmount = Number(amount);
    let charges = 0;

    if (Number(deductChargesFromVendor) === 1) {
      charges = CHARGE_IN_PAISE / 100; // 3.90
      if (Number(amount) < charges) {
        return res.status(400).json({
          msg: "Amount should be greater than ₹3.90",
          isSuccess: false,
        });
      }
      const transferAmountInPaise = Math.max(0, Math.round(Number(amount) * 100) - CHARGE_IN_PAISE);

      transferAmount = transferAmountInPaise / 100;
    }

    log.info(
      `[${C}], [${F}], Original Amount [${amount}], Transfer Amount [${transferAmount}], Deduct Charges [${deductChargesFromVendor}]`
    );

    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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );
    }
    let transferId = await generatePayOutTransId();
    let transferMode = "neft";
    if (CONSTANTS.PAYOUT_MODE.IMPS === method) {
      transferMode = "imps";
    } else if (CONSTANTS.PAYOUT_MODE.RTGS === method) {
      transferMode = "rtgs";
    }
    let payoutResp: any;
    if (!isQuickTransfer) {
      let accountDetails = await payoutBeneficiaryDB.getById({ id });
      accountName = accountDetails?.holderName;
      accountNumber = accountDetails?.accountNum;
      ifsc = accountDetails?.ifsc;
      payoutResp = await cashfreePayout.payoutToBeneficiary(C, F, clientId, transferId, transferAmount, beneficiaryId, transferMode, remarks);
    } else {
      payoutResp = await cashfreePayout.quickPayout(C, F, clientId, transferId, transferAmount, accountName, accountNumber, ifsc, transferMode, remarks);
    }
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Transfer Resp [${JSON.stringify(payoutResp)}]`
    );

    // payoutResp = {
    //   data: {
    //     cf_transfer_id:"2113131313",
    //     status :"INITIATED",
    //     status_description:"Transfer Initiated"
    //   }
    // }

    let beneficiary: any = null;
    if (!isQuickTransfer) beneficiary = await payoutBeneficiaryDB.getById({ id: beneficiaryId });

    if (payoutResp?.data?.status) {
      let status = CONSTANTS.TRANSFER_STATUS.INITIATED;
      if ("FAILED" === payoutResp?.data?.status.toUpperCase())
        status = CONSTANTS.TRANSFER_STATUS.FAILED;
      else if ("REJECTED" === payoutResp?.data?.status.toUpperCase()) {
        status = CONSTANTS.TRANSFER_STATUS.REJECTED;
      }
      const transId = await payoutTransactionDB.add({
        clientId,
        transId: transferId,
        cfTransferId: payoutResp?.data?.cf_transfer_id,
        amount: transferAmount,
        vendorChargeDeducted: charges,
        charges: null,
        mode: method,
        payoutBeneficiaryId: id,
        cfBeneficiaryId: beneficiaryId,
        expenseId: null,
        expenseType,
        status,
        statusDescription: payoutResp?.data?.status_description,
        holderName: accountName,
        accountNum: accountNumber,
        ifsc: ifsc,
        remark: remarks,
        doneById: Number(req.id),
        doneByType: userType,
        beneficiaryMobile: beneficiary ? beneficiary?.beneficiaryMobile : mobile ? mobile : null,
        currentBalance: Number(currentBalance) || 0.00,
      });
      
      await recordTransferInitiate({
          clientId: clientId,
          userType: Number(userType),
          staffId: Number(userType) === CONSTANTS.USER_TYPE.STAFF ? Number(req.id) : null,
          transferAmount: Number(Number(transferAmount).toFixed(2))
      });
      await payoutTransactionDB.updateIsWalletAdjusted({
        id: transId,
        isWalletAdjusted: 1,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], B Id [${id}], Beneficiary Id [${beneficiaryId}], Transfer initiated successfully`
      );
      return res.status(200).json({
        msg: "Transfer initiated",
        isSuccess: true,
      });
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], B Id [${id}], Beneficiary Id [${beneficiaryId}], Transfer Failed`
      );
      return res.status(400).json({
        msg: payoutResp?.error?.message || CONSTANTS.MSG.ERROR_MESSAGE,
        isSuccess: false,
      });
    }

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

payout.ListTransfers = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "ListTransfers";
  try {
      const platform = req.platform;

      log.info(`[${C}], [${F}], Platform [${platform}]`);
      if(platform === CONSTANTS.TENANT_DEVICE_TYPE.ANDROID || platform === CONSTANTS.TENANT_DEVICE_TYPE.IOS) {
        log.info(`[${C}], [${F}], Platform [${platform}], Sending list for App`);
        return payout.ListTransfersForApp(req, res);
      } else {
        log.info(`[${C}], [${F}], Platform [${platform}], Sending list for web`);
        return payout.ListTransfersForWeb(req, res);
      }
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

payout.ListTransfersForWeb = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "ListTransfersForWeb";
  try {
    //let { beneficiaryId } = req.query
    const { beneficiaryId, startDate, endDate, s, t, pageNum = null } = req.query;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Beneficiary Id [${beneficiaryId}], StartDate [${startDate}], End Date [${endDate}], Search Value [${s}], Search Type [${t}], Page Num [${pageNum}]`);
    let limit = 10;

    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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );
    }

    let list: any;
    let summary: any = {};
    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      if (beneficiaryId && Number(beneficiaryId) !== 0) {

        if (s && s !== undefined && s !== "") {
          list = await payoutTransactionDB.getByBeneficiaryIdWithSearch({
            clientId,
            beneficiaryId,
            searchVal: s,
            searchType: t,
            pageNum,
            limit,
          });
        } else {
          list = await payoutTransactionDB.getByBeneficiaryIdWithDate({
            clientId,
            beneficiaryId,
            startDate,
            endDate,
            pageNum,
            limit
          });
        }

        // list = await payoutTransactionDB.getByBeneficiaryId({
        //   clientId,
        //   payoutBeneficiaryId: Number(beneficiaryId)
        // });
      } else {
        if (s && s !== undefined && s !== "") {
          list = await payoutTransactionDB.getByClientIdWithSearch({
            clientId,
            searchVal: s,
            searchType: t,
            pageNum,
            limit
          });
        } else {
          list = await payoutTransactionDB.getByClientIdWithDate({
            clientId,
            startDate,
            endDate,
            pageNum,
            limit
          });
        }

        summary = await payoutTransactionDB.getTransactionsSummaryByClientIdWithDate({
          clientId,
          startDate,
          endDate,
        });
      }
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], For staff`
      );
      if (beneficiaryId && Number(beneficiaryId) !== 0) {

        if (s && s !== undefined && s !== "") {
          list = await payoutTransactionDB.getByBeneficiaryIdWithSearch({
            clientId,
            beneficiaryId,
            searchVal: s,
            searchType: t,
            pageNum,
            limit,
          });
        } else {
          list = await payoutTransactionDB.getByBeneficiaryIdWithDate({
            clientId,
            beneficiaryId,
            startDate,
            endDate,
            pageNum,
            limit
          });
        }

        // list = await payoutTransactionDB.getByBeneficiaryId({
        //   clientId,
        //   payoutBeneficiaryId: Number(beneficiaryId)
        // });
      } else {
        if (s && s !== undefined && s !== "") {
          list = await payoutTransactionDB.getByClientIdWithSearchForStaff({
            clientId,
            searchVal: s,
            searchType: t,
            doneById: req.id, 
            pageNum,
            limit
          });
        } else {
          list = await payoutTransactionDB.getByClientIdWithDateForStaff({
            clientId,
            startDate,
            endDate,
            doneById: req.id,
            pageNum,
            limit
          });
        }

        summary = await payoutTransactionDB.getTransactionsSummaryByClientIdWithDateForStaff({
          clientId,
          doneById: req.id,
          startDate,
          endDate,
        });
      }
    }

    let beneficiaries = await payoutBeneficiaryDB.getByClientIdForDropDown({ clientId });
    //let lastPaymentDoneData = await expenseDB.getLastPaymentDoneToUsers({clientId });
    
    // const lastPaymentMap = new Map(lastPaymentDoneData.map((item: any) => [
    //     `${item.paidTo}_${item.paidToUserType}`,
    //     item,
    // ]));
    
    if(beneficiaries && beneficiaries.length > 0) {
      beneficiaries.forEach((beneficiary: any) => {
        // const lastPayment: any = lastPaymentMap.get(
        //   `${beneficiary.userId}_${beneficiary.userType}`
        // );

        beneficiary.isLastPaymentOnline =  !!beneficiary?.lastPaidAmount;
      });
    }

    let balance = await cashfreePayout.getWalletBalance(C, F, clientId);
    if (!balance.error) {
      summary.balance = Number(balance?.availableBalance);
    } else {
      summary.balance = 0;
    }

    let payoutData = await getWalletBalanceAndLimits({
      clientId,
      userType: Number(userType),
      staffId: userType === CONSTANTS.USER_TYPE.STAFF ? Number(req.id) : null,
    });
    summary.payoutData = payoutData;
    summary.balance = payoutData?.walletBalance || summary.balance;

    let expenseCategories = await expenseDB.getExpenseCategories();

    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 || [];
    }


    log.info(
      `[${C}], [${F}], Client Id [${clientId}], list sent successfully`
    );

    return res.status(200).json({
      msg: "List sent successfully",
      beneficiaries: beneficiaries || [],
      data: list || [],
      summary: summary,
      categories: expenseCategories || [],
      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,
    });
  }
};


payout.ListTransfersForApp = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "ListTransfersForApp";
  try {
    const { beneficiaryId, startDate, endDate, s, pageNum = null } = req.query;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Beneficiary Id [${beneficiaryId}], StartDate [${startDate}], End Date [${endDate}], Search Value [${s}], Page Num [${pageNum}]`);
    let limit = 10;

    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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );
    }

    let list: any;
    let summary: any = {};
    if (beneficiaryId && Number(beneficiaryId) !== 0) {

      if (s && s !== undefined && s !== "") {
        list = await payoutTransactionDB.getByBeneficiaryIdWithSearchForApp({
          clientId,
          beneficiaryId,
          searchVal: s,
          pageNum,
          limit,
        });
      } else {
        list = await payoutTransactionDB.getByBeneficiaryIdWithDate({
          clientId,
          beneficiaryId,
          startDate,
          endDate,
          pageNum,
          limit
        });
      }

    } else {
      if (s && s !== undefined && s !== "") {
        list = await payoutTransactionDB.getByClientIdWithSearchForApp({
          clientId,
          searchVal: s,
          pageNum,
          limit
        });
      } else {
        list = await payoutTransactionDB.getByClientIdWithDate({
          clientId,
          startDate,
          endDate,
          pageNum,
          limit
        });
      }

      summary = await payoutTransactionDB.getTransactionsSummaryByClientIdWithDate({
        clientId,
        startDate,
        endDate,
      });
    }

    let beneficiaries = await payoutBeneficiaryDB.getByClientIdForDropDown({ clientId });
    let lastPaymentDoneData = await expenseDB.getLastPaymentDoneToUsers({clientId });
    
    const lastPaymentMap = new Map(lastPaymentDoneData.map((item: any) => [
        `${item.paidTo}_${item.paidToUserType}`,
        item,
    ]));
    if(beneficiaries && beneficiaries.length > 0) {
      beneficiaries.forEach((beneficiary: any) => {
        const lastPayment: any = lastPaymentMap.get(
          `${beneficiary.userId}_${beneficiary.userType}`
        );

        // beneficiary.lastPaidDate = lastPayment?.paidDate ?? null;
        // beneficiary.lastPaidAmount = lastPayment?.paidAmount ?? 0;
        // beneficiary.isLastPaymentOnline =  !!lastPayment?.bankRefNum;
        beneficiary.isLastPaymentOnline =  !!beneficiary?.lastPaidAmount;
      });
    }
    
    let balance = await cashfreePayout.getWalletBalance(C, F, clientId);
    if (!balance.error) {
      summary.balance = Number(balance?.availableBalance);
    } else {
      summary.balance = 0;
    }

    let payoutData = await getWalletBalanceAndLimits({
      clientId,
      userType: Number(userType),
      staffId: userType === CONSTANTS.USER_TYPE.STAFF ? Number(req.id) : null,
    });
    summary.payoutData = payoutData;
    summary.balance = payoutData?.walletBalance || summary.balance;

    let expenseCategories = await expenseDB.getExpenseCategories();

    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 || [];
    }


    log.info(
      `[${C}], [${F}], Client Id [${clientId}], list sent successfully`
    );
    //log.info(JSON.stringify(beneficiaries));
    return res.status(200).json({
      msg: "List sent successfully",
      beneficiaries: beneficiaries || [],
      data: list || [],
      summary: summary,
      categories: expenseCategories || [],
      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,
    });
  }
};

payout.InitiateLandlordPayOut = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "InitiateLandlordPayOut";
  try {
    const { landlordId, amount, type = 1, paymentType, remarks = null, expenseId, method, beneficiaryId = null } = req.body;

    //paymentType - 0 Paying Fully, 1 Paying partially
    //type : 1 Paying Rent, 2 Paying Security 
    //let clientId = req.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}]`
        }, Landlord Id [${landlordId}], Amount [${amount}], Payment Method [${method}], Payment Type [${paymentType}], Expense Id [${expenseId}], Beneficiary Id [${beneficiaryId}], Remark [${remarks}], ${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.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Landlord Id [${landlordId}], Amount [${amount}], Payment Type [${paymentType}], Expense Id [${expenseId}], Beneficiary Id [${beneficiaryId}], Remark [${remarks}], Admin Requesting....`
      );
    }

    let expense = await expenseDB.getUnpaidById({ id: expenseId });
    if (!expense) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], No unpaid expense found`);
      return res.status(400).json({ msg: "No expense found", isSuccess: false });
    }

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

    const landlord = await landlordDB.getById({
      id: landlordId,
    });
    if (!landlord) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Amount [${amount}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const client = await clientDB.getById({
      id: clientId,
    });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Amount [${amount}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let getBeneficiary: any;
    if (beneficiaryId) {
      getBeneficiary = await payoutBeneficiaryDB.getById({ id: beneficiaryId });
    } else {
      getBeneficiary = await payoutBeneficiaryDB.getByUserIdAndUserType({ userId: landlordId, userType: CONSTANTS.USER_TYPE.LANDLORD, clientId });
    }
    if (!getBeneficiary) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], No Beneficiary Found`);
      return res.status(400).json({ msg: "Landlord is not added as a beneficiary", isSuccess: false });
    }
    if (amount < 1) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Amount [${amount}], Amount cannot be less then 1`
      );
      return res.status(400).json({
        msg: `Amount entered is below the allowed limit`,
        isSuccess: true,
      });
    }
    let payoutResp: any = [];
    //if(CONSTANTS.PAYMENT_TYPE.CASHFREE === paymentType) {
    let transferId = await generatePayOutTransId();
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Amount [${amount}], Transfer Id [${transferId}]`
    );
    let transferMode = "neft";
    if (CONSTANTS.PAYOUT_MODE.IMPS === method) {
      transferMode = "imps";
    } else if (CONSTANTS.PAYOUT_MODE.RTGS === method) {
      transferMode = "rtgs";
    }
    payoutResp = await cashfreePayout.payoutToBeneficiary(C, F, clientId, transferId, amount, getBeneficiary?.cfBeneficiaryId, transferMode, remarks);
    if (payoutResp?.data?.status) {
      let status = CONSTANTS.TRANSFER_STATUS.INITIATED;
      if ("FAILED" === payoutResp?.data?.status.toUpperCase())
        status = CONSTANTS.TRANSFER_STATUS.FAILED;
      else if ("REJECTED" === payoutResp?.data?.status.toUpperCase()) {
        status = CONSTANTS.TRANSFER_STATUS.REJECTED;
      }
      const transId = await payoutTransactionDB.add({
        clientId,
        transId: transferId,
        cfTransferId: payoutResp?.data?.cf_transfer_id,
        amount,
        charges: null,
        mode: method,
        payoutBeneficiaryId: getBeneficiary?.id,
        cfBeneficiaryId: getBeneficiary?.cfBeneficiaryId,
        expenseId: expenseId,
        expenseType: expense?.type,
        status,
        statusDescription: payoutResp?.data?.status_description,
        holderName: getBeneficiary?.holderName,
        accountNum: getBeneficiary?.accountNum,
        ifsc: getBeneficiary?.ifsc,
        remark: remarks,
        doneById: Number(req.id),
        doneByType: userType,
        beneficiaryMobile: getBeneficiary?.beneficiaryMobile || null,
      });

      await recordTransferInitiate({
          clientId: clientId,
          userType: Number(userType),
          staffId: Number(userType) === CONSTANTS.USER_TYPE.STAFF ? Number(req.id) : null,
          transferAmount: Number(Number(amount).toFixed(2))
      });
      await payoutTransactionDB.updateIsWalletAdjusted({
        id: transId,
        isWalletAdjusted: 1,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], B Id [${getBeneficiary?.id}], Beneficiary Id [${getBeneficiary?.cfBeneficiaryId}], Transfer initiated successfully`
      );
      return res.status(200).json({
        msg: "Transfer initiated",
        isSuccess: true,
      });
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], LandlordId [${landlordId}], Transfer Failed`
      );
      return res.status(400).json({
        msg: payoutResp?.error?.message || CONSTANTS.MSG.ERROR_MESSAGE,
        isSuccess: false,
      });
    }
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};


payout.InitiateExpensePayOut = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "InitiateLandlordPayOut";
  try {
    const { payoutUserType,  payoutUserId, amount, type = 1, paymentType, remarks = null, expenseId, method, beneficiaryId = null } = req.body;

    //paymentType - 0 Paying Fully, 1 Paying partially
    //type : 1 Paying Rent, 2 Paying Security 
    //let clientId = req.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}]`
        }, Payout UserType [${payoutUserType}], Payout User Id [${payoutUserId}], Amount [${amount}], Payment Method [${method}], Payment Type [${paymentType}], Expense Id [${expenseId}], Beneficiary Id [${beneficiaryId}], Remark [${remarks}], ${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.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Payout UserType [${payoutUserType}], Payout User Id [${payoutUserId}], Amount [${amount}], Payment Type [${paymentType}], Expense Id [${expenseId}], Beneficiary Id [${beneficiaryId}], Remark [${remarks}], Admin Requesting....`
      );
    }

    let expense = await expenseDB.getUnpaidById({ id: expenseId });
    if (!expense) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Expense Id [${expenseId}], No unpaid expense found`);
      return res.status(400).json({ msg: "No expense found", isSuccess: false });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Amount [${amount}], Expense Id [${expenseId}], Expense Type [${expense?.type}]`
    );
    let payoutTo: any;
    if (CONSTANTS.USER_TYPE.LANDLORD === Number(payoutUserType)) {
      payoutTo = await landlordDB.getById({
        id: payoutUserId,
      });
    } else if (CONSTANTS.USER_TYPE.STAFF === Number(payoutUserType)) {
      payoutTo = await staffDB.getById({
        id: payoutUserId,
      });
    } else if (CONSTANTS.USER_TYPE.VENDOR === Number(payoutUserType)) {
      payoutTo = await vendorDB.getById ({
        id: payoutUserId,
      });
    }
    if (!payoutTo) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Payout UserType [${payoutUserType}], Payout User Id [${payoutUserId}], Amount [${amount}], No User Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const client = await clientDB.getById({
      id: clientId,
    });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Payout UserType [${payoutUserType}], Payout User Id [${payoutUserId}], Amount [${amount}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let getBeneficiary: any;
    if (beneficiaryId) {
      getBeneficiary = await payoutBeneficiaryDB.getById({ id: beneficiaryId });
    } else {
      getBeneficiary = await payoutBeneficiaryDB.getByUserIdAndUserType({ userId: payoutUserId, userType: payoutUserType, clientId });
    }
    if (!getBeneficiary) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Payout UserType [${payoutUserType}], Payout User Id [${payoutUserId}], No Beneficiary Found`);
      return res.status(400).json({ msg: "User is not added as a beneficiary", isSuccess: false });
    }
    if (amount < 1) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Payout UserType [${payoutUserType}], Payout User Id [${payoutUserId}], Amount [${amount}], Amount cannot be less then 1`
      );
      return res.status(400).json({
        msg: `Amount entered is below the allowed limit`,
        isSuccess: true,
      });
    }
    let payoutResp: any = [];
    //if(CONSTANTS.PAYMENT_TYPE.CASHFREE === paymentType) {
    let transferId = await generatePayOutTransId();
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Payout UserType [${payoutUserType}], Payout User Id [${payoutUserId}], Amount [${amount}], Transfer Id [${transferId}]`
    );
    let transferMode = "neft";
    if (CONSTANTS.PAYOUT_MODE.IMPS === method) {
      transferMode = "imps";
    } else if (CONSTANTS.PAYOUT_MODE.RTGS === method) {
      transferMode = "rtgs";
    }
    payoutResp = await cashfreePayout.payoutToBeneficiary(C, F, clientId, transferId, amount, getBeneficiary?.cfBeneficiaryId, transferMode, remarks);
    if (payoutResp?.data?.status) {
      let status = CONSTANTS.TRANSFER_STATUS.INITIATED;
      if ("FAILED" === payoutResp?.data?.status.toUpperCase())
        status = CONSTANTS.TRANSFER_STATUS.FAILED;
      else if ("REJECTED" === payoutResp?.data?.status.toUpperCase()) {
        status = CONSTANTS.TRANSFER_STATUS.REJECTED;
      }
      const transId = await payoutTransactionDB.add({
        clientId,
        transId: transferId,
        cfTransferId: payoutResp?.data?.cf_transfer_id,
        amount,
        charges: null,
        mode: method,
        payoutBeneficiaryId: getBeneficiary?.id,
        cfBeneficiaryId: getBeneficiary?.cfBeneficiaryId,
        expenseId: expenseId,
        expenseType: expense?.type,
        status,
        statusDescription: payoutResp?.data?.status_description,
        holderName: getBeneficiary?.holderName,
        accountNum: getBeneficiary?.accountNum,
        ifsc: getBeneficiary?.ifsc,
        remark: remarks,
        doneById: Number(req.id),
        doneByType: userType,
        beneficiaryMobile: getBeneficiary?.beneficiaryMobile || null,
      });

      await recordTransferInitiate({
          clientId: clientId,
          userType: Number(userType),
          staffId: Number(userType) === CONSTANTS.USER_TYPE.STAFF ? Number(req.id) : null,
          transferAmount: Number(Number(amount).toFixed(2))
      });
      await payoutTransactionDB.updateIsWalletAdjusted({
        id: transId,
        isWalletAdjusted: 1,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], B Id [${getBeneficiary?.id}], Beneficiary Id [${getBeneficiary?.cfBeneficiaryId}], Transfer initiated successfully`
      );
      return res.status(200).json({
        msg: "Transfer initiated",
        isSuccess: true,
      });
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Payout UserType [${payoutUserType}], Payout User Id [${payoutUserId}], Transfer Failed`
      );
      return res.status(400).json({
        msg: payoutResp?.error?.message || CONSTANTS.MSG.ERROR_MESSAGE,
        isSuccess: false,
      });
    }
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

payout.DeactivateBeneficiary = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "DeactivateBeneficiary";
  try {
    const userType = req.userType;
    let { id } = req.body;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Beneficiary Id [${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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting.....`
      );
    }
    let beneficiary = await payoutBeneficiaryDB.getById({ id });
    if (!beneficiary) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Id [${id}], Invalid beneficiary`
      );
      return res.status(400).json({
        msg: `Invalid beneficiary`,
        isSuccess: false,
      });
    }

    await payoutBeneficiaryDB.updateStatus({
      id,
      status: CONSTANTS.BENEFICIARY_STATUS.INACTIVE
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Id [${id}], Beneficiary deactivated successfully`
    );

    return res.status(200).json({
      msg: "Beneficiary deactivated 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,
    });
  }
};

payout.ActivateBeneficiary = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "ActivateBeneficiary";
  try {
    const userType = req.userType;
    let { id } = req.body;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Beneficiary Id [${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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting.....`
      );
    }
    let beneficiary = await payoutBeneficiaryDB.getById({ id });
    if (!beneficiary) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Id [${id}], Invalid beneficiary`
      );
      return res.status(400).json({
        msg: `Invalid beneficiary`,
        isSuccess: false,
      });
    }

    await payoutBeneficiaryDB.updateStatus({
      id,
      status: CONSTANTS.BENEFICIARY_STATUS.VERIFIED
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Id [${id}], Beneficiary activated successfully`
    );

    return res.status(200).json({
      msg: "Beneficiary activated 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,
    });
  }
};

payout.VerifyAccount = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "VerifyAccount";
  try {
    const { accountNumber, ifsc } = req.query;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Account Number [${accountNumber}], IFSC [${ifsc}]`);

    if (!accountNumber || !ifsc) {
      return res.status(400).json({
        msg: "Account number and IFSC are required",
        isSuccess: false,
      });
    }

    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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting.....`
      );
    }

    let output = await cashfreePayout.verifyAccount(C, F, clientId, accountNumber, ifsc);
    //let output = {"isSuccess":true,"data":{"reference_id":1437155394,"name_at_bank":"Mr. ANUJ  .","bank_name":"STATE BANK OF INDIA","utr":"616717163977","city":"ISRANA","branch":"ISRANA","micr":132002013,"name_match_score":null,"name_match_result":null,"account_status":"VALID","account_status_code":"ACCOUNT_IS_VALID","ifsc_details":{"bank":"STATE BANK OF INDIA","ifsc":"SBIN0013693","micr":132002013,"nbin":null,"address":"PREMISES OF SH AJMER SINGH S/O SH MAHA SINGH AMAR ANAJ MANDI P.O. ISRANA DISTT PANIPAT 132107","city":"ISRANA","state":"HARYANA","branch":"ISRANA","ifsc_subcode":"SBIN0","category":null,"swift_code":null}}};
    //let output = {"isSuccess":false,"data": {"name_at_bank": "", "bank_name": "", "branch": "", "city": "", "account_status": "", "account_status_code": ""}};

    if (output?.isSuccess === true && output?.data?.name_at_bank) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Account Verified`
      );

      // const verificationData = {
      //   accountHolderName: output?.name_at_bank,
      //   //accountHolderName: 'Sukhbir Singh',
      //   bankName: output?.bank_name,
      //   branch: output?.branch,
      //   city: output?.city,
      //   accountStatus: output?.account_status,
      //   accountStatusCode: output?.account_status_code,
      //   isVerified:
      //     !(output?.account_status_code === "ACCOUNT_IS_VALID"),
      // };

      let verificationData = {
        accountHolderName: output?.data?.name_at_bank,
        bankName: output?.data?.bank_name,
        branch: output?.data?.branch,
        city: output?.data?.city,
        accountStatus: output?.data?.account_status,
        accountStatusCode: output?.data?.account_status_code,
        isVerified:
          output?.data?.account_status_code === "ACCOUNT_IS_VALID",
      };

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Api Data [${JSON.stringify(output)}]`
      );

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Resp Data [${JSON.stringify(verificationData)}], Account verification completed`
      );

      return res.status(200).json({
        msg: "Account verified successfully",
        //data1: output || [],
        data: verificationData || [],
        isSuccess: true,
      });
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Account verification failed`
      );
      let verificationData = {
        accountHolderName: output?.data?.name_at_bank || null,
        bankName: output?.data?.bank_name || null,
        branch: output?.data?.branch || null,
        city: output?.data?.city || null,
        accountStatus: output?.data?.account_status || null,
        accountStatusCode: output?.data?.account_status_code || null,
        isVerified:
          output?.data?.account_status_code === "ACCOUNT_IS_VALID",
      };

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

payout.GetPayoutRecharge = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "GetPayoutRecharge";
  try {
    const { startDate, endDate } = req.query;
    const userType = req.userType;

    log.info(`[${C}], [${F}], StartDate [${startDate}], End Date [${endDate}]`);

    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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting.....`
      );
    }

    let list: any = [];

    list = await payoutTransactionDB.getPayoutRechargeWithDate({
      clientId,
      startDate,
      endDate,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], list sent successfully`
    );

    return res.status(200).json({
      msg: "List sent successfully",
      data: list || [],
      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,
    });
  }
};


payout.InitiateTenantRefund = async (req: CustomRequest, res: Response) => {
  const C = "Payout Controller";
  const F = "InitiateTenantRefund";

  try {
    let { tenantId, amount, method, name, mobile, accountName, accountNumber, ifsc, remarks = null, currentBalance = 0.00, deductChargesFromVendor = 0, propId = null } = req.body;

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Method [${method}], Current Balance [${currentBalance}], Deduct Charges [${deductChargesFromVendor}], Prop Id [${propId}], Remarks [${remarks}] Refund transfer Req`
    );

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

    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"
        }....`
      );
      if (isPartner) {
        const staff = await staffDB.getById({ id: req.id });
        if (staff) {
          recordedBy = staff.name;
        }
      }
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      recordedBy = staff.name;
      clientId = staff?.clientId;

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

    let tenant = await tenantDB.getById({ id: tenantId });
    if (!tenant) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No tenant found`
      );

      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }
    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      const client = await clientDB.getById({ id: clientId });
      recordedBy = client?.name || "";
    } else if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      recordedBy = staff?.name || "";
    }
    let beneId = 0;
    let beneficiaryId: string;
    let isAccountAlreadyExist = await payoutBeneficiaryDB.getByAccountNumberIfscForTenant({
      clientId,
      accountNum: accountNumber,
      ifsc
    });
    // if (isAccountAlreadyExist) {
    //   log.info(`[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Account already exist for tenant`);
    //   beneId = isAccountAlreadyExist?.id;
    //   beneficiaryId = isAccountAlreadyExist?.cfBeneficiaryId;
    // } else {
      const firstName = name.trim().split(" ")[0];
      //let beneficiaryId: string;
      let response = await cashfreePayout.fetchBeneficiary(C, F, clientId, accountNumber, ifsc);

      if (response?.isSuccess === false) {
        beneficiaryId = await generateBeneficiaryId({ userName: firstName });
        log.info(`[${C}], [${F}], Client Id [${clientId}], Beneficiary Id [${beneficiaryId}]`);

        response = await cashfreePayout.addBeneficiary(
          C,
          F,
          clientId,
          accountName,
          beneficiaryId,
          accountNumber,
          ifsc,
          null,
          1,
          mobile
        );

        if (response?.isSuccess === false) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Resp [${JSON.stringify(response.error)}], Failed to add beneficiary`
          );
          return res.status(400).json({
            msg: response?.error?.message || "Fail to add beneficiary",
            isSuccess: false,
          });
        }
      } else {
        beneficiaryId = response?.data?.beneficiary_id;
        accountName = response?.data?.beneficiary_name;
        log.info(`[${C}], [${F}], Client Id [${clientId}], Fetched Beneficiary Id [${beneficiaryId}], Fetched Beneficiary Name [${accountName}]`);
      }
      let beneficiaryStatus = response?.data?.beneficiary_status;
      let status = CONSTANTS.BENEFICIARY_STATUS.INITIATED;
      if (beneficiaryStatus === "VERIFIED") {
        status = CONSTANTS.BENEFICIARY_STATUS.HIDDEN;
        beneId = await payoutBeneficiaryDB.add({
          clientId,
          userId: tenant?.id,
          userType: CONSTANTS.USER_TYPE.TENANT,
          bankName: null,
          name,
          holderName: accountName,
          type: 1,
          accountNum: accountNumber,
          ifsc: ifsc,
          upiId: null,
          cfBeneficiaryId: beneficiaryId,
          status,
          mobile,
        });
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Bene Status [${beneficiaryStatus}], Response [${JSON.stringify(response)}], Failed to add beneficary`
        );
        return res.status(400).json({
          msg: "Failed to add beneficiary",
          isSuccess: false,
        });
      }
    //}

    //Transfer Start
    let transferId = await generatePayOutTransId();
    let transferMode = "neft";
    if (CONSTANTS.PAYOUT_MODE.IMPS === method) {
      transferMode = "imps";
    } else if (CONSTANTS.PAYOUT_MODE.RTGS === method) {
      transferMode = "rtgs";
    }
    
    //amount = 1;
    let transferAmount = Number(amount);
    let charges = 0;
    if (Number(deductChargesFromVendor) === 1) {
      charges = CHARGE_IN_PAISE / 100; // 3.90
      if (Number(amount) < charges) {
        return res.status(400).json({
          msg: "Amount should be greater than ₹3.90",
          isSuccess: false,
        });
      }
      const transferAmountInPaise = Math.max(0, Math.round(Number(amount) * 100) - CHARGE_IN_PAISE);

      transferAmount = transferAmountInPaise / 100;
    }

    log.info(
      `[${C}], [${F}], Original Amount [${amount}], Transfer Amount [${transferAmount}], Deduct Charges [${deductChargesFromVendor}]`
    );

    let payoutResp: any;
    payoutResp = await cashfreePayout.payoutToBeneficiary(C, F, clientId, transferId, transferAmount, beneficiaryId, transferMode, remarks);

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Transfer Resp [${JSON.stringify(payoutResp)}]`
    );

    if (payoutResp?.data?.status) {
      let status = CONSTANTS.TRANSFER_STATUS.INITIATED;
      if ("FAILED" === payoutResp?.data?.status.toUpperCase())
        status = CONSTANTS.TRANSFER_STATUS.FAILED;
      else if ("REJECTED" === payoutResp?.data?.status.toUpperCase()) {
        status = CONSTANTS.TRANSFER_STATUS.REJECTED;
      }
      const transId = await payoutTransactionDB.add({
        clientId,
        transId: transferId,
        cfTransferId: payoutResp?.data?.cf_transfer_id,
        amount: amount,
        vendorChargeDeducted: 0,
        charges: null,
        mode: method,
        payoutBeneficiaryId: beneId,
        cfBeneficiaryId: beneficiaryId,
        expenseId: null,
        expenseType: null,
        status,
        statusDescription: payoutResp?.data?.status_description,
        holderName: accountName,
        accountNum: accountNumber,
        ifsc: ifsc,
        remark: remarks,
        doneById: Number(req.id),
        doneByType: userType,
        beneficiaryMobile: mobile ? mobile : null,
        currentBalance: Number(currentBalance) || 0.00,
      });

      await recordTransferInitiate({
          clientId: clientId,
          userType: Number(userType),
          staffId: Number(userType) === CONSTANTS.USER_TYPE.STAFF ? Number(req.id) : null,
          transferAmount: Number(Number(amount).toFixed(2))
      });
      await payoutTransactionDB.updateIsWalletAdjusted({
        id: transId,
        isWalletAdjusted: 1,
      });

      const moveOutOccupancies = await moveOutDB.getAllByTenantIdAndClientIdPropId({
        tenantId,
        clientId,
        propId,
      });
      for (let occupancy of moveOutOccupancies) {
        await moveOutDB.updateRefundStatus({
          id: occupancy.id,
          refundStatus: CONSTANTS.REFUND_STATUS.INITIATED,
        });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Method [${method}], Amount [${amount}], Refund initiated successfully`
      );

      return res.status(200).json({
        msg: "Refund initiated successfully",
        isSuccess: true,
      });
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], B Id [${beneId}], Beneficiary Id [${beneficiaryId}], Transfer Failed`
      );
      return res.status(400).json({
        msg: payoutResp?.error?.message || CONSTANTS.MSG.ERROR_MESSAGE,
        isSuccess: false,
      });

    }

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

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

export default payout;
