import { Response } from "express";
import jwt from "jsonwebtoken";
import moment from "moment";
import CONSTANTS from "../config/constants";
import log from "../config/log";
import bedDB from "../models/beds.model";
import clientDB from "../models/client.model";
import documentDB from "../models/document.model";
import duesDB from "../models/dues.model";
import moveOutDB from "../models/moveOut.model";
import occupancyDB from "../models/occupancy.model";
import otpDB from "../models/otp.model";
import propertyDB from "../models/property.model";
import requestDB from "../models/request.model";
import roomDB from "../models/room.model";
import staffDB from "../models/staff.model";
import tenantDB from "../models/tenant.model";
import transactionDB from "../models/transaction.model";
import CustomRequest from "../types/requestType";
import generateTransId from "../utils/generateTransId";
import sendSMS from "../utils/sendSMS";
import getDueName from "../utils/getDueName";
import complaintDB from "../models/complaint.model";
import moveInDB from "../models/moveIn.model";
import notificationDB from "../models/notification.model";
import imageDB from "../models/images.model";
import getModeName from "../utils/getModeName";
import {
  clientDashboard,
  staffAdminDashboard,
  staffDashboard,
  //DashboardDetails,
  getStaffAdminDashboardByYearMonth,
  clientIncomeDetails,
} from "../utils/client/getDashboardData";
import {
  clientCollectionGraph,
  clientOccupancyGraph,
  clientExpenseGraph,
  clientComplaintGraph,
  clientRentCollectedExpectedGraph,
  clientOccupancyGraphFY,
} from "../utils/client/getGraphData";
import requestsTypes from "../schemas/request.schema";
import propertyStaffTypes from "../schemas/propertyStaff.schema";
import propertiesTypes from "../schemas/property.schema";
import rentAgreementRecordDB from "../models/rentAgreementRecord.model";
import fsPromises from "fs/promises";
import fs from "fs";
import {
  sendWhatsappOwnerWelcome,
  sendWhatsappTenantDues,
  sendWhatsappTenantSingleDue,
  sendWhatsappOtp,
  sendWhatsappTenantRentAgreementRenew,
  sendWhatsappTenantMoveOutInitiated,
  sendWhatsappRequestUpdates,
  sendWhatsappRequestRejectionWithReason,
  sendWhatsappEvictionNotify,
} from "../utils/sendWhatsappWithConfig";
import notificationPrefDB from "../models/notificationPref.model";
import {
  clientDailyPendingRent,
  clientDailyPendingRentForMonth,
  clientMonthlyPendingRent,
  staffDailyPendingRent,
  staffDailyPendingRentForMonth,
  staffMonthlyPendingRent,
} from "../utils/client/rentPendingStats";
import generateLedgerReferenceId from "../utils/generateLedgerReferenceId";
import ledgerDB from "../models/ledger.model";
import getDueDescription from "../utils/getDueDescription";
//import { request } from "http";
import { isUserFinanceAdmin, isUserPartner, staffAttendanceModulePermission } from "../utils/isUserPartner";
import moveOutDuesDB from "../models/moveOutDues.model";
import staffAttendanceDB from "../models/staffAttendance.model";
//mport { createPaymentLink } from "../utils/client/payu";
import settingsDB from "../models/settings.model";
//import paymentLinkDB from "../models/paymentLinks.model";
//import generateTenantGId from "../utils/generateTenantGId";
import createReceiptMultipleDues from "../utils/createReceiptMultipleDues";
import flatDB from "../models/flat.model";
import eqaroTenantsDB from "../models/eqaroTenants.model";
import getDocumentTitle from "../utils/getDocumentTitle";
import expenseDB from "../models/expense.model";
import occupancyReportDB from "../models/occupancyReport.model";
import years from "../utils/client/getYearsArray";
import { isPrivilegedStaff } from "../utils/isPrivilegedStaff";
import { exec } from "child_process";
import bankDB from "../models/bank.model";
import landlordDocumentDB from "../models/landlordDocuments.model";
import vendorDB from "../models/vendor.model";
import clientConfigDB from "../models/clientConfig.model";
//import { Configuration } from "cashfree-pg";
import { AddDues } from "../utils/dueHandler";
//import axios from "axios";
import { logActivity, logTenantAgreementRenewActivity, logTenantEvictionActivity, logTenantRequestActivity } from "../utils/logActivity";
import extraChargeDB from "../models/extraCharges.model";
import tenantGuestsDB from "../models/tenantGuests.model";
import getRequestName from "../utils/getRequestName";
import hiddenDuesDB from "../models/hiddenDues.model";
import clientLandlordDB from "../models/clientLandlord.model";
import landlordDB from "../models/landlord.model";
import landlordBankAccountsDB from "../models/landlordBankAccounts.model";
import parcelDB from "../models/parcel.model";
import tenantAttendanceDB from "../models/tenantAttendance.model";
//import leadDB from "../models/lead.model";
import ClientPendingTasks, { StaffPendingTasks } from "../utils/client/getPendingTasks";
import propertyLeaseDB from "../models/propertyLease.model";
import locationDB from "../models/location.model";
import sendWhatsapp from "../utils/sendWhatsappChatMitra";
import argeementRenewConfigDB from "../models/argeementRenewConfig.model";
import { canToggleOnlinePayment } from "../utils/canToggleOnlinePayment";
import evictionConfigDB from "../models/evictionConfig.model";
import tenantBankDB from "../models/tenantBank.model";
import { checkTallyOnlineStatus, setMoveOutDueTallyStatus, syncDuesTallyStatus, syncExpenseJournalTally, syncExpensePaymentTally, syncOccupancyTallyStatus, syncPropertyIncomeSecurityLedgerTally, syncTransactionTallyStatus } from "../utils/setTallyStatus";
import tallyConfigDB from "../models/tallyConfig.model";
import checkPermission from "../utils/checkPermission";
import stampPaperDB from "../models/stampPapers.model";
import foodDB from "../models/food.model";
import tenantMovementDB from "../models/tenantMovement.model";
import { generateRentReciept } from "../utils/generateRentReciept";
import laundryRequestsDB from "../models/laundryRequests.model";
import cashfreePayout from "../utils/cashfreePayout";
import addTransactions from "../utils/addTransaction";
import { generateClientGId } from "../utils/generateOccupancyGId";
import { sendWhatsappMC } from "../utils/sendWhatsappMessageCentral";
import { isHalfDay } from "../utils/helpers";
import { getWalletBalanceAndLimits } from "../utils/walletHelper";
import walletDB from "../models/wallet.model";
import subscriptionDB from "../models/subscription.model";
// import payoutBeneficiaryDB from "../models/payoutBeneficiary.model";
// import tallyConfigDB from "../models/tallyConfig.model";

const clients: any = {};

clients.Login = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "login";

  try {
    const { mobile, platform = CONSTANTS.TENANT_DEVICE_TYPE.ANDROID } =
      req.body;
    log.info(`[${C}], [${F}], Mobile [${mobile}], Platform [${platform}]`);

    let userType = CONSTANTS.USER_TYPE.CLIENT;
    //For Whatsapp OTP
    let sendToName = "";
    const isExists = await clientDB.getByMobile({ mobile });

    if (isExists) {
      sendToName = isExists?.name || "";
      log.info(`[${C}], [${F}], Mobile [${mobile}], Client Found`);
      if (platform === CONSTANTS.TENANT_DEVICE_TYPE.WEB) {
        const properties = await propertyDB.getAllByClientId({
          clientId: isExists?.id,
        });
        // if (!properties) {
        //   return res.status(400).json({
        //     msg: "No properties added yet. Please add properties using Kipinn App before logging into web.",
        //     isSuccess: false,
        //   });
        // }  
        if (isExists?.status != CONSTANTS.CLIENT_STATUS.ENROLLED) {
          log.info(`[${C}], [${F}], Client Id [${isExists?.id}], Client not enrolled`);
          return res.status(400).json({
            msg: "You are not authorized to login via web. Please login via Kipinn App",
            isSuccess: false,
          });
        }
      }
    } else {
      const isStaffExists = await staffDB.getByMobile({ mobile });
      const isLandlordExists = await landlordDB.getByMobile({ mobile });

      if (isStaffExists) {
        sendToName = isStaffExists?.name || "";
        log.info(`[${C}], [${F}], Mobile [${mobile}], Staff Found`);
        let isStaffAllowed = await isPrivilegedStaff(isStaffExists.role, 3);
        if (isStaffExists.status === CONSTANTS.STAFF_STATUS.INACTIVE) {
          return res.status(400).json({
            msg: "Your account has been deactivated by your owner",
            isSuccess: true,
          });
        } else if (isStaffExists.role === CONSTANTS.STAFF_ROLES.BROKER) {
          log.info(`[${C}], [${F}], Staff Id [${isStaffExists.id}], Staff Role [${isStaffExists.role}], Broker Not Allowed To Login`);
          return res.status(400).json({
            msg: "You are not authorized to login",
            isSuccess: false,
          });
        } else if (
          platform === CONSTANTS.TENANT_DEVICE_TYPE.WEB && !isStaffAllowed
          // isStaffExists.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
          // isStaffExists.role !== CONSTANTS.STAFF_ROLES.PARTNER &&
          // isStaffExists.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE
        ) {
          return res.status(400).json({
            msg: "You are not authorized to login via web. Please login via Kipinn App",
            isSuccess: false,
          });
        } else if (platform === CONSTANTS.TENANT_DEVICE_TYPE.WEB) {
          const properties = await propertyDB.getAllByClientId({
            clientId: isStaffExists?.clientId,
          });
          if (!properties) {
            return res.status(400).json({
              msg: "No properties added yet. Please add properties using Kipinn App before logging into web.",
              isSuccess: false,
            });
          }
        }
        userType = CONSTANTS.USER_TYPE.STAFF;
      } else if (isLandlordExists) {
        sendToName = isLandlordExists?.name || "";
        log.info(`[${C}], [${F}], Mobile [${mobile}], Landlord Found`);
        userType = CONSTANTS.USER_TYPE.LANDLORD;
      } else if (platform === CONSTANTS.TENANT_DEVICE_TYPE.WEB) {
        log.info(
          `[${C}], [${F}], Mobile [${mobile}], Client Not Registered Via App`
        );
        return res.status(400).json({
          msg: "You are not a registered user. Please register via Kipinn App first.",
          isSuccess: false,
        });
      } else {
        let clientId = await clientDB.create({ mobile });

        await clientConfigDB.create({ clientId, provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.APP, type: CONSTANTS.CLIENT_CONFIG_TYPE.ANDROID, value: CONSTANTS.DEFAULT_APP_LINK.ANDROID });
        await clientConfigDB.create({ clientId, provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.APP, type: CONSTANTS.CLIENT_CONFIG_TYPE.IOS, value: CONSTANTS.DEFAULT_APP_LINK.IOS });
        await clientConfigDB.create({ clientId, provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.WATEMPLATE, type: CONSTANTS.CLIENT_CONFIG_TYPE.WAONBOARDING, value: CONSTANTS.DEFAULT_APP_LINK.TEMPLATE });
        await clientConfigDB.create({ clientId, provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.WATEMPLATE, type: CONSTANTS.CLIENT_CONFIG_TYPE.FOOTER, value: CONSTANTS.DEFAULT_APP_LINK.FOOTER });

        let gId = await generateClientGId(Number(clientId));
        clientDB.updateGId({ id: clientId, gId: gId });

        log.info(
          `[${C}], [${F}], Mobile [${mobile}], Client successfully created`
        );
      }
    }
    let otp = Math.floor(Math.random() * (999999 - 100000) + 100000).toString();

    if ("7999999999" == mobile) otp = "111111";
    if (mobile === "9599054423") otp = "535312";
    if (process.env.ENABLE_RANDOM_OTP === "false") {
      otp = "111111";
    }

    await otpDB.create({ mobile, otp });

    const msg = `${otp} ${CONSTANTS.MSG.OTP}`;

    const isSent = await sendSMS(mobile, msg, CONSTANTS.SMS_TEMPLATE_IDS.OTP);
    log.info(
      `[${C}], [${F}], Mobile [${mobile}], OTP [${otp}], ${isSent ? "OTP sent successfully" : "Failed to send OTP SMS"
      }`
    );
    sendWhatsappOtp(mobile, otp, sendToName);

    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      await clientDB.updateClientDevice({
        mobile,
        device: platform,
      });
    } else {
      await staffDB.updateStaffDevice({
        mobile,
        device: platform,
      });
    }

    return res
      .status(200)
      .json({ msg: "OTP has been sent 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,
    });
  }
};

clients.InternalLogin = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "InternalLogin";

  try {
    const { mobile, platform = CONSTANTS.TENANT_DEVICE_TYPE.WEB } = req.body;
    log.info(`[${C}], [${F}], Mobile [${mobile}], Platform [${platform}]`);

    const userType = CONSTANTS.USER_TYPE.CLIENT;

    const isExists = await clientDB.getByMobile({ mobile });

    if (isExists) {
      log.info(`[${C}], [${F}], Mobile [${mobile}], Client Found`);
    } else {
      const isStaffExists = await staffDB.getByMobile({ mobile });
      const isLandlordExists = await landlordDB.getByMobile({ mobile });

      if (isStaffExists) {
        log.info(`[${C}], [${F}], Mobile [${mobile}], Staff Found`);

        if (isStaffExists.status === CONSTANTS.STAFF_STATUS.INACTIVE) {
          return res.status(400).json({
            msg: "Your account has been deactivated by your owner",
            isSuccess: true,
          });
        }
      } else if (isLandlordExists) {
        log.info(`[${C}], [${F}], Mobile [${mobile}], Landlord Found`);
      } else {
        let clientId = await clientDB.create({ mobile });
        await clientConfigDB.create({ clientId, provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.APP, type: CONSTANTS.CLIENT_CONFIG_TYPE.ANDROID, value: CONSTANTS.DEFAULT_APP_LINK.ANDROID });
        await clientConfigDB.create({ clientId, provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.APP, type: CONSTANTS.CLIENT_CONFIG_TYPE.IOS, value: CONSTANTS.DEFAULT_APP_LINK.IOS });
        await clientConfigDB.create({ clientId, provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.WATEMPLATE, type: CONSTANTS.CLIENT_CONFIG_TYPE.WAONBOARDING, value: CONSTANTS.DEFAULT_APP_LINK.TEMPLATE });
        await clientConfigDB.create({ clientId, provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.WATEMPLATE, type: CONSTANTS.CLIENT_CONFIG_TYPE.FOOTER, value: CONSTANTS.DEFAULT_APP_LINK.FOOTER });

        log.info(
          `[${C}], [${F}], Mobile [${mobile}], Client successfully created`
        );
        // log.info(`[${C}], [${F}], Mobile [${mobile}], Client Not Registered Via App`);
        // return res.status(400).json({
        //   msg: "Your are not a registered user. Please register via Kipinn App first.",
        //   isSuccess: false,
        // });
      }
    }
    let otp = mobile.slice(-6);

    await otpDB.create({ mobile, otp });

    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      await clientDB.updateClientDevice({
        mobile,
        device: platform,
      });
    } else {
      await staffDB.updateStaffDevice({
        mobile,
        device: platform,
      });
    }

    return res.status(200).json({
      msg: "OTP has been sent 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,
    });
  }
};

clients.OTPVerify = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "OTPVerify";

  try {
    const { otp, mobile } = req.body;
    log.info(`[${C}], [${F}], Mobile [${mobile}], OTP [${otp}]`);

    let personalAndPartnerProperties = [];
    let staffAccounts = [];

    const record = await otpDB.getByMobile({ mobile });

    if (!record) {
      log.info(
        `[${C}], [${F}], Mobile [${mobile}], OTP [${otp}] OTP has been expired`
      );

      return res.status(400).json({
        msg: "OTP has been expired",
        isSuccess: false,
      });
    }

    if (record?.otp !== otp) {
      log.info(
        `[${C}], [${F}], Mobile [${mobile}], OTP [${otp}], DB OTP [${record?.otp}], Incorrect OTP`
      );

      return res
        .status(400)
        .json({ msg: "Please enter a valid OTP", isSuccess: false });
    }

    log.info(`[${C}], [${F}], Mobile [${mobile}], OTP [${otp}], OTP Matched`);

    let user = null;
    let totalProperties = 0;
    let token = null;

    const client = await clientDB.getByMobile({ mobile });
    if (client) {
      log.info(
        `[${C}], [${F}], Mobile [${mobile}], OTP [${otp}], Client Found`
      );
      delete client.propertyCount;

      await otpDB.updateStatus({
        otp,
        status: CONSTANTS.OTP_STATUS.VERIFIED,
        mobile,
      });

      if (client.status === CONSTANTS.CLIENT_STATUS.UNVERIFIED) {
        await clientDB.updateStatus({
          id: client?.id,
          status: CONSTANTS.CLIENT_STATUS.VERIFIED,
        });
      }

      if (
        client.status !== CONSTANTS.CLIENT_STATUS.UNVERIFIED &&
        client.status !== CONSTANTS.CLIENT_STATUS.VERIFIED
      ) {
        const propertiesCount = await propertyDB.getCount({
          clientId: client?.id,
        });
        totalProperties = propertiesCount.count;
      }

      personalAndPartnerProperties = await propertyDB.getPersonalAndPartnerProperties({
        clientId: client?.id,
      });

      token = jwt.sign(
        {
          id: client?.id,
          type: CONSTANTS.USER_TYPE.CLIENT,
          platform: client?.device,
        },
        process.env.TOKEN_SECRET!,
        { expiresIn: process.env.TOKEN_EXPIRY }
      );

      user = await clientDB.getByMobile({ mobile });
      user = { ...user, userType: CONSTANTS.USER_TYPE.CLIENT, userRole: 0 };
    } else {
      const staff = await staffDB.getByMobile({ mobile });
      // if (!staff) {
      //   log.info(
      //     `[${C}], [${F}], Mobile [${mobile}], OTP [${otp}], No Client Nor Staff Found`
      //   );
      //   return res.status(400).json({
      //     msg: "Please enter a valid mobile number",
      //     isSuccess: false,
      //   });
      // }

      if (staff) {
        log.info(`[${C}], [${F}], Mobile [${mobile}], OTP [${otp}], Staff Found`);

        if (staff?.role === CONSTANTS.STAFF_ROLES.PARTNER) {
          personalAndPartnerProperties = await propertyDB.getPersonalAndPartnerProperties({
            clientId: staff?.clientId,
          });
        }

        staffAccounts = await staffDB.getStaffAccounts({
          mobile: mobile,
        });

        token = jwt.sign(
          {
            id: staff?.id,
            type: CONSTANTS.USER_TYPE.STAFF,
            role: staff?.role,
            platform: staff?.device,
            clientId: staffAccounts ? staffAccounts[0].clientId : staff.clientId,
          },
          process.env.TOKEN_SECRET!,
          { expiresIn: process.env.TOKEN_EXPIRY }
        );

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

        user = {
          ...staff,
          userType: CONSTANTS.USER_TYPE.STAFF,
          ownerMobile: client?.mobile || "",
          ownerName: client?.name || "",
          userRole: staff?.role || 0,
        };
      } else {
        const landlord = await landlordDB.getByMobile({ mobile });

        if (!landlord) {
          log.info(
            `[${C}], [${F}], Mobile [${mobile}], OTP [${otp}], No Client, Staff Or Landlord Found`
          );
          return res.status(400).json({
            msg: "Please enter a valid mobile number",
            isSuccess: false,
          });
        }

        token = jwt.sign(
          {
            id: landlord?.id,
            type: CONSTANTS.USER_TYPE.LANDLORD,
          },
          process.env.TOKEN_SECRET!,
          { expiresIn: process.env.TOKEN_EXPIRY }
        );

        user = {
          ...landlord,
          userType: CONSTANTS.USER_TYPE.LANDLORD,
        };
      }
    }

    return res.status(200).json({
      msg: "Mobile has been verified successfully",
      isSuccess: true,
      totalProperties,
      user,
      token,
      personalAndPartnerProperties: personalAndPartnerProperties || [],
      staffAccounts: staffAccounts || [],
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

clients.LoginWithPartnerOrPersonalProp = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "LoginWithPartnerOrPersonalProp";

  try {
    const { clientId, parentId } = req.body;
    log.info(`[${C}], [${F}], Client Id [${clientId}], Parent Id [${parentId}], Platform [${req?.platform}]`);

    let user = null;
    let totalProperties = 0;
    let token = null;

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], No Client Found`
      );

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

    const parentClient = await clientDB.getById({ id: parentId });
    if (!parentClient) {
      log.info(
        `[${C}], [${F}], Parent Client Id [${clientId}], No Parent Client Found`
      );

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

    if (
      client.status !== CONSTANTS.CLIENT_STATUS.UNVERIFIED &&
      client.status !== CONSTANTS.CLIENT_STATUS.VERIFIED
    ) {
      const propertiesCount = await propertyDB.getCount({
        clientId: client?.id,
      });
      totalProperties = propertiesCount.count;
    }

    token = jwt.sign(
      {
        id: client?.id,
        type: CONSTANTS.USER_TYPE.CLIENT,
        platform: Number(client?.device) === 0 ? CONSTANTS.TENANT_DEVICE_TYPE.WEB : req?.platform,
        parentClientId: parentClient?.id,
        switchingClient: req.switchingClient ? req.switchingClient : client?.id,
      },
      process.env.TOKEN_SECRET!,
      { expiresIn: process.env.TOKEN_EXPIRY }
    );

    user = await clientDB.getById({ id: clientId });
    user = { ...user, userType: CONSTANTS.USER_TYPE.CLIENT, userRole: 0 };

    let personalAndPartnerProperties = await propertyDB.getPersonalAndPartnerProperties({
      clientId: parentClient?.id,
    });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Parent Client Id [${parentId}], Client Switch Successfully`);

    return res.status(200).json({
      msg: "Mobile has been verified successfully",
      isSuccess: true,
      totalProperties,
      user,
      token,
      personalAndPartnerProperties: personalAndPartnerProperties || [],
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

clients.StaffLoginWithParticularClient = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "StaffLoginWithParticularClient";

  try {
    const { switchingId } = req.body;
    log.info(`[${C}], [${F}], Switching Id [${switchingId}], Platform [${req?.platform}]`);

    let user = null;
    let token = null;

    const staff = await staffDB.getById({ id: switchingId });
    if (!staff) {
      log.info(
        `[${C}], [${F}], Staff Id [${switchingId}], No Staff Found With Id`
      );

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

    token = jwt.sign(
      {
        id: staff?.id,
        type: CONSTANTS.USER_TYPE.STAFF,
        role: staff?.role,
        platform: staff?.device,
        clientId: staff.clientId,
      },
      process.env.TOKEN_SECRET!,
      { expiresIn: process.env.TOKEN_EXPIRY }
    );

    user = await staffDB.getById({ id: switchingId });
    user = { ...user, userType: CONSTANTS.USER_TYPE.STAFF, userRole: staff.role };

    const staffAccounts = await staffDB.getStaffAccounts({
      mobile: staff.mobile,
    });

    log.info(`[${C}], [${F}], Client Id [${staff.clientId}], New Staff Id [${switchingId}], Staff Switch Successfully`);

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

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

clients.OnBoarding = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "OnBoarding";

  try {
    const { name, city, propertyCount } = req.body;
    const clientId = req.id;
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Name [${name}], City [${city}], Property Count [${propertyCount}]`
    );

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

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

    await clientDB.updateProfile({ name, city, propertyCount, id: clientId });
    await clientDB.updateStatus({
      id: client?.id,
      status: CONSTANTS.CLIENT_STATUS.ACTIVE,
    });

    await locationDB.addLocation({
      clientId,
      name: city,
      description: null,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Name [${name}], City [${city}], Client Profile Updated`
    );

    // notificationPrefDB.add({
    //   clientId,
    //   notificationType: CONSTANTS.NOTIFICATION_TYPE.SMS,
    //   isEnabled: 1,
    //   isSelfBranding: 0,
    // });
    // notificationPrefDB.add({
    //   clientId,
    //   notificationType: CONSTANTS.NOTIFICATION_TYPE.WHATSAPP,
    //   isEnabled: 1,
    //   isSelfBranding: 0,
    // });

    sendWhatsappOwnerWelcome(client.mobile, name);

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

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

clients.Dashboard = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "Dashboard";

  try {
    const userType = req.userType;
    let { y, m, v=21 } = req.query;

    let dueTenantCount = 0;
    let totalIncome = 0;
    let totalDues = 0;
    let expectedRent = null;
    let expectedExtraCharges = null;
    let expectedSecurity = null;
    let incomeData: any = null;
    let year = y;
    let month = m;
    let movingInCurrentMonthCount = 0;
    let movingInNextMonthCount = 0;
    let movingInNextToNextMonthCount = 0;
    let movingOutCurrentMonthCount = 0;
    let movingOutNextMonthCount = 0;
    let currentMonthAmount = 0;
    let nextMonthAmount = 0;
    let income = 0;
    let expense = 0;
    let netProfitLoss = 0;
    let vacancyLoss = 0;
    let curMonthMoveIn = { count: 0, totalRent: 0 };
    let curMonthMoveOut = { count: 0, totalRent: 0 };;
    let lastMonthMoveIn = { count: 0, totalRent: 0 };;
    let lastMonthMoveOut = { count: 0, totalRent: 0 };;
    let nextMonthMoveIn = { count: 0, totalRent: 0 };;
    let nextMonthMoveOut = { count: 0, totalRent: 0 };;
    let vacancyLossFullMonth = 0;
    let occupiedPercentage = 0;
    let lastMonthOccupancyPercent = 0;
    let totalLandlordRentToBePaid = 0;
    let rentPending = 0;
    let curMonthRentPaid = 0;
    let isPayoutEnabled = 0;
    let staffAccounts = [];
    let staffList = [];
    let propList = [];

    if (!year || !month || "undefined" == month) {
      year = moment().format("YYYY");
      month = moment().format("M");
    }

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

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

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

    let data = {} as any;
    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner || isFinanceAdmin) {
      // const clientId = req.id || "";
      log.info(`[${C}], [${F}], App Version [${v}], ${isFinanceAdmin ? `Finance Admin Id [${req.id}]` : isPartner ? `Partner Id [${req.id}], Partner` : `Client Id [${clientId}]`
        } Requesting....`);

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

      if (!client) {
        log.info(`[${C}], [${F}], Client Id [${clientId}], No Client Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      let staffStatus = 0;
      let staffPermission = 1;
      if (isPartner || isFinanceAdmin) {
        const staff = await staffDB.getById({ id: req.id });

        if (!staff) {
          log.info(
            `[${C}], [${F}], Staff Id [${req.id}], No Staff Found For Partner Or Finance Admin`
          );
          return res.status(400).json({
            msg: CONSTANTS.MSG.INVALID_REQUEST,
            isSuccess: false,
          });
        }
        staffStatus = staff?.status;
        staffAccounts = await staffDB.getStaffAccounts({
          mobile: staff.mobile,
        });

        staffPermission = staff.permissions;

        if (Number(staff?.currentAppVersion) !== Number(v) || staff?.currentAppVersion === null) await staffDB.updateAppVersion({ id: req.id, currentAppVersion: v });
      }

      data = await clientDashboard(client);
      if (isFinanceAdmin) {
        data.staffStatus = staffStatus;
        delete data.clientStatus;
        data.permissions = staffPermission;
      }
      if (isPartner || isFinanceAdmin) {
        await staffDB.updateLastLogin({ id: req.id });
      } else {
        await clientDB.updateLastLogin({ id: clientId });
        if (Number(client?.currentAppVersion) !== Number(v) || client?.currentAppVersion === null) await clientDB.updateAppVersion({ id: clientId, currentAppVersion: v });
      }
      data.tenantAttendanceEnabled = client?.tenantAttendanceEnabled || 0;
      data.defaultTenantFilter = client?.defaultTenantFilter || '';

      incomeData = await clientIncomeDetails(client, year, month);
      if (incomeData) {
        // todaysCollection =
        //   data.todaysCollection == null ? 0 : data.todaysCollection;
        dueTenantCount = incomeData.dueTenantCount;
        totalIncome = incomeData.transactionStats[0]?.totalIncome || 0;
        totalDues = incomeData.duesStats?.duesForMonth || 0;
        vacancyLoss = incomeData.vacancyLoss || 0;
        vacancyLossFullMonth = incomeData.vacancyLossFullMonth || 0;
        income = incomeData.income || 0;
        expense = incomeData.expense || 0;
        netProfitLoss = incomeData.netProfitLoss || 0;
      }

      expectedRent = await occupancyDB.getExpectedRentForClient({
        clientId,
        month: month,
        year: year,
      });

      expectedExtraCharges = await occupancyDB.getExpectedExtraChargesForClient({
        clientId,
      });

      expectedSecurity = await occupancyDB.getExpectedSecurityForClient({
        clientId,
        month: month,
        year: year,
      });

      expectedSecurity = expectedSecurity || 0;

      const movingInCurrentMonth =
        await occupancyDB.getMovingInCurrentMonthCount({
          clientId: clientId,
        });

      const movingOutCurrentMonth =
        await occupancyDB.getMovingOutCurrentMonthCount({
          clientId: clientId,
        });

      const movingInNextMonth = await occupancyDB.getMovingInNextMonthCount({
        clientId: clientId,
      });

      const movingInNextToNextMonth =
        await occupancyDB.getMovingInNextToNextMonthCount({
          clientId: clientId,
        });

      const movingOutNextMonth = await occupancyDB.getMovingOutNextMonthCount({
        clientId: clientId,
      });

      movingInCurrentMonthCount = movingInCurrentMonth.count;
      movingInNextMonthCount = movingInNextMonth.count;
      movingInNextToNextMonthCount = movingInNextToNextMonth.count;
      movingOutCurrentMonthCount = movingOutCurrentMonth.count;
      movingOutNextMonthCount = movingOutNextMonth.count;
      currentMonthAmount =
        Number(movingInNextMonth.totalRent) -
        Number(movingOutCurrentMonth.totalRent);
      nextMonthAmount =
        Number(movingInNextToNextMonth.totalRent) -
        Number(movingOutNextMonth.totalRent);
      let pendingTasks = await ClientPendingTasks(clientId);
      data.pendingTasks = pendingTasks.totalPendingTasks;

      curMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYear({
        clientId,
        month: moment().format("MM"),
        year: moment().format("YYYY"),
      });
      nextMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYear({
        clientId,
        month: moment().add(1, "month").format("MM"),
        year: moment().add(1, "month").format("YYYY"),
      });
      lastMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYear({
        clientId,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });
      curMonthMoveOut = await occupancyDB.getMovingOutImpactCountByMonthYear({
        clientId,
        month: moment().format("MM"),
        year: moment().format("YYYY"),
      });
      lastMonthMoveOut = await occupancyDB.getMovingOutImpactCountByMonthYear({
        clientId,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });
      nextMonthMoveOut = await occupancyDB.getMovingOutImpactCountByMonthYear({
        clientId,
        month: moment().add(1, "month").format("MM"),
        year: moment().add(1, "month").format("YYYY"),
      });

      const lastMonthOccupancyData = await occupancyReportDB.getOccupancyReportByYearMonth({
        clientId,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });

      lastMonthOccupancyPercent = Number(((lastMonthOccupancyData.occupied / lastMonthOccupancyData.total) * 100).toFixed(2)) || 0;

      const beds = await bedDB.getCountsByClientId({ clientId });
      let bedCount = beds?.total || 0;
      let occupiedBeds = beds?.occupied || 0;
      occupiedPercentage = bedCount > 0 ? Number(((occupiedBeds / bedCount) * 100).toFixed(2)) : 0;

      curMonthRentPaid = await expenseDB.getTotalByClientIdAndDateRangeAndType({
        clientId,
        startDate: moment().startOf('month').format("YYYY-MM-DD"),
        endDate: moment().endOf('month').format("YYYY-MM-DD"),
        type: 1, //Building Rent
      });

      rentPending = await expenseDB.getTotalUnMarkedByClientIdAndTypeAndDateRange({
        clientId,
        startDate: moment().startOf('month').format("YYYY-MM-DD"),
        endDate: moment().endOf('month').format("YYYY-MM-DD"),
        type: 1, //Building Rent
      });
      totalLandlordRentToBePaid = await propertyLeaseDB.getTotalMonthlyRentByClientId({
        clientId,
      });

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

      propList = await propertyDB.getActivePropIdsByClientId({
        clientId,
        status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Dashboard data sent successfully`
      );
      const isPayoutEnabledConfig = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_PAYOUT_ENABLED,
      });
      if (isPayoutEnabledConfig && Number(isPayoutEnabledConfig.value)) {
        isPayoutEnabled = Number(isPayoutEnabledConfig.value);
      }
      if (isPartner || isFinanceAdmin) {
        const staffWallet = await walletDB.getByClientIdAndStaffId({
          clientId,
          staffId: req.id,
        });

        if (!staffWallet) {
          isPayoutEnabled = 0;
        }
      }
    } else {
      const staffId = req.id;
      log.info(`[${C}], [${F}], Staff Id [${staffId}], App Version [${v}], Staff Requesting....`);

      const staff = await staffDB.getById({ id: staffId });

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

      if (Number(staff?.currentAppVersion) !== Number(v) || staff?.currentAppVersion === null) await staffDB.updateAppVersion({ id: staffId, currentAppVersion: v });

      staffAccounts = await staffDB.getStaffAccounts({
        mobile: staff.mobile,
      });

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

      if (
        staff.role === CONSTANTS.STAFF_ROLES.ADMIN ||
        staff.role === CONSTANTS.STAFF_ROLES.WARDEN ||
        staff.role === CONSTANTS.STAFF_ROLES.BACK_OFFICE
      ) {
        data = await staffAdminDashboard(staff);
        if (staff.role === CONSTANTS.STAFF_ROLES.ADMIN || staff.role === CONSTANTS.STAFF_ROLES.WARDEN || staff.role === CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
          incomeData = await getStaffAdminDashboardByYearMonth(
            staff,
            year,
            month
          );
        }
        // log.info(
        //   `[${C}], [${F}], Staff Id [${staffId}], Staff Requesting... Admin`
        // );
      } else {
        data = await staffDashboard(staff);
        // log.info(
        //   `[${C}], [${F}], Staff Id [${staffId}], Staff Requesting... Normal`
        // );
      }

      await staffDB.updateLastLogin({ id: staffId });

      data.tenantAttendanceEnabled = client?.tenantAttendanceEnabled || 0;
      data.defaultTenantFilter = client?.defaultTenantFilter || '';

      if (incomeData) {
        // todaysCollection =
        //   data.todaysCollection == null ? 0 : data.todaysCollection;
        dueTenantCount = incomeData.dueTenantCount;
        totalIncome = incomeData.transactionStats[0]?.totalIncome || 0;
        totalDues =
          incomeData.duesStats?.totalDues ||
          incomeData.duesStats?.duesForMonth ||
          0;
        expectedRent = incomeData.expectedRent || 0;
        expectedSecurity = incomeData.expectedSecurity || 0;
        expectedExtraCharges = incomeData.expectedExtraCharges || 0;
        vacancyLoss = incomeData.vacancyLoss || 0;
        vacancyLossFullMonth = incomeData.vacancyLossFullMonth || 0;
        income = incomeData.income || 0;
        expense = incomeData.expense || 0;
        netProfitLoss = incomeData.netProfitLoss || 0;
      }

      const movingInCurrentMonth =
        await occupancyDB.getMovingInCurrentMonthCountForStaff({
          clientId: clientId,
          staffId: staffId,
        });

      const movingOutCurrentMonth =
        await occupancyDB.getMovingOutCurrentMonthCountForStaff({
          clientId: clientId,
          staffId: staffId,
        });

      const movingInNextMonth =
        await occupancyDB.getMovingInNextMonthCountForStaff({
          clientId: clientId,
          staffId: staffId,
        });

      const movingInNextToNextMonth =
        await occupancyDB.getMovingInNextToNextMonthCountForStaff({
          clientId: clientId,
          staffId: staffId,
        });

      const movingOutNextMonth =
        await occupancyDB.getMovingOutNextMonthCountForStaff({
          clientId: clientId,
          staffId: staffId,
        });

      movingInCurrentMonthCount = movingInCurrentMonth.count;
      movingInNextMonthCount = movingInNextMonth.count;
      movingInNextToNextMonthCount = movingInNextToNextMonth.count;
      movingOutCurrentMonthCount = movingOutCurrentMonth.count;
      movingOutNextMonthCount = movingOutNextMonth.count;
      currentMonthAmount =
        Number(movingInNextMonth.totalRent) -
        Number(movingOutCurrentMonth.totalRent);
      nextMonthAmount =
        Number(movingInNextToNextMonth.totalRent) -
        Number(movingOutNextMonth.totalRent);

      let pendingTasks = await StaffPendingTasks(clientId, Number(staffId));
      data.pendingTasks = pendingTasks.totalPendingTasks;

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

      if (staffLinkedProps) {
        propList = staffLinkedProps.map(({ id, name, status }: any) => ({ id, name, status }));
        const propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");

        curMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYearStaff({
          clientId,
          propertiesIds,
          month: moment().format("MM"),
          year: moment().format("YYYY"),
        });
        nextMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYearStaff({
          clientId,
          propertiesIds,
          month: moment().add(1, "month").format("MM"),
          year: moment().add(1, "month").format("YYYY"),
        });
        lastMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYearStaff({
          clientId,
          propertiesIds,
          month: moment().subtract(1, "month").format("MM"),
          year: moment().subtract(1, "month").format("YYYY"),
        });
        curMonthMoveOut = await occupancyDB.getMovingOutImpactCountByMonthYearStaff({
          clientId,
          propertiesIds,
          month: moment().format("MM"),
          year: moment().format("YYYY"),
        });
        nextMonthMoveOut = await occupancyDB.getMovingOutImpactCountByMonthYearStaff({
          clientId,
          propertiesIds,
          month: moment().add(1, "month").format("MM"),
          year: moment().add(1, "month").format("YYYY"),
        });
        lastMonthMoveOut = await occupancyDB.getMovingOutImpactCountByMonthYearStaff({
          clientId,
          propertiesIds,
          month: moment().subtract(1, "month").format("MM"),
          year: moment().subtract(1, "month").format("YYYY"),
        });

        const lastMonthOccupancyData = await occupancyReportDB.getOccupancyReportForStaffByYearMonth({
          clientId,
          propertiesIds,
          month: moment().subtract(1, "month").format("MM"),
          year: moment().subtract(1, "month").format("YYYY"),
        });

        lastMonthOccupancyPercent = Number(((lastMonthOccupancyData.occupied / lastMonthOccupancyData.total) * 100).toFixed(2)) || 0;

        const beds = await bedDB.getStatsForStaff({ clientId, propertiesIds: propertiesIds, });

        let bedCount = beds?.total || 0;
        let occupiedBeds = beds?.occupied || 0;
        occupiedPercentage = bedCount > 0 ? Number(((occupiedBeds / bedCount) * 100).toFixed(2)) : 0;

        curMonthRentPaid = await expenseDB.getTotalByClientIdAndDateRangeAndType({
          clientId,
          startDate: moment().startOf('month').format("YYYY-MM-DD"),
          endDate: moment().endOf('month').format("YYYY-MM-DD"),
          type: 1, //Building Rent
        });
        rentPending = await expenseDB.getTotalUnMarkedByClientIdAndTypeAndDateRange({
          clientId,
          startDate: moment().startOf('month').format("YYYY-MM-DD"),
          endDate: moment().endOf('month').format("YYYY-MM-DD"),
          type: 1, //Building Rent
        });
        totalLandlordRentToBePaid = await propertyLeaseDB.getTotalMonthlyRentByClientId({
          clientId,
        });
      }

      staffList = await staffDB.getAllExcludingRequester({
        clientId,
        id: staff.id,
        status: 1,
      });

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

      const staffWallet = await walletDB.getByClientIdAndStaffId({
        clientId,
        staffId,
      });

      if (!staffWallet) {
        isPayoutEnabled = 0;
      }

      log.info(
        `[${C}], [${F}], StaffId [${staffId}], Dashboard data sent successfully`
      );
    }

    let canHideDues = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.CAN_HIDE_DUES,
    });
    if (!canHideDues) {
      canHideDues = 0;
    } else {
      canHideDues = Number(canHideDues.value) || 0;
    }

    let totalExpected = Number(expectedRent);
    if (clientId === 168) {
      totalExpected = Number(expectedRent) + Number(expectedSecurity) + Number(expectedExtraCharges);
    }

    const incomeDataSummary = {
      //todaysCollection,
      totalIncome,
      totalDues,
      dueTenantCount,
      year,
      month,
      expectedRent: totalExpected || 0,
      movingInCurrentMonthCount,
      movingInNextMonthCount,
      movingInNextToNextMonthCount,
      movingOutCurrentMonthCount,
      movingOutNextMonthCount,
      currentMonthAmount,
      nextMonthAmount,
    };

    const currentVersion = CONSTANTS.CURRENT_VERSION;

    const bankAccounts = await bankDB.getByClientIdLimitedFields({ clientId });
    const vendorTypes = await vendorDB.getVendorTypes();

    const isManualMoveInEnabled = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_MANUAL_MOVEIN
    });

    let personalAndPartnerProperties = await propertyDB.getPersonalAndPartnerProperties({
      clientId: req.switchingClient,
    });

    if (userType !== CONSTANTS.USER_TYPE.CLIENT) {
      personalAndPartnerProperties = [];
    }

    const locationList = await locationDB.getByClientId({
      clientId,
    });

    let payoutBalance = 0;
    let payoutData: any = {
      walletBalance: 0,
      maxTransLimit: 0,
      dailyPayoutLimit: 0,
      dailyPayoutLimitRemaining: 0,
    }
    if (isPayoutEnabled === 1) {
      let balance = await cashfreePayout.getWalletBalance(C, F, clientId);
      if (!balance.error) {
        payoutBalance = Number(balance?.availableBalance);
      } else {
        payoutBalance = 0;
      }
      payoutData = await getWalletBalanceAndLimits({
        clientId,
        userType: Number(userType),
        staffId: userType === CONSTANTS.USER_TYPE.STAFF ? Number(req.id) : null,
      });
      payoutBalance = payoutData?.walletBalance || payoutBalance;
    }

    //CAN PUSH EKYC BULK
    let isEkycBulkEnabled = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EKYC_BULK_REMINDER,
    });
    if (!isEkycBulkEnabled) {
      isEkycBulkEnabled = 0;
    } else {
      isEkycBulkEnabled = Number(isEkycBulkEnabled.value) || 0;
    }

    const vendors = await vendorDB.getByClientId({
      clientId,
    });

    const subscription = await subscriptionDB.getByClientId({clientId});

    return res.status(200).json({
      isSuccess: true,
      data,
      vendors: vendors || [],
      subscription : subscription || [],
      isPayoutEnabled,
      payoutBalance,
      payoutData,
      incomeDataSummary,
      personalAndPartnerProperties: personalAndPartnerProperties || [],
      staffAccounts: staffAccounts || [],
      locationList: locationList || [],
      profitLossData: {
        income: income,
        expense: expense,
        netProfitLoss: netProfitLoss,
      },
      dueData: {
        expectedRent: totalExpected || 0,
        vacancyLoss: vacancyLoss || 0,
        totalDues: totalDues || 0,
      },
      businessImpact: {
        vacantInventoryValue: vacancyLossFullMonth || 0,
        curMonth: {
          moveIn: curMonthMoveIn,
          moveOut: curMonthMoveOut,
          occupancyPercent: occupiedPercentage,
        },
        lastMonth: {
          moveIn: lastMonthMoveIn,
          moveOut: lastMonthMoveOut,
          occupancyPercent: lastMonthOccupancyPercent,
        },
        nextMonth: {
          moveIn: nextMonthMoveIn,
          moveOut: nextMonthMoveOut,
          net: Number(curMonthMoveIn.totalRent) + Number(nextMonthMoveIn.totalRent) - Number(curMonthMoveOut.totalRent),
        }
      },
      landlordData: {
        curMonthRentPaid: curMonthRentPaid || 0,
        rentPending: rentPending || 0,
        totalRentToBePaid: totalLandlordRentToBePaid || 0,
      },
      currentVersion,
      bankAccounts: bankAccounts || [],
      vendorTypes: vendorTypes || [],
      isManualMoveInEnabled: isManualMoveInEnabled ? isManualMoveInEnabled?.value : 0,
      canHideDues,
      staffList: staffList || [],
      propList: propList || [],
      allowTenantOnInactiveProperty: client?.allowTenantOnInactiveProperty || 0,
      isEkycBulkEnabled: isEkycBulkEnabled,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

clients.GetRequestList = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "GetRequestList";

  try {
    const userType = req.userType;
    const { type = 0 } = req.query;
    let requests = [];
    let clientId = req.id;
    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Type [${type}], Client Requesting....`);
      const client = await clientDB.getById({ id: clientId });

      if (!client) {
        log.info(`[${C}], [${F}], Client Id [${clientId}], No Client Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (Number(type) === 0) {
        requests = await requestDB.getRoomReqByClientId({
          clientId,
          status: CONSTANTS.REQUEST_STATUS.PENDING,
        });
      } else {
        requests = await requestDB.getRoomReqByClientIdWithRequestType({
          clientId,
          status: CONSTANTS.REQUEST_STATUS.PENDING,
          type: Number(type),
        });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Requests list sent successfully`
      );
    } else {
      const staffId = req.id;
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Type [${type}], Staff Requesting....`);

      const staff = await staffDB.getById({ id: staffId });
      clientId = staff.clientId;
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      clientId = staff.clientId;
      let clientRequestList = [];
      if (Number(type) === 0) {
        clientRequestList = await requestDB.getRoomReqByClientId({
          clientId: staff.clientId,
          status: CONSTANTS.REQUEST_STATUS.PENDING,
        });
      } else {
        clientRequestList = await requestDB.getRoomReqByClientIdWithRequestType({
          clientId: staff.clientId,
          status: CONSTANTS.REQUEST_STATUS.PENDING,
          type: Number(type),
        });
      }

      if (!clientRequestList) clientRequestList = [];

      if (staff.role === CONSTANTS.STAFF_ROLES.PARTNER) {
        requests = clientRequestList;
      } else {
        const staffLinkedProps = await propertyDB.getPropsByStaffId({
          staffId,
        });

        if (staffLinkedProps) {
          requests = clientRequestList?.filter(
            (request: requestsTypes & { propertyId: number }) => {
              const isLinked = staffLinkedProps.find(
                (prop: propertyStaffTypes) => prop.id === request.propertyId
              );

              if (isLinked) return true;
              else return false;
            }
          );
        }
      }
    }

    if (requests && requests.length > 0) {
      for (let request of requests) {
        let flatName = "";
        if (request.propType === CONSTANTS.PROPERTY_TYPE.FLAT) {
          const { name } = await flatDB.getById({ id: request.flatId });
          flatName = name;
        } else {
          flatName =
            request.floor === "G" ? "Ground Floor" : "Floor " + request.floor;
        }
        request.flatName = flatName;
        if (request.type === CONSTANTS.REQUEST_TYPE.MOVE_OUT) {
          const lastRentDue = await ledgerDB.getLastRent({
            tenantId: request.tenantId,
            clientId: request.clientId,
          });

          let rentStartDate = moment().add(1, "days").format("YYYY-MM-DD");
          if (lastRentDue && lastRentDue.rentEndDate) {
            rentStartDate = moment(lastRentDue?.rentEndDate)
              .add(1, "days")
              .format("YYYY-MM-DD");
          }
          request.rentStartDate = rentStartDate;
          let pendingDue = await duesDB.getPendingDuesTenantIdAndClientId({ clientId, tenantId: request?.tenantId });
          request.totalDues = pendingDue || 0;
        } else if (request.type === CONSTANTS.REQUEST_TYPE.REFUND) {
          let collection = 0;
          let advance = 0;
          const { totalCollection, advancePaid } =
            await ledgerDB.getTotalByTenantIdForMovingOut({
              tenantId: request.tenantId,
              clientId: request.clientId,
            });
          collection = totalCollection;
          advance = advancePaid;

          collection = collection || 0;
          advance = advance || 0;
          let total = 0;
          if (advance < 0 && collection >= 0) {
            total = Number(collection) + Number(advance);
          } else if (advance < 0 && collection < 0) {
            total = Number(advance);
          } else {
            total = Number(collection);
          }

          request.refundAmount = Number(total) > 0 ? 0 : total;
          request.accountDetails = await tenantBankDB.getByClientIdAndTenantId({ clientId, tenantId: request?.tenantId });
          let cancelledCheque = await documentDB.getIDByType({ tenantId: request?.tenantId, clientId, type: CONSTANTS.DOCUMENT_TYPES.CANCELLED_CHEQUE, moveOut: 1 });
          request.accountDetails.cancelledCheque = null;
          if (cancelledCheque) {
            request.accountDetails.cancelledCheque = cancelledCheque?.value;
          }

        } else if (request.type === CONSTANTS.REQUEST_TYPE.GUEST) {
          let guestDetail = await tenantGuestsDB.getGuestDetailByRequestId({
            clientId,
            requestId: request.id
          });
          //name       | mobile     | relation | noOfGuest | checkInDate | checkOutDate | vehicleNo  | description
          request.guestName = guestDetail?.name || "";
          request.guestMobile = guestDetail?.mobile || "";
          request.relation = guestDetail?.relation || "";
          request.noOfGuests = guestDetail?.noOfGuest || "";
          request.checkInDate = guestDetail?.checkInDate || "";
          request.checkOutDate = guestDetail?.checkOutDate || "";
          request.vehicleNo = guestDetail?.vehicleNo || "";
          request.reason = guestDetail?.reason || "";
        } //else if (request.type === CONSTANTS.REQUEST_TYPE.TENANT_MOVEMENT) {
        //   let movementDetail = await tenantMovementDB.getByRequestId ({requestId: request.id});
        //   request.movementType = movementDetail?.type || null;
        //   request.fromDate = movementDetail?.fromDate || null;
        //   request.toDate = movementDetail?.toDate || null;
        //   request.reason = movementDetail?.description || null;
        // }
      }
    }

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

clients.ConsentToGuestRequest = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ConsentToGuestRequest";

  try {
    const userType = req.userType;
    const { requestId, status, amount, reason = null } = req.body;
    let clientId = req.id;
    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      const clientId = req.id;
      log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Status [${status}], Client Requesting....`);

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

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

    } else {
      const staffId = req.id;
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Request Id [${requestId}], Status [${status}], Staff Requesting....`);

      const staff = await staffDB.getById({ id: staffId });

      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      clientId = staff?.clientId || 0;
    }

    const requestDetail = await requestDB.getById({ id: requestId });
    let message = CONSTANTS.MSG.ERROR_MESSAGE;
    if (!requestDetail) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request does not exist`);
    } else if (CONSTANTS.REQUEST_STATUS.PENDING !== requestDetail?.status) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request has been processed already`);
    } else {
      const occupancy = await occupancyDB.getByTenantIdAndClientId({
        clientId,
        tenantId: requestDetail.tenantId,
      })
      if (!occupancy) {
        log.info(
          `[${C}], [${F}], Tenant Id [${requestDetail?.tenantId}], Client Id [${clientId}], No Occupancy Found`
        );
        return res.status(400).json({
          msg: "Unable to send Guest request because your are not occupied at any bed",
          isSuccess: false,
        });
      }
      const notiSettings = await settingsDB.getByClientIdAndPropId({
        clientId,
        propId: requestDetail.propId,
      });

      let footerText = notiSettings.footer || "Kipinn Team";
      const property = await propertyDB.getById({ id: requestDetail.propId });
      let flatName = "";
      if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
        const { name } = await flatDB.getById({ id: occupancy.flatId });
        flatName = name;
      } else {
        flatName =
          occupancy.floor === "G" ? "Ground Floor" : "Floor " + occupancy.floor;
      }
      const room = await roomDB.getById({ id: occupancy.roomId });
      let occupancyName = `${property?.name} (${flatName}, ${room.roomNum})`;
      const tenant = await tenantDB.getById({ id: requestDetail.tenantId });

      if (CONSTANTS.REQUEST_STATUS.APPROVED === Number(status)) {
        log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Guest Charges [${amount}], Request approved`);
        if (Number(amount) > 0) {
          let guestDetail = await tenantGuestsDB.getGuestDetailByRequestId({
            clientId,
            requestId: requestId
          });
          let startDate = moment().format("YYYY-MM-DD");
          let endDate = moment().format("YYYY-MM-DD");
          if (guestDetail) {
            startDate = guestDetail?.checkInDate || moment().format("YYYY-MM-DD");
            endDate = guestDetail?.checkOutDate || moment().format("YYYY-MM-DD");
          }
          const referenceId = await generateLedgerReferenceId({ clientId });
          await duesDB.addWithStartEndDateX({
            tenantId: requestDetail.tenantId,
            amount: amount,
            occupancyId: requestDetail.occupancyId,
            roomId: requestDetail.roomId,
            propId: requestDetail.propId,
            clientId,
            rentStartDate: startDate,
            rentEndDate: endDate,
            type: CONSTANTS.DUES_TYPES.GUEST_CHARGES,
            dueDate: startDate,
            balance: amount,
            ledgerReferenceId: referenceId,
            description: getDueDescription(CONSTANTS.DUES_TYPES.GUEST_CHARGES),
            title: "Guest Charges",
          });
          await ledgerDB.add({
            tenantId: requestDetail.tenantId,
            roomId: requestDetail.roomId,
            propId: requestDetail.propId,
            clientId,
            amount: amount,
            balance: amount,
            referenceId: referenceId,
            transactionId: null,
            type: CONSTANTS.DUES_TYPES.GUEST_CHARGES,
            rentStartDate: startDate,
            rentEndDate: endDate,
            dueDate: startDate,
            description: `${getDueDescription(CONSTANTS.DUES_TYPES.GUEST_CHARGES)} for ${startDate}`,
            title: "Guest Charges",
          });
        }
        let requestName = getRequestName(CONSTANTS.REQUEST_TYPE.GUEST);
        await sendWhatsappRequestUpdates(
          tenant?.mobile,
          tenant?.name,
          requestName,
          occupancyName,
          `accepted`,
          footerText,
          Number(clientId)
        );

        logTenantRequestActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId),
          Number(req.platform),
          CONSTANTS.ACTIVITY_TYPES.GUEST_REQUEST_ACCEPTED,
          requestDetail.propId,
          Number(requestDetail.tenantId),
          tenant.name,
          requestDetail.roomId,
          Number(amount),
        );
        message = "Request approved successfully";
        await requestDB.updateStatus({ status, id: requestId });
      } else if (CONSTANTS.REQUEST_STATUS.REJECTED === Number(status)) {
        message = "Request rejected successfully";
        log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request rejected`);
        let requestReason = "Your request has been rejected";
        if (!reason) {
          await requestDB.updateStatus({ status, id: requestId });
        } else {
          await requestDB.updateStatusWithReason({ status, reason, id: requestId });
          requestReason = reason;
        }
        if (Number(notiSettings?.whatsApp) === 1) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], WhatsApp Enabled`
          );
          let requestName = getRequestName(CONSTANTS.REQUEST_TYPE.GUEST);
          await sendWhatsappRequestRejectionWithReason(
            tenant?.mobile,
            tenant?.name,
            requestName,
            property?.name,
            requestReason,
            footerText,
            Number(clientId)
          );
        } else {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], WhatsApp Not Enabled`
          );
        }
        // const tenant = await tenantDB.getById({ id: requestDetail.tenantId });
        // await sendWhatsappRequestUpdates(
        // tenant?.mobile,
        // tenant?.name,
        // `Guest`,
        // occupancyName,
        // `rejected`,
        // footerText,
        // Number(clientId)
        // );
        logTenantRequestActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId),
          Number(req.platform),
          CONSTANTS.ACTIVITY_TYPES.GUEST_REQUEST_REJECTED,
          requestDetail.propId,
          Number(requestDetail.tenantId),
          tenant.name,
          requestDetail.roomId,
          0
        );
      }
    }

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

clients.ConsentToRefundRequest = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ConsentToRefundRequest";

  try {
    const userType = req.userType;
    //allowResubmit 1 Alllow , 0 Not allow
    let { requestId, status, reason = null, allowResubmit = 0 } = req.body;
    let clientId = req.id;
    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      const clientId = req.id;
      log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Status [${status}], Allow Resubmit [${allowResubmit}], Reason [${reason}], Client Requesting....`);
      //log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Status [${status}], Allow Resubmit [${allowResubmit}], Client Requesting....`);

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

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

    } else {
      const staffId = req.id;
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Request Id [${requestId}], Status [${status}], Allow Resubmit [${allowResubmit}], Staff Requesting....`);

      const staff = await staffDB.getById({ id: staffId });

      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      clientId = staff?.clientId || 0;
    }

    const requestDetail = await requestDB.getById({ id: requestId });
    let message = CONSTANTS.MSG.ERROR_MESSAGE;
    if (!requestDetail) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request does not exist`);
    } else if (CONSTANTS.REQUEST_STATUS.PENDING !== requestDetail?.status) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request has been processed already`);
    } else {
      const occupancy = await moveOutDB.getByTenantIdAndClientId({
        clientId,
        tenantId: requestDetail.tenantId,
      })
      if (!occupancy) {
        log.info(
          `[${C}], [${F}], Tenant Id [${requestDetail?.tenantId}], Client Id [${clientId}], No Occupancy Found`
        );
        return res.status(400).json({
          msg: "Unable to process Refund request because your are not occupied at any bed",
          isSuccess: false,
        });
      }
      const notiSettings = await settingsDB.getByClientIdAndPropId({
        clientId,
        propId: requestDetail.propId,
      });

      let footerText = notiSettings.footer || "Kipinn Team";
      const property = await propertyDB.getById({ id: requestDetail.propId });
      let flatName = "";
      if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
        const { name } = await flatDB.getById({ id: occupancy.flatId });
        flatName = name;
      } else {
        flatName =
          occupancy.floor === "G" ? "Ground Floor" : "Floor " + occupancy.floor;
      }
      const room = await roomDB.getById({ id: occupancy.roomId });
      let occupancyName = `${property?.name} (${flatName}, ${room.roomNum})`;
      const tenant = await tenantDB.getById({ id: requestDetail.tenantId });
      if (CONSTANTS.REQUEST_STATUS.INPROGRESS === Number(status)) {
        log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request InProgress`);
        //const tenant = await tenantDB.getById({ id: requestDetail.tenantId });
        await sendWhatsappRequestUpdates(
          tenant?.mobile,
          tenant?.name,
          `Refund`,
          occupancyName,
          `InProgress`,
          footerText,
          Number(clientId)
        );
        logTenantRequestActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId),
          Number(req.platform),
          CONSTANTS.ACTIVITY_TYPES.REFUND_REQUEST_INPROGRESS,
          requestDetail.propId,
          Number(requestDetail.tenantId),
          tenant.name,
          requestDetail.roomId,
          0,
        );
        message = "Request accepted successfully";
        await requestDB.updateStatus({ status, id: requestId });
      } else if (CONSTANTS.REQUEST_STATUS.REJECTED === Number(status)) {
        message = "Request rejected successfully";
        if (allowResubmit === 1) {
          status = CONSTANTS.REQUEST_STATUS.REOPEN_ALLOWED
          log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request rejected and allowed to resubmit again`);
        } else {
          log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request rejected`);
        }

        let requestReason = "Your request has been rejected";
        if (!reason) {
          await requestDB.updateStatus({ status, id: requestId });
        } else {
          await requestDB.updateStatusWithReason({ status, reason, id: requestId });
          requestReason = reason;
        }
        if (Number(notiSettings?.whatsApp) === 1) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], WhatsApp Enabled`
          );
          let requestName = getRequestName(CONSTANTS.REQUEST_TYPE.REFUND);
          await sendWhatsappRequestRejectionWithReason(
            tenant?.mobile,
            tenant?.name,
            requestName,
            property?.name,
            requestReason,
            footerText,
            Number(clientId)
          );
        } else {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], WhatsApp Not Enabled`
          );
        }
        // await requestDB.updateStatus({ status, id: requestId });
        // const tenant = await tenantDB.getById({ id: requestDetail.tenantId });
        // await sendWhatsappRequestUpdates(
        //   tenant?.mobile,
        //   tenant?.name,
        //   `Refund`,
        //   occupancyName,
        //   `rejected`,
        //   footerText,
        //   Number(clientId)
        // );
        logTenantRequestActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId),
          Number(req.platform),
          CONSTANTS.ACTIVITY_TYPES.REFUND_REQUEST_REJECTED,
          requestDetail.propId,
          Number(requestDetail.tenantId),
          tenant.name,
          requestDetail.roomId,
          0
        );
      }
    }

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

clients.InitiateRemovalOfTenant = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "InitiateRemovalOfTenant";

  try {
    let {
      tenantId,
      moveOutDate,
      isSecurityAdjustment,
      calculationDate,//not being used -- 2026-02-16
      requestId,
      forfeitRefund = 0,
    } = req.body;

    const userType = req.userType;

    let clientId = req.id;

    let doneBy = "";

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

      clientId = staff.clientId;
      doneBy = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], MoveOut Date [${moveOutDate}], Calculation Date [${calculationDate}], Is Security Adjustment [${isSecurityAdjustment}], Request Id [${requestId}], Forfeit Refund [${forfeitRefund}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], MoveOut Date [${moveOutDate}], Calculation Date [${calculationDate}], Is Security Adjustment [${isSecurityAdjustment}], Request Id [${requestId}], Forfeit Refund [${forfeitRefund}], Client Requested....`
      );
    }
    const client = await clientDB.getById({ id: clientId });

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

    if (Number(userType) === CONSTANTS.USER_TYPE.CLIENT) {
      doneBy = client.name;
    }

    const tenant = await tenantDB.getById({ id: tenantId });

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], MoveOut Date [${moveOutDate}], Calculation Date [${calculationDate}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const occupancies = await occupancyDB.getAllByTenantIdAndClientIdAsc({
      tenantId,
      clientId,
    });
    if (!occupancies) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], MoveOut Date [${moveOutDate}], Calculation Date [${calculationDate}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], MoveOut Date [${moveOutDate}], Calculation Date [${calculationDate}], is All Beds Occupied [${occupancies.length > 1
      }]`
    );

    const occupancy = occupancies[0];

    if (occupancy.status !== CONSTANTS.OCCUPANCY_STATUS.OCCUPIED) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], MoveOut Date [${moveOutDate}], Calculation Date [${calculationDate}], Occupancy status is not occupied`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    // Sukhbir - MultiOccupancy
    // if (tenant.status !== CONSTANTS.TENANT_STATUS.OCCUPIED) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], MoveOut Date [${moveOutDate}], Calculation Date [${calculationDate}], Tenant is not occupied`
    //   );
    //   return res
    //     .status(400)
    //     .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    // }

    const bed = await bedDB.getById({ id: occupancy.bedId });

    // if (bed?.status !== CONSTANTS.BED_STATUS.OCCUPIED) {
    if (bed?.status !== CONSTANTS.BED_STATUS.OCCUPIED && bed?.status !== CONSTANTS.BED_STATUS.RESERVED) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], MoveOut Date [${moveOutDate}], Calculation Date [${calculationDate}], Bed is not occupied`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (requestId) {
      const request = await requestDB.getById({ id: requestId });

      if (!request) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], MoveOut Date [${moveOutDate}], Calculation Date [${calculationDate}], No Request Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
    }

    //To handle other date and Move out date
    // moveOutDate = calculationDate;

    // amount < 0
    const securityTransanction = await ledgerDB.getSecurityTransactionAmountX({
      tenantId,
      roomId: occupancy.roomId,
      propId: occupancy.propId,
      type: CONSTANTS.DUES_TYPES.SECURITY,
    });

    // let securityAmt = securityTransanction ? Number(occupancy.security) : 0;
    let securityAmt = securityTransanction
      ? Math.abs(Number(securityTransanction.amount))
      : 0;

    const securityUsed = await ledgerDB.getUsedSecurityAmtByTenantIdAndClientId({
      clientId,
      tenantId,
      createdAt: occupancy.createdAt,
    });

    securityAmt = Number(securityAmt) - (Number(securityUsed) || 0);

    // To handle previous excess balance
    let prevBalance = await ledgerDB.getPreviousBalance({
      tenantId,
      clientId,
    });
    if (prevBalance && prevBalance < 0) {
      securityAmt += Math.abs(prevBalance);
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Security Amount [${securityAmt}]`
    );
    let referenceId = null;
    let remainingTempDues = await duesDB.getTempDuesByDate({
      tenantId,
      propId: occupancy.propId,
    });
    for (const due of remainingTempDues) {
      referenceId = await generateLedgerReferenceId({ clientId });
      let dueId = null;
      if (due.type === CONSTANTS.DUES_TYPES.RENT) {
        dueId = await duesDB.addWithStartEndDate({
          tenantId,
          amount: due.amount,
          occupancyId: due.occupancyId,
          roomId: due.roomId,
          propId: due.propId,
          clientId: due.clientId,
          type: due.type,
          dueDate: due.dueDate,
          balance: due.balance,
          ledgerReferenceId: referenceId,
          rentStartDate: due.rentStartDate,
          rentEndDate: due.rentEndDate,
        });
      } else {
        dueId = await duesDB.add({
          tenantId,
          amount: due.amount,
          occupancyId: due.occupancyId,
          roomId: due.roomId,
          propId: due.propId,
          clientId: due.clientId,
          type: due.type,
          dueDate: due.dueDate,
          balance: due.balance,
          ledgerReferenceId: referenceId,
        });
      }

      let dueName = await getDueName(due.type);
      await ledgerDB.add({
        tenantId,
        roomId: occupancy.roomId,
        propId: occupancy.propId,
        clientId,
        amount: due.amount,
        balance: due.balance,
        referenceId: referenceId,
        transactionId: null,
        type: due.type,
        dueDate: due.dueDate,
        rentStartDate: due.rentStartDate || null,
        rentEndDate: due.rentEndDate || null,
        description: `${dueName} for ${moment(due.rentStartDate).format(
          "MMM YYYY"
        )} added during eviction`,
        title: due?.title || null,
      });

      await duesDB.updateTallyStatus({
        id: dueId,
        tallyStatus: CONSTANTS.TALLY_STATUS.PENDING,
      });

      await duesDB.removeTempDue({ id: due.id });
    }

    const allDues = await duesDB.getByTenantIdAndPropIdAndRoomId({
      tenantId,
      propId: occupancy.propId,
      roomId: occupancy.roomId,
    });

    //To handle adding of excess balance in last due
    // let lengthOfDues = allDues.length;
    // let count = 1;

    let description = getDueDescription(-1);

    if (isSecurityAdjustment) {
      if (securityAmt <= 0) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Security Amount Available [${securityAmt}], Security amount is not sufficient to adjust dues`
        );
        return res.status(400).json({
          msg: "Security amount is not sufficient to adjust dues.",
          isSuccess: false,
        });
      }

      for (const singleDue of allDues) {
        if (securityAmt <= 0) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Security Amount Available [${securityAmt}], Due Id [${singleDue.id}], Security amount is not sufficient to adjust dues`
          );
          break;
        }

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Security Amount Available [${securityAmt}], Due Id [${singleDue.id}],  Ledger ReferenceId [${singleDue.ledgereferenceId}], Due Amount [${singleDue.amount}]`
        );

        const differenceAmt = securityAmt - Number(singleDue.balance);
        let isFullyAdjustable = false;
        let transactionAmount = 0;
        let remainingDueAmount = 0;

        if (differenceAmt >= 0) {
          isFullyAdjustable = true;
          transactionAmount = Number(singleDue.balance);
          securityAmt = differenceAmt;
        } else {
          isFullyAdjustable = false;
          transactionAmount = securityAmt;
          remainingDueAmount = Number(singleDue.balance) - securityAmt;
          securityAmt = 0;
        }

        if (isFullyAdjustable) {
          await duesDB.removeDue({ id: singleDue.id });
          const transId = await addTransactions({
            due: singleDue,
            clientId: clientId,
            tenantId: tenantId,
            amount: transactionAmount,
            description: `${getDueDescription(singleDue.type)} (Adjusted from security)`,
            mode: CONSTANTS.TRANSACTION_MODES.ADJUSTED_FROM_SECURITY,
            recordedBy: doneBy || "",
            duesStatsArr: [
              { title: description, amount: transactionAmount },
            ],
          });

          ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            amount: -transactionAmount,
            balance: 0,
            referenceId: singleDue.ledgerReferenceId,
            transactionId: transId,
            type: singleDue.type,
            dueDate: singleDue.dueDate,
            rentStartDate: singleDue.rentStartDate,
            rentEndDate: singleDue.rentEndDate,
            description: description,
            discount: singleDue.discount,
            title: singleDue?.title || null,
            subType: CONSTANTS.LEDGER_SUBTYPES.MARKED_FROM_SECURITY,
          });
        } else {
          await duesDB.updateBalance({
            id: singleDue.id,
            balance: remainingDueAmount,
          });

          const transId = await addTransactions({
            due: singleDue,
            clientId: clientId,
            tenantId: tenantId,
            amount: transactionAmount,
            description: `${getDueDescription(singleDue.type)} (Adjusted from security)`,
            mode: CONSTANTS.TRANSACTION_MODES.ADJUSTED_FROM_SECURITY,
            recordedBy: doneBy || "",
            duesStatsArr: [
              { title: description, amount: transactionAmount },
            ],
          });

          await ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            amount: -transactionAmount,
            balance: remainingDueAmount,
            referenceId: singleDue.ledgerReferenceId,
            transactionId: transId,
            type: singleDue.type,
            dueDate: singleDue.dueDate,
            rentStartDate: singleDue.rentStartDate,
            rentEndDate: singleDue.rentEndDate,
            description: description,
            discount: singleDue.discount,
            title: singleDue?.title || null,
            subType: CONSTANTS.LEDGER_SUBTYPES.MARKED_FROM_SECURITY,
          });
        }

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${singleDue.id
          }], Due Amount [${singleDue.amount
          }], Adjusted Amount [${transactionAmount}],  ${isFullyAdjustable ? "Fully" : "Partially"
          } Adjusted Successfully`
        );
      }
    }

    //Adding rest of the remaining security balance to the ledger to indicate that owner has to payback the amount to tenant.
    if (securityAmt > 0) {
      referenceId = await generateLedgerReferenceId({ clientId });
      ledgerDB.add({
        tenantId,
        roomId: occupancy.roomId,
        propId: occupancy.propId,
        clientId,
        amount: 0,
        balance: Number(forfeitRefund) === 1 ? 0 : -securityAmt,
        referenceId: referenceId,
        transactionId: null,
        type: CONSTANTS.DUES_TYPES.SECURITY,
        rentStartDate: null,
        rentEndDate: null,
        dueDate: moveOutDate,
        description: Number(forfeitRefund) === 1 ?
          `Refund of amount ₹${securityAmt} has been forfeited during eviction.`
          : "Security deposit and any excess amounts paid by tenant. Refund after eviction.",
      });
    }

    // Done by Sukhbir to avoid security deposit refund post-eviction and dues settlement if it is not paid.
    const { totalDues } = await duesDB.getTotalDuesByTenantIdForEviction({
      tenantId: tenant.id,
      propId: occupancy.propId,
    });

    for (const occupancy of occupancies) {
      await occupancyDB.initiateMoveOut({
        id: occupancy?.id,
        status: CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT,
        moveOutDate: moveOutDate + " 00:00:00",
      });

      let moveOutId = await moveOutDB.add({
        clientId,
        tenantId,
        propId: occupancy.propId,
        roomId: occupancy.roomId,
        bedId: occupancy.bedId,
        floor: occupancy.floor,
        reason: occupancy.reason,
        clientDues: securityAmt,
        tenantDues: totalDues || 0,
        rent: occupancy.rent,
        rentalCycle: occupancy.rentalCycle,
        rentalType: occupancy.rentalType,
        rentalMonths: occupancy.rentalMonths,
        noticePeriod: occupancy.noticePeriod,
        lockInPeriod: occupancy.lockInPeriod,
        security: occupancy.security,
        agreementPeriod: occupancy.agreementPeriod,
        agreementStartDate: occupancy.agreementStartDate,
        moveInDate: occupancy.moveInDate,
        moveOutDate: moveOutDate + " 00:00:00",
        flatId: occupancy.flatId,
        notes: occupancy.notes,
        rentalBond: occupancy.rentalBond,
        moveOutReason: occupancy.moveOutReason,
        isPoliceVerified: occupancy.isPoliceVerified,
        isRentAgreementSigned: occupancy.isRentAgreementSigned,
        stayType: occupancy.stayType,
        kycStatus: occupancy.kycStatus,
        discountType: occupancy.discountType,
        discount: occupancy.discount,
        discountPeriod: occupancy.discountPeriod,
        discountStartDate: occupancy.discountStartDate,
        discountEndDate: occupancy.discountEndDate,
        isDiscountFromFirstMonth: occupancy.isDiscountFromFirstMonth,
        wasCancelled: 0,
        status: CONSTANTS.MOVE_OUT_STATUS.INITIATED,
        gId: occupancy?.gId || null,
        tallyStatus: occupancy.tallyStatus,
      });

      await moveOutDB.updateBookedBy({ bookedBy: occupancy.bookedBy, id: moveOutId });

      await moveOutDB.updateIsGstEnabled({ isGstEnabled: occupancy.isGstEnabled, id: moveOutId });

      if (bed?.status === CONSTANTS.BED_STATUS.OCCUPIED) {
        await bedDB.updateStatus({
          id: occupancy.bedId,
          status: CONSTANTS.BED_STATUS.MOVING_OUT,
        });
      }

      if (Number(forfeitRefund) === 1) {
        await moveOutDB.updateRefundStatus({
          id: moveOutId,
          refundStatus: CONSTANTS.REFUND_STATUS.FORFEITED,
        });
      }
    }

    const propSettings = await settingsDB.getByClientIdAndPropId({
      clientId: client.id,
      propId: occupancy.propId,
    });

    if (Number(propSettings.whatsApp) === 1 && Number(propSettings.moveOutNotification) === 1 && Number(totalDues) > 0) {
      let footerText = propSettings.footer || "The Kipinn Team";

      const property = await propertyDB.getById({
        id: occupancy.propId,
      });
      const room = await roomDB.getById({ id: occupancy.roomId });
      if (Number(totalDues) > 0) {
        //When pending dues
        sendWhatsappTenantMoveOutInitiated(
          tenant.mobile,
          tenant.name,
          property.name,
          moveOutDate,
          totalDues,
          footerText,
          Number(clientId)
        );
      } else {
        //No pending dues
        sendWhatsappEvictionNotify(
          tenant.mobile,
          tenant.name,
          moveOutDate,
          property.name,
          room.roomNum,
          footerText,
          Number(clientId),
        );
      }
    }
    // Sukhbir - MultiOccupancy
    // await tenantDB.updateStatus({
    //   id: tenant?.id,
    //   status: CONSTANTS.TENANT_STATUS.MOVING_OUT,
    // });

    if (requestId) {
      await requestDB.updateStatus({
        id: requestId,
        status: CONSTANTS.REQUEST_STATUS.APPROVED,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move Out Date [${moveOutDate}], Move Out Request Approved`
      );
      await logTenantEvictionActivity(
        req.userType!,
        Number(req.id)!,
        Number(req.parentClientId),
        Number(req.platform),
        CONSTANTS.ACTIVITY_TYPES.MOVEOUT_REQUEST_ACCEPTED,
        occupancy.propId,
        Number(tenant.id),
        tenant.name,
        occupancy.roomId,
        ""
      );
    } else {
      await logTenantEvictionActivity(
        req.userType!,
        Number(req.id)!,
        Number(req.parentClientId),
        Number(req.platform),
        CONSTANTS.ACTIVITY_TYPES.MOVEOUT_INITIATED,
        occupancy.propId,
        Number(tenant.id),
        tenant.name,
        occupancy.roomId,
        moveOutDate
      );
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move Out Date [${moveOutDate}], Removal of tenant initiated successfully`
      );
    }

    return res.status(200).json({
      msg: "Removal of tenant initiated 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,
    });
  }
};

clients.CancelRemovalOfTenant = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "CancelRemovalOfTenant";

  try {
    const { tenantId } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

    const 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 });
    }

    // Sukhbir - MultiOccupancy
    // if (tenant.status !== CONSTANTS.TENANT_STATUS.MOVING_OUT) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Tenant is not moving out`
    //   );
    //   return res
    //     .status(400)
    //     .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    // }

    const occupancies = await occupancyDB.getAllByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancies) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], is All Beds Occupied [${occupancies.length > 1
      }]`
    );

    const occupancy = occupancies[0];

    for (let occupancy of occupancies) {
      if (occupancy.status !== CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Removal of tenant is not initiated`
        );
        return res.status(400).json({
          msg: "Removal of tenant is not initiated",
          isSuccess: false,
        });
      }

      const bed = await bedDB.getById({ id: occupancy.bedId });

      if (bed?.status === CONSTANTS.BED_STATUS.RESERVED) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Bed is reserved for other tenant`
        );
        return res.status(400).json({
          msg: "Please first cancel the reservation of other tenant",
          isSuccess: false,
        });
      }

      if (bed?.status !== CONSTANTS.BED_STATUS.MOVING_OUT) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Bed is not moving out`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
    }

    const securityAdjustedTrans = await ledgerDB.getEvictionAffectedRecords({
      tenantId,
      propId: occupancy.propId,
      roomId: occupancy.roomId,
    });

    if (securityAdjustedTrans) {
      for (const ledger of securityAdjustedTrans) {
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Ref Id [${ledger.referenceId}], Ledger Type [${ledger.type}], Ledger Amount [${ledger.amount}], Ledger Balance [${ledger.balance}]`
        );

        //To handle the deletion of remaining excess security balance from Ledger without adding it to dues
        if (ledger.amount == 0) {
          await ledgerDB.removeById({ id: ledger.id });
          continue;
        }
        const isDueExists = await duesDB.getByTenantIdAndLedgerReferenceId({
          tenantId,
          ledgerReferenceId: ledger.referenceId,
        });
        if (isDueExists) {
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Due Id [${isDueExists[0].id}], Due Balance [${isDueExists[0].balance}], Due Exist`
          );
          await duesDB.updateBalance({
            id: isDueExists[0].id,
            balance:
              Number(isDueExists[0].balance) + Math.abs(Number(ledger.amount)),
          });
          await ledgerDB.removeById({ id: ledger.id });
          await transactionDB.removeByLedgerReferenceId({ ledgerReferenceId: ledger.referenceId });
        } else {
          log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Due Not Exist`);
          await duesDB.addWithStartEndDate({
            tenantId,
            amount: Math.abs(ledger.amount),
            occupancyId: occupancy.id,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            rentStartDate: ledger.rentStartDate,
            rentEndDate: ledger.rentEndDate,
            dueDate: ledger.dueDate,
            type: ledger.type,
            balance: Math.abs(ledger.amount),
            ledgerReferenceId: ledger.referenceId,
          });

          await ledgerDB.removeById({ id: ledger.id });
          await transactionDB.removeByLedgerReferenceId({ ledgerReferenceId: ledger.referenceId });
        }
        // await transactionDB.removeByReferenceId({ id: transaction.referenceId });

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Amount [${ledger.amount}], Due Type [${ledger.type}], Due Added Successfully`
        );
      }
    }

    const moveOutCharge = await extraChargeDB.getByPropAndTypeX({
      clientId,
      propId: occupancy.propId,
      type: CONSTANTS.EXTRA_CHARGE_TYPES.MOVE_OUT_CHARGES,
    });

    if (moveOutCharge) {
      const moveOutChargeDue = await duesDB.getByTenantIdAndClientIdAndType({
        tenantId,
        clientId,
        type: CONSTANTS.DUES_TYPES.MOVE_OUT_CHARGES,
      });

      if (moveOutChargeDue) {
        for (let due of moveOutChargeDue) {
          if (due.amount !== due.balance) {
            continue;
          }
          await duesDB.removeDue({
            id: due?.id
          });

          await ledgerDB.remove({
            referenceId: due?.ledgerReferenceId,
          });
        }
      }
    }

    await duesDB.removeFutureDues({
      tenantId,
      clientId,
      propId: occupancy.propId,
    });

    await ledgerDB.removeFutureDues({
      tenantId,
      clientId,
      propId: occupancy.propId,
    });

    const lastRentRecord = await ledgerDB.getLastRent({
      tenantId: tenantId,
      clientId: clientId,
    });

    log.info(`Last Rent Record [${JSON.stringify(lastRentRecord)}]`);

    if (lastRentRecord) {
      // log.info(
      //   `Checking if last rent record is same as occupancy move out date, Due End Date [${lastRentRecord.rentEndDate}], Occupancy Move Out Date [${occupancy.moveOutDate}]`
      // );
      if (moment(lastRentRecord.rentEndDate).isSame(moment(occupancy.moveOutDate))) {
        // log.info(`Last rent record is same as occupancy move out date`);
        const lastRentDue = await duesDB.getByTenantIdAndLedgerReferenceId({
          tenantId,
          ledgerReferenceId: lastRentRecord.referenceId,
        });

        // log.info(`Last Rent Due [${JSON.stringify(lastRentDue)}]`);

        const lastRentDueTransactions = await transactionDB.getByLedgerReferenceId({
          ledgerReferenceId: lastRentRecord.referenceId,
        });

        // log.info(`Last Rent Due Transactions [${JSON.stringify(lastRentDueTransactions)}]`);

        let totalPaidAmt = 0;
        if (lastRentDueTransactions) {
          for (let transaction of lastRentDueTransactions) {
            totalPaidAmt += Number(transaction.amount);
          }
        }

        // log.info(`Total Paid Amount [${totalPaidAmt}]`);

        let rentEndDate = moment().date(occupancy.rentalCycle).subtract(1, "day").add(1, "month").format("YYYY-MM-DD");

        // log.info(`Rent End Date [${rentEndDate}]`);

        if (lastRentDue) {
          // log.info(`Last Rent Due Found, Updating Dues And Ledgers`);
          if (Number(occupancy.rent) - Number(totalPaidAmt) > 0) {
            await duesDB.updateDueForCancelEviction({
              id: lastRentDue[0].id,
              rentStartDate: lastRentDue[0].rentStartDate,
              rentEndDate: rentEndDate,
              amount: occupancy.rent,
              balance: Number(occupancy.rent) - Number(totalPaidAmt),
            });
          }

          await ledgerDB.updateDueForCancelEviction({
            referenceId: lastRentRecord.referenceId,
            rentStartDate: lastRentDue[0].rentStartDate,
            rentEndDate: rentEndDate,
            amount: occupancy.rent,
            balance: Number(occupancy.rent) - Number(lastRentDue[0].amount),
            description: `Rent for ${moment(lastRentDue[0].rentStartDate).format("DD MMM, YY")} to ${moment(rentEndDate).format("DD MMM, YY")}`
          });
        } else {
          // log.info(`Last Rent Due Not Found, Adding Dues And Ledgers`);
          let referenceId = await generateLedgerReferenceId({ clientId });
          await duesDB.addWithStartEndDateX({
            tenantId: tenantId,
            amount: occupancy.rent - totalPaidAmt,
            occupancyId: occupancy.id,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            rentStartDate: lastRentRecord.rentStartDate,
            rentEndDate: rentEndDate,
            type: CONSTANTS.DUES_TYPES.RENT,
            dueDate: rentEndDate,
            balance: occupancy.rent,
            ledgerReferenceId: referenceId,
            description: `Rent for ${moment(lastRentRecord.rentStartDate).format("DD MMM, YY")} to ${moment(rentEndDate).format("DD MMM, YY")}`,
            title: "Rent",
          });
          await ledgerDB.add({
            tenantId: tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            amount: occupancy.rent - totalPaidAmt,
            balance: occupancy.rent - totalPaidAmt,
            referenceId: referenceId,
            transactionId: null,
            type: CONSTANTS.DUES_TYPES.SECURITY,
            rentStartDate: null,
            rentEndDate: null,
            dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
            description: `Rent for ${moment(lastRentRecord.rentStartDate).format("DD MMM, YY")} to ${moment(rentEndDate).format("DD MMM, YY")}`,
            title: "Rent",
          });
        }
      }
    }

    for (const occupancy of occupancies) {
      if (occupancy.stayType === CONSTANTS.STAY_TYPE.SHORT) {
        await occupancyDB.cancelMoveOutShortStay({
          id: occupancy?.id,
          status: CONSTANTS.OCCUPANCY_STATUS.OCCUPIED,
          moveOutDate: moment(occupancy.moveInDate).add(occupancy.agreementPeriod, "days").format("YYYY-MM-DD"),
        });
      } else {
        await occupancyDB.cancelMoveOut({
          id: occupancy?.id,
          status: CONSTANTS.OCCUPANCY_STATUS.OCCUPIED,
        });
      }

      await moveOutDB.remove({
        clientId,
        tenantId,
        propId: occupancy.propId,
        roomId: occupancy.roomId,
        bedId: occupancy.bedId,
      });

      await bedDB.updateStatus({
        id: occupancy.bedId,
        status: CONSTANTS.BED_STATUS.OCCUPIED,
      });
    }

    // Sukhbir - MultiOccupancy
    // await tenantDB.updateStatus({
    //   id: tenant?.id,
    //   status: CONSTANTS.TENANT_STATUS.OCCUPIED,
    // });
    await logTenantEvictionActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.MOVEOUT_REQUEST_CANCELLED,
      occupancy.propId,
      Number(tenant.id),
      tenant.name,
      occupancy.roomId,
      ""
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Removal of tenant cancelled successfully`
    );

    return res.status(200).json({
      msg: "Removal of tenant cancelled 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,
    });
  }
};

clients.CancelRemovalOfTenant_TRASH = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Client Controller";
  const F = "CancelRemovalOfTenant";

  try {
    const { tenantId } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    const 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 });
    }

    // Sukhbir - MultiOccupancy
    // if (tenant.status !== CONSTANTS.TENANT_STATUS.MOVING_OUT) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Tenant is not moving out`
    //   );
    //   return res
    //     .status(400)
    //     .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    // }

    const occupancies = await occupancyDB.getAllByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancies) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], is All Beds Occupied [${occupancies.length > 1
      }]`
    );

    const occupancy = occupancies[0];

    if (occupancy.status !== CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Removal of tenant is not initiated`
      );
      return res
        .status(400)
        .json({ msg: "Removal of tenant is not initiated", isSuccess: false });
    }

    const bed = await bedDB.getById({ id: occupancy.bedId });

    if (bed?.status === CONSTANTS.BED_STATUS.RESERVED) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Bed is reserved for other tenant`
      );
      return res.status(400).json({
        msg: "Please first cancel the reservation of other tenant",
        isSuccess: false,
      });
    }

    if (bed?.status !== CONSTANTS.BED_STATUS.MOVING_OUT) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Bed is not moving out`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const securityAdjustedTrans =
      await transactionDB.getSecurityAdjustedTransactions({
        clientId,
        tenantId,
        propId: occupancy.propId,
        roomId: occupancy.roomId,
        type: CONSTANTS.TRANSACTION_TYPES.SECURITY_ADJUSTED,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
      });

    if (securityAdjustedTrans) {
      for (const transaction of securityAdjustedTrans) {
        const isDueExists = await duesDB.getParticularDue({
          tenantId,
          occupancyId: occupancy.id,
          clientId,
          dueDate: transaction.dueDate,
          type: transaction.transactionFor,
        });
        const referenceId = await generateLedgerReferenceId({ clientId });
        if (isDueExists) {
          await duesDB.updateAmount({
            id: isDueExists.id,
            amount: Number(isDueExists.amount) + Number(transaction.amount),
          });
        } else {
          await duesDB.add({
            clientId,
            tenantId,
            amount: transaction.amount,
            occupancyId: occupancy.id,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            type: transaction.transactionFor,
            dueDate: transaction.dueDate,
            balance: transaction.amount,
            ledgerReferenceId: referenceId,
          });
          await ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            amount: transaction.amount.amount,
            balance: transaction.amount.amount,
            referenceId: referenceId,
            transactionId: null,
            type: transaction.type.type,
            rentStartDate: null,
            rentEndDate: null,
          });
        }

        await transactionDB.removeById({ id: transaction.id });

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Amount [${transaction.amount}], Due Type [${transaction.transactionFor}], Due Added Successfully`
        );
      }
    }

    await duesDB.removeFutureDues({
      tenantId,
      clientId,
      propId: occupancy.propId,
    });

    for (const occupancy of occupancies) {
      await occupancyDB.cancelMoveOut({
        id: occupancy?.id,
        status: CONSTANTS.OCCUPANCY_STATUS.OCCUPIED,
      });

      await moveOutDB.remove({
        clientId,
        tenantId,
        propId: occupancy.propId,
        roomId: occupancy.roomId,
        bedId: occupancy.bedId,
      });

      await bedDB.updateStatus({
        id: occupancy.bedId,
        status: CONSTANTS.BED_STATUS.OCCUPIED,
      });
    }

    // Sukhbir - MultiOccupancy
    // await tenantDB.updateStatus({
    //   id: tenant?.id,
    //   status: CONSTANTS.TENANT_STATUS.OCCUPIED,
    // });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Removal of tenant cancelled successfully`
    );

    return res.status(200).json({
      msg: "Removal of tenant cancelled 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,
    });
  }
};

clients.RemoveTenantNow = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "RemoveTenantNow";

  try {
    const {
      tenantId,
      electricityReading,
      discardDues = false,
      settleDues = true,
      totalDues,
    } = req.body;

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

    let clientId = req.id;

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

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Electricity Reading [${electricityReading}], Staff Id [${req.id}], Discard Dues [${discardDues}], Settle Dues [${settleDues}], Total Dues [${totalDues}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Electricity Reading [${electricityReading}], Discard Dues [${discardDues}], Settle Dues [${settleDues}], Total Dues [${totalDues}], Client Requested....`
      );
    }

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

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      recordedBy = client.name;
    }

    const tenant = await tenantDB.getById({ id: tenantId });

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

    // Sukhbir - MultiOccupancy
    // if (tenant.status !== CONSTANTS.TENANT_STATUS.MOVING_OUT) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Electricity Reading [${electricityReading}], Tenant is not moving out`
    //   );
    //   return res
    //     .status(400)
    //     .json({ msg: "Removal of tenant is not initiated", isSuccess: false });
    // }

    const occupancies = await occupancyDB.getAllByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancies) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Electricity Reading [${electricityReading}],  No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Electricity Reading [${electricityReading}], is All Beds Occupied [${occupancies.length > 1
      }]`
    );
    const occupancy = occupancies[0];

    if (occupancy.status !== CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Electricity Reading [${electricityReading}], Removal of tenant is not initiated`
      );
      return res
        .status(400)
        .json({ msg: "Removal of tenant is not initiated", isSuccess: false });
    }

    const property = await propertyDB.getById({ id: occupancy.propId });

    const room = await roomDB.getById({ id: occupancy.roomId });

    // const room = await roomDB.getById({ id: occupancy.roomId }); //moved to occupancy loop

    let moveOutDetails = null;
    if (occupancy.flatId) {
      moveOutDetails = await moveOutDB.getAllByMultiDetailsFlat({
        clientId,
        tenantId,
        propId: occupancy.propId,
        flatId: occupancy.flatId,
        status: CONSTANTS.MOVE_OUT_STATUS.INITIATED,
      });
    } else {
      moveOutDetails = await moveOutDB.getAllByMultiDetails({
        clientId,
        tenantId,
        propId: occupancy.propId,
        roomId: occupancy.roomId,
        status: CONSTANTS.MOVE_OUT_STATUS.INITIATED,
      });
    }

    if (!moveOutDetails) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Electricity Reading [${electricityReading}], No Move Out Details Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    //const tenantDues = await duesDB.getByTenantId({ tenantId });
    const tenantDues = await duesDB.getByTenantIdAndClientId({ tenantId, clientId });
    if (tenantDues && tenantDues.length > 0) {
      for (let due of tenantDues) {
        if (due.type !== CONSTANTS.DUES_TYPES.SECURITY) {
          // await moveOutDuesDB.create({
          //   moveOutId: moveOutDetails[0].id,
          //   tenantId: tenantId,
          //   roomId: due.roomId,
          //   propId: due.propId,
          //   clientId: due.clientId,
          //   amount: due.balance,
          //   dueDate: due.dueDate,
          //   type: due.type,
          // });
          const dueId = await moveOutDuesDB.add({
            moveOutId: moveOutDetails[0].id,
            tenantId,
            roomId: due.roomId,
            propId: due.propId,
            clientId: due.clientId,
            amount: due.amount,
            balance: due.balance,
            ledgerReferenceId: due.ledgerReferenceId,
            dueDate: due.dueDate,
            type: due.type,
            rentStartDate: due.rentStartDate,
            rentEndDate: due.rentEndDate,
            description: due.description,
            title: due.title,
            discount: due.discount,
          });

          await moveOutDuesDB.updateTallyStatus({
            id: dueId,
            tallyStatus: due.tallyStatus,
          });
          await moveOutDuesDB.updateTallyInfo({
            id: dueId,
            tallyBillRef: due.tallyBillRef,
            tallyGuid: due.tallyGuid,
          });
        } else if (due.type === CONSTANTS.DUES_TYPES.SECURITY) {
          if (Number(due.amount) === Number(due.balance)) {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Ledger Reference Id [${due.ledgerReferenceId}], Delete Ledger Entry as Security Due is not paid`
            );
            await ledgerDB.remove({ referenceId: due.ledgerReferenceId });
          } else {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Ledger Reference Id [${due.ledgerReferenceId}], Update Ledger Entry as Security Due is partially paid`
            );
            await ledgerDB.UpdateAmountAndBalanceByReferenceId({
              referenceId: due.ledgerReferenceId,
              amount: due.amount - due.balance,
              balance: due.amount - due.balance,
            });
            await ledgerDB.UpdateBalanceByReferenceIdForPaidEntry({
              referenceId: due.ledgerReferenceId,
              balance: due.amount - (due.amount - due.balance),
            });
          }
        }
      }
    }

    for (const occupancy of occupancies) {
      try {
        const bed = await bedDB.getById({ id: occupancy.bedId });
        const room = await roomDB.getById({ id: occupancy.roomId });
        const bedCount = await bedDB.getCountsByRoomId({
          id: occupancy.roomId,
        });
        const totalBeds = bedCount.totalBeds;

        if (
          bed?.status !== CONSTANTS.BED_STATUS.MOVING_OUT &&
          bed?.status !== CONSTANTS.BED_STATUS.RESERVED
        ) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Electricity Reading [${electricityReading}], Bed Id [${bed.id}] Bed status is not moving out nor reserved`
          );
          continue;
        }

        if (bed?.status === CONSTANTS.BED_STATUS.MOVING_OUT) {
          await bedDB.updateStatus({
            id: occupancy.bedId,
            status: CONSTANTS.BED_STATUS.VACANT,
          });

          const vacantBed = await bedDB.getVacantBeds({
            roomId: room.id,
            status: CONSTANTS.BED_STATUS.VACANT,
          });
          if (vacantBed && vacantBed.length === Number(totalBeds)) {
            await roomDB.updateStatus({
              id: room.id,
              status: CONSTANTS.ROOM_STATUS.VACANT,
            });
          } else {
            await roomDB.updateStatus({
              id: room.id,
              status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
            });
          }

          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Bed Id [${occupancy.bedId}], Electricity Reading [${electricityReading}], Bed status updated to vacant`
          );
        }

        if (bed?.status === CONSTANTS.BED_STATUS.RESERVED) {
          const reservedOccupancy = await occupancyDB.getByBedId({
            bedId: occupancy.bedId,
            status: CONSTANTS.OCCUPANCY_STATUS.RESERVED,
          });

          const tenants = await occupancyDB.getAllByBedId({
            bedId: bed.id,
          });

          if (tenants && tenants.length > 2) {
            await bedDB.updateStatus({
              id: occupancy.bedId,
              status: CONSTANTS.BED_STATUS.RESERVED,
            });
          } else if (!reservedOccupancy) {
            await bedDB.updateStatus({
              id: occupancy.bedId,
              status: CONSTANTS.BED_STATUS.VACANT,
            });

            const vacantBed = await bedDB.getVacantBeds({
              roomId: room.id,
              status: CONSTANTS.BED_STATUS.VACANT,
            });
            if (vacantBed && vacantBed.length === Number(totalBeds)) {
              await roomDB.updateStatus({
                id: room.id,
                status: CONSTANTS.ROOM_STATUS.VACANT,
              });
            } else {
              await roomDB.updateStatus({
                id: room.id,
                status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
              });
            }

            log.info(
              `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Bed Id [${bed.id}], Electricity Reading [${electricityReading}], No Reservation Found and Bed status updated to vacant`
            );
          } else {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Bed Id [${bed.id}], Electricity Reading [${electricityReading}], Reservation Found with Occupancy Id [${reservedOccupancy.id}]`
            );

            let isFuturMoveInDate = false;
            if (moment(reservedOccupancy.moveInDate).isAfter(moment()))
              isFuturMoveInDate = true;

            await occupancyDB.updateStatus({
              id: reservedOccupancy.id,
              status: isFuturMoveInDate
                ? CONSTANTS.OCCUPANCY_STATUS.RESERVED
                : CONSTANTS.OCCUPANCY_STATUS.OCCUPIED,
            });

            await bedDB.updateStatus({
              id: occupancy.bedId,
              status: isFuturMoveInDate
                ? CONSTANTS.BED_STATUS.VACANT_RESERVED
                : CONSTANTS.BED_STATUS.OCCUPIED,
            });

            // Sukhbir - MultiOccupancy
            // await tenantDB.updateStatus({
            //   id: reservedOccupancy.tenantId,
            //   status: isFuturMoveInDate
            //     ? CONSTANTS.TENANT_STATUS.RESERVED
            //     : CONSTANTS.TENANT_STATUS.OCCUPIED,
            // });

            log.info(
              `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Bed Id [${bed.id}], Reservation Found with Occupancy Id [${reservedOccupancy.id}], Electricity Reading [${electricityReading}], Bed & Occupancy status updated to occupied`
            );
          }
        }

        // await documentDB.removeByTenantIdandClientId({
        //   tenantId: occupancy?.tenantId,
        //   clientId: occupancy?.clientId,
        // });

        await documentDB.updateMoveOutStatus({
          tenantId: occupancy?.tenantId,
          clientId: occupancy?.clientId,
          moveOut: 1,
        });

        await duesDB.removeByOccupancyId({
          occupancyId: occupancy?.id,
        });

        await requestDB.removeByOccupancyId({
          occupancyId: occupancy?.id,
        });

        await occupancyDB.removeById({
          id: occupancy?.id,
        });
      } catch (error: any) {
        log.info(
          `[${C}], [${F}], Inner Loop Error: ${error?.message || error}`
        );
      }
    }

    for (const singleMoveOut of moveOutDetails) {
      await moveOutDB.updateStatus({
        id: singleMoveOut.id,
        status: CONSTANTS.MOVE_OUT_STATUS.MOVEOUT,
      });

      await moveOutDB.updateElectricityReading({
        id: singleMoveOut.id,
        electricityReading,
      });

      // if (!moment(singleMoveOut.moveOutDate).isSame(moment(), "day")) {
      //   await moveOutDB.updateMoveOutDate({
      //     id: singleMoveOut.id,
      //     moveOutDate: moment().format("YYYY-MM-DD HH:mm:ss"),
      //   });
      // }

      if (moment(singleMoveOut.moveOutDate).isAfter(moment(), "day")) {
        await moveOutDB.updateMoveOutDate({
          id: singleMoveOut.id,
          moveOutDate: moment().format("YYYY-MM-DD HH:mm:ss"),
        });
      }
    }

    // Sukhbir - MultiOccupancy - Keep this status as such
    await tenantDB.updateStatus({
      id: tenant?.id,
      status: CONSTANTS.TENANT_STATUS.VACANT,
    });

    await eqaroTenantsDB.updateStatus({
      id: tenant?.id,
      status: CONSTANTS.EQARO_TENANT_STATUS.EXPIRED,
    });

    await tenantDB.updateKycStatus({
      id: tenant?.id,
      kycStatus: CONSTANTS.KYC_STATUS.PENDING,
    });

    await tenantDB.updateLastReminded({
      id: tenantId,
      lastRemindedOn: null,
    });

    // remove everything except transactions...
    await complaintDB.removeByClientIdAndTenantId({ tenantId, clientId });
    await duesDB.removeTempByClientIdAndTenantId({ tenantId, clientId });
    await notificationDB.removeByUserId({ userId: tenantId });

    const moveInDetails = await moveInDB.getByTenantIdandClientId({
      tenantId,
      clientId,
    });

    if (moveInDetails) {
      await imageDB.removeByMoveInId({ moveInInspectionId: moveInDetails.id });
      await moveInDB.removeById({ id: moveInDetails.id });
    }

    if (true === discardDues) {
      if (true === settleDues) {
        const securityAdjustEntry = await ledgerDB.getLastEntry({
          tenantId,
          clientId,
        });

        if (securityAdjustEntry !== false) {
          await ledgerDB.removeById({
            id: securityAdjustEntry.id,
          });
        }

        let transationType = CONSTANTS.TRANSACTION_FOR.SETTLEMENT;
        /**
         * Below Code to handle setting of transaction type, 
         * if single due exist then same dues type will be recorded in collection 
         * if multiple dues but of same type then same dues type will be recorded in collection
         * if multiple dues but of different types then transaction type will be recorded as SETTLEMENT in collection 
        **/
        if (tenantDues && tenantDues.length > 0) {
          let allSame = true;
          let dueType = tenantDues[0].type;
          for (let due of tenantDues) {
            if (due.type !== dueType) {
              allSame = false;
              break;
            }
          }
          if (allSame) {
            transationType = dueType;
          }
        }
        //<--------Handling end-------->

        if (!isNaN(Number(totalDues)) && Number(totalDues) !== 0) {
          //if (Number(totalDues) !== 0) {
          const referenceId = await generateLedgerReferenceId({ clientId });
          const gId = await generateTransId();

          const transId = await transactionDB.add({
            gId: gId,
            clientId: clientId,
            tenantId: tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            amount: totalDues,
            name: "Dues settled offline during eviction",
            type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
            transactionFor: transationType,
            status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
            dueDate: tenantDues[0]?.dueDate || moment().format("YYYY-MM-DD"),
            mode: CONSTANTS.TRANSACTION_MODES.OFFLINE,
            receipt: null,
            collectionDate: moment().format("YYYY-MM-DD HH:mm:ss"),
            propName: property.name,
            roomNum: room.roomNum,
            ledgerReferenceId: referenceId,
            recordedBy: recordedBy,
            title: null,
            isFinanciallyApplicable: 1,
          });

          await ledgerDB.add({
            tenantId: tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId: clientId,
            amount: -totalDues,
            balance: 0,
            referenceId: referenceId,
            transactionId: transId,
            type: transationType,
            rentStartDate: tenantDues[0]?.rentStartDate || moment().format("YYYY-MM-DD"),
            rentEndDate: tenantDues[0]?.rentEndDate || moment().format("YYYY-MM-DD"),
            description: "Dues settled offline during eviction",
            dueDate: tenantDues[0]?.dueDate || moment().format("YYYY-MM-DD"),
          });

          let settings = await settingsDB.getByClientIdAndPropId({
            clientId,
            propId: occupancy.propId,
          });

          let logo = settings?.logo
            ? process.env.RS_LOGO_URI + client?.id + "/" + occupancy?.propId + "/" + settings?.logo
            : client?.logo
              ? process.env.RS_LOGO_URI + client?.id + "/" + client?.logo
              : String(process.env.RS_DEFAULT_LOGO_URI);

          let flatName = "";
          if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
            const { name } = await flatDB.getById({ id: occupancy.flatId });
            flatName = name;
          } else {
            flatName =
              occupancy.floor === "G"
                ? "Ground Floor"
                : "Floor " + occupancy.floor;
          }

          let isGstEnabled = property?.isGstEnabled || 0;
          let invoiceNo = property?.invoiceNo || null;
          let invoiceNoPrefix = property?.invoiceNoPrefix || null;
          let gstNo = property?.gstNo || null;
          let businessName = property?.ownerName || ""
          let transactionInvoiceNo = ""
          let prefix = "";
          if (0 != isGstEnabled && 0 != invoiceNo) {
            let getGstTransactions = await transactionDB.getByGstNo({ gstNo });
            if (false == getGstTransactions) {
              if (null != invoiceNoPrefix) {
                prefix = invoiceNoPrefix;
              }
              transactionInvoiceNo = `${prefix}${invoiceNo}`;
            } else {
              invoiceNo = getGstTransactions?.invoiceNo;
              if (null != invoiceNoPrefix) {
                prefix = invoiceNoPrefix;
                invoiceNo = invoiceNo.replace(prefix, "");
                transactionInvoiceNo = `${prefix}${String(Number(invoiceNo) + 1)}`;
              } else {
                transactionInvoiceNo = String(Number(invoiceNo) + 1);
              }
            }
            businessName = property?.businessName || "";
          } else {
            isGstEnabled = 0;
          }

          // const receipt = await createReceiptMultipleDues({
          //   title: "Dues settled offline during eviction",
          //   roomNum: room.roomNum,
          //   propName: property.name,
          //   dueDate: moment().format("MMM, YYYY"),
          //   mode: getModeName(Number(CONSTANTS.TRANSACTION_MODES.OFFLINE)),
          //   transGId: gId,
          //   paidDate: moment().format("DD MMM, YYYY"),
          //   month: moment().format("MMM, YYYY"),
          //   tenantName: tenant?.name || "",
          //   amount: Number(totalDues),
          //   address: `${property?.address}`,
          //   landlord: businessName || "",
          //   landlordNumber: property?.ownerMobile || "",
          //   logo: logo,
          //   dueStats: [
          //     {
          //       title: "Dues settled offline during eviction",
          //       amount: Number(totalDues),
          //     },
          //   ],
          //   "landlord-pan": "",
          //   occupancy,
          //   flatName,
          //   propType: property.type,
          //   isGstEnabled,
          //   transactionInvoiceNo,
          //   gstNo,
          // });
          const receipt = await generateRentReciept({
            title: "Dues settled offline during eviction",
            roomNum: room.roomNum,
            propName: property.name,
            dueDate: moment().format("MMM, YYYY"),
            mode: getModeName(Number(CONSTANTS.TRANSACTION_MODES.OFFLINE)),
            transGId: gId,
            paidDate: moment().format("DD MMM, YYYY"),
            month: moment().format("MMM, YYYY"),
            tenantName: tenant?.name || "",
            amount: Number(totalDues),
            address: `${property?.address}`,
            landlord: businessName || "",
            landlordNumber: property?.ownerMobile || "",
            logo: logo,
            dueStats: [
              {
                title: "Dues settled offline during eviction",
                amount: Number(totalDues),
              },
            ],
            "landlord-pan": "",
            occupancy,
            flatName,
            propType: property.type,
            isGstEnabled,
            transactionInvoiceNo,
            gstNo,
            recordedBy
          });

          if (0 != isGstEnabled && 0 != invoiceNo) {
            await transactionDB.updateReceiptWithInvoice({ id: transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
          } else {
            await transactionDB.updateReceipt({
              id: transId,
              receipt,
            });
          }
        }

        //Below code for settling refund on 1 option
        // const transGId: string = await generateTransId();
        // let transId = await transactionDB.add({
        //   gId: transGId,
        //   clientId: occupancy.clientId,
        //   tenantId: occupancy.tenantId,
        //   roomId: occupancy.roomId,
        //   propId: occupancy.propId,
        //   amount: securityAdjustEntry.balance,
        //   name: "Excess payment or security returned",
        //   type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
        //   transactionFor: CONSTANTS.TRANSACTION_FOR.SETTLEMENT,
        //   status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
        //   dueDate: moment(occupancy.moveOutDate).format("YYYY-MM-DD"),
        //   receipt: "",
        //   mode: CONSTANTS.TRANSACTION_MODES.OFFLINE,
        //   collectionDate: moment().format("YYYY-MM-DD"),
        //   propName: property.name,
        //   roomNum: room.roomNum,
        //   ledgerReferenceId: securityAdjustEntry?.referenceId || null,
        //   recordedBy: recordedBy,
        // });

        // let isGstEnabled = property?.isGstEnabled || 0;
        // let invoiceNo = property?.invoiceNo || null;
        // let invoiceNoPrefix = property?.invoiceNoPrefix || null;
        // let gstNo = property?.gstNo || null;
        // let businessName = property?.ownerName || ""
        // let transactionInvoiceNo = ""
        // let prefix = "";
        // if(0 != isGstEnabled && 0 != invoiceNo) {
        //     let getGstTransactions = await transactionDB.getByGstNo ({ gstNo });
        //     if(false == getGstTransactions) {
        //       if(null != invoiceNoPrefix){
        //         prefix = invoiceNoPrefix;
        //       }
        //       transactionInvoiceNo = `${prefix}${invoiceNo}`;
        //     } else {
        //       invoiceNo = getGstTransactions?.invoiceNo;
        //       if(null != invoiceNoPrefix){
        //         prefix = invoiceNoPrefix;
        //         invoiceNo = invoiceNo.replace(prefix, "");
        //         transactionInvoiceNo = `${prefix}${String(Number(invoiceNo)+1)}`;
        //       } else {
        //         transactionInvoiceNo = String(Number(invoiceNo)+1);
        //       }
        //     } 
        //     businessName = property?.businessName || "";
        // } else {
        //   isGstEnabled = 0;
        // }

        // let logo = client?.logo
        //   ? process.env.RS_LOGO_URI + client?.id + "/" + client?.logo
        //   : String(process.env.RS_DEFAULT_LOGO_URI);

        // let flatName = "";
        // if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
        //   const { name } = await flatDB.getById({ id: occupancy.flatId });
        //   flatName = name;
        // } else {
        //   flatName =
        //     occupancy.floor === "G" ? "Ground Floor" : "Floor " + occupancy.floor;
        // }

        // const receipt = await createReceiptMultipleDues({
        //   // title: transTitle,
        //   title: `${transId}_${transGId}`,
        //   roomNum: room.roomNum,
        //   propName: property.name,
        //   dueDate: moment(occupancy?.moveOutDate).format("MMM, YYYY") || null,
        //   mode: getModeName(CONSTANTS.TRANSACTION_MODES.OFFLINE),
        //   transGId,
        //   paidDate: moment().format("DD MMM, YYYY"),
        //   month:
        //     moment(occupancy?.moveOutDate).format("MMM, YYYY") ||
        //     moment().format("MMM, YYYY"),
        //   tenantName: tenant?.name || "",
        //   amount: Number(securityAdjustEntry.balance) || 0,
        //   address: `${property?.address}`,
        //   landlord: businessName || "",
        //   landlordNumber: property?.ownerMobile || "",
        //   logo: logo,
        //   "landlord-pan": "",
        //   occupancy,
        //   dueStats: [{title: "Excess Payment/Security returned", amount: securityAdjustEntry.balance}],
        //   flatName,
        //   propType: property.type,
        //   isGstEnabled,
        //   transactionInvoiceNo,
        //   gstNo,
        // });

        // if(0 != isGstEnabled && 0 != invoiceNo) {
        //   await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
        // } else {
        //   await transactionDB.updateReceipt({
        //     id: transId,
        //     receipt,
        //   });
        // }

        await ledgerDB.add({
          tenantId: securityAdjustEntry.tenantId,
          roomId: securityAdjustEntry.roomId,
          propId: securityAdjustEntry.propId,
          clientId: clientId,
          amount: securityAdjustEntry.amount,
          balance: securityAdjustEntry.balance,
          referenceId: securityAdjustEntry.referenceId,
          transactionId: securityAdjustEntry.transactionId,
          type: securityAdjustEntry.type,
          rentStartDate: securityAdjustEntry.rentStartDate,
          rentEndDate: securityAdjustEntry.rentEndDate,
          description: securityAdjustEntry.description,
          dueDate: securityAdjustEntry.dueDate,
        });

        // await ledgerDB.settleExcessPaymentAfterEviction({
        //   tenantId,
        //   clientId,
        //   transactionId: transId,
        //   description: `Paid back ₹${-securityAdjustEntry.balance} to tenant via CASH on ${moment().format("DD MMM YYYY")}`,
        // });
      }

      await moveOutDB.setTenantDue({
        tenantId: tenantId,
        tenantDues: 0,
        roomId: occupancy.roomId,
        propId: occupancy.propId,
      });

      if (false === settleDues) {
        await ledgerDB.removeByTenantIdForMoveOut({
          tenantId: tenantId,
          clientId: clientId,
        });
      }

      await moveOutDuesDB.removeAllByTenantId({
        tenantId: tenantId,
      });
    }

    const movedOutTenant = await moveOutDB.getByTenantIdAndClientIdDesc({
      clientId,
      tenantId,
    })

    await hiddenDuesDB.updateOccupancyByTenantIdAndClientId({
      tenantId,
      clientId,
      occupancyId: movedOutTenant.id,
    })

    await logTenantEvictionActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.MOVEOUT_NOW,
      occupancy.propId,
      Number(tenant.id),
      tenant.name,
      occupancy.roomId,
      ""
    );
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Electricity Reading [${electricityReading}], Tenant removed successfully`
    );

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

clients.ExtendStay = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ExtendStay";

  try {
    const { tenantId, extendedDays, duesList } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}],  Dues List Length [${duesList.length
        }], Due List [${JSON.stringify(duesList)}], Staff Id [${req.id
        }], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List Length [${duesList.length
        }], Due List [${JSON.stringify(duesList)}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    const tenant = await tenantDB.getById({ id: tenantId });

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const occupancies = await occupancyDB.getAllByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancies) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length}] No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length
      }] is All Beds Occupied [${occupancies.length > 1}]`
    );

    const occupancy = occupancies[0];

    if (occupancy.status !== CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length}], Removal of tenant is not initiated`
      );
      return res
        .status(400)
        .json({ msg: "Removal of tenant is not initiated", isSuccess: false });
    }

    // Sukhbir - MultiOccupancy
    // if (tenant.status !== CONSTANTS.TENANT_STATUS.MOVING_OUT) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length}], Tenant is not moving out`
    //   );
    //   return res
    //     .status(400)
    //     .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    // }

    const bed = await bedDB.getById({ id: occupancy.bedId });

    if (bed?.status === CONSTANTS.BED_STATUS.RESERVED) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length}], Bed is reserved for other tenant`
      );
      return res.status(400).json({
        msg: "Please first cancel the reservation of other tenant",
        isSuccess: false,
      });
    }

    if (bed?.status !== CONSTANTS.BED_STATUS.MOVING_OUT) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length}], Bed is not moving out`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const moveOutDetails = await moveOutDB.getAllByMultiDetails({
      clientId,
      tenantId,
      propId: occupancy.propId,
      roomId: occupancy.roomId,
      status: CONSTANTS.MOVE_OUT_STATUS.INITIATED,
    });

    if (!moveOutDetails) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length}], No Move Out Details Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const singleMoveOutDetails = moveOutDetails[0];

    if (!singleMoveOutDetails.moveOutDate) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length}], No Move Out Date Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let newMoveOutDate = moment(singleMoveOutDetails.moveOutDate)
      .add(Number(extendedDays), "days")
      .format("YYYY-MM-DD HH:mm:ss");

    let dueAmount = 0;
    for (const dueObj of duesList) {
      const { amount, type } = dueObj;
      const referenceId = await generateLedgerReferenceId({ clientId });
      let dueDescription = getDueDescription(type);
      if (type === CONSTANTS.DUES_TYPES.RENT) {
        dueDescription = "Advance Rent";
      }

      const dueId = await duesDB.addWithStartEndDateX({
        tenantId,
        amount,
        occupancyId: occupancy.id,
        roomId: occupancy.roomId,
        propId: occupancy.propId,
        clientId,
        rentStartDate: moment(singleMoveOutDetails.moveOutDate).format(
          "YYYY-MM-DD"
        ),
        rentEndDate: moment(newMoveOutDate).format("YYYY-MM-DD"),
        dueDate: moment(singleMoveOutDetails.moveOutDate).format(
          "YYYY-MM-DD HH:mm:ss"
        ),
        type,
        balance: amount,
        ledgerReferenceId: referenceId,
        description: dueDescription,
        title: `${dueDescription} for stay extd`,
      });

      await ledgerDB.add({
        tenantId,
        roomId: occupancy.roomId,
        propId: occupancy.propId,
        clientId,
        amount,
        balance: amount,
        referenceId: referenceId,
        transactionId: null,
        type,
        rentStartDate: moment(singleMoveOutDetails.moveOutDate).format(
          "YYYY-MM-DD"
        ),
        rentEndDate: moment(newMoveOutDate).format("YYYY-MM-DD"),
        description: `${dueDescription} for ${moment(
          singleMoveOutDetails.moveOutDate
        ).format("DD MMM YY")} to ${moment(newMoveOutDate).format(
          "DD MMM YY"
        )}`,
        dueDate: moment(singleMoveOutDetails.moveOutDate).format(
          "YYYY-MM-DD HH:mm:ss"
        ),
      });

      dueAmount += amount;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length}], Due Id [${dueId}], Due Added Successfully`
      );
    }

    await moveOutDB.updateTenantDue({
      tenantId: tenantId,
      tenantDues: dueAmount,
      propId: occupancy.propId,
      roomId: occupancy.roomId,
    });

    for (const occupancy of occupancies) {
      await occupancyDB.extendStay({
        id: occupancy.id,
        moveOutDate: newMoveOutDate,
      });
    }

    for (const singleMoveOut of moveOutDetails) {
      await moveOutDB.extendStay({
        id: singleMoveOut.id,
        moveOutDate: newMoveOutDate,
      });
    }


    await logTenantEvictionActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.EXTEND_STAY,
      occupancy.propId,
      Number(tenant.id),
      tenant.name,
      occupancy.roomId,
      newMoveOutDate
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Extended Days [${extendedDays}], Dues List [${duesList.length
      }], Old MoveOut Date [${moment(singleMoveOutDetails.moveOutDate).format(
        "YYYY-MM-DD HH:mm:ss"
      )}], New MoveOut Date [${newMoveOutDate}], Stay of tenant extended successfully`
    );

    return res.status(200).json({
      msg: "Stay of tenant extended 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,
    });
  }
};

clients.UpdateRegId = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "UpdateRegId";

  try {
    const clientId = req.id;
    const platform = req.platform;
    const { regId, deviceName = null } = req.body;

    log.info(`[${C}], [${F}], Client Id [${clientId}], Reg Id [${regId}], Device Name [${deviceName}], Platform [${platform}]`);

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

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

    await clientDB.updateRegId({ id: clientId, regId });
    // await clientDB.addRegId({ 
    //   id: clientId, 
    //   regId, 
    //   deviceName: deviceName || null, 
    //   platform: Number(platform) ? Number(platform) : 1, 
    // });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Reg Id [${regId}], Reg Id updated successfully`
    );

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

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

clients.UpdateTenantLastReminded = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Client Controller";
  const F = "UpdateTenantLastReminded";

  try {
    const { tenantId, dueId = 0 } = req.body;

    const userType = req.userType;

    let clientId = req.id;
    let movedOutTenant = false;

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

      clientId = staff.clientId;

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

    if (Number(tenantId) === 0) {
      let rentRemiderScript = process.env.RENT_REMIDER_PATH;
      exec(`php ${rentRemiderScript} ${clientId} > /dev/null 2>&1 &`);

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Last reminded datetime updated successfully for all tenants`
      );

      return res.status(200).json({
        msg: "Reminders sent successfully to all tenants",
        isSuccess: true,
      });
    }

    if (!Number(tenantId)) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Invalid Tenant Id`
      );
      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}], TenantId [${tenantId}], No Client Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

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

    // const occupancy = await occupancyDB.getByTenantId({
    //   tenantId,
    // });
    //Pallav - Multi tenant senerio
    let occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });

    if (!occupancy) {
      occupancy = await moveOutDB.getByTenantIdAndClientId({
        tenantId,
        clientId,
      });
      if (!occupancy) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], No Occupancy Found`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }
      movedOutTenant = true;
    }

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

    const room = await roomDB.getById({
      id: occupancy.roomId,
    });

    let roomName = "";

    if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
      const flat = await flatDB.getById({
        id: occupancy.flatId,
      });
      roomName += flat.name;
    } else {
      roomName += occupancy.floor;
    }

    roomName += ` (${room.roomNum})`;

    let { totalDues, dueDate } = await duesDB.getTotalDuesForTenant({
      tenantId: tenant.id,
      clientId,
    });
    if (totalDues === null || Number(totalDues) < 1) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Mobile [${tenant.mobile}], Total Dues [${totalDues}], Due Date [${dueDate}], Tenant has no dues to pay.`
      );
      return res.status(400).json({
        msg: "Due already settled for tenant",
        isSuccess: false,
      });
    }
    dueDate = moment(dueDate).format("YYYY-MM-DD");
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Mobile [${tenant.mobile}], Total Dues [${totalDues}], Due Date [${dueDate}]`
    );

    let remindedToday = await tenantDB.remindedToday({
      id: tenantId,
    });

    if (movedOutTenant) {
      remindedToday = await moveOutDB.remindedToday({
        clientId,
        tenantId,
      });
    } else {
      remindedToday = await occupancyDB.remindedToday({
        clientId,
        tenantId,
      });
    }

    const propSettings = await settingsDB.getByClientIdAndPropId({
      clientId: client.id,
      propId: property.id,
    });

    let footerText = propSettings.footer || "The Kipinn Team";
    let sendManualReminder = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.SEND_RENT_REMINDER,
    });
    let manualReminder = 0;
    if (sendManualReminder) {
      if (sendManualReminder?.value === null || sendManualReminder?.value === '') {
        manualReminder = 0;
      } else {
        manualReminder = Number(sendManualReminder?.value) || 0;
      }
    }
    //manualReminder = 1;
    let replyMessage = "";
    if (
      process.env.WEB_LINK_ENABLED === "true" &&
      property.isOnlinePaymentEnabled === 1 &&
      occupancy.isOnlinePaymentEnabled === 1
    ) {
      //const paymentLink = `${process.env.PAYU_WEB_CHECKOUT_URL}/${tenant.gId}`;
      //Pallav - Multi tenant scenario
      let paymentLink = `${process.env.PAYU_WEB_CHECKOUT_URL}/${occupancy.gId}`;
      if (client?.paymentLinkBaseUrl && client?.paymentLinkBaseUrl.trim() !== "") {
        paymentLink = `${client?.paymentLinkBaseUrl.trim()}/${occupancy.gId}`;
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Mobile [${tenant.mobile}], Payment Link [${paymentLink}]`
      );
      if (0 === manualReminder) {
        if (remindedToday) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Already Reminded Today`
          );

          return res.status(200).json({
            msg: "Tenant reminder sent successfully",
            isSuccess: true,
          });
        } else {
          let whatsappAgreegrator = await clientConfigDB.getClientConfig({
            clientId,
            provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
            type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.WHATSAPP_AGGREGATOR
          });

          if (whatsappAgreegrator && Number(whatsappAgreegrator?.value) === CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL) {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending Through Message Central`
            );
            const bodyValues: Record<string, string> = {
              body_1: `${tenant?.name}`,
              body_2: `${property.name}`,
              body_3: `${roomName}`,
              body_4: `${totalDues}`,
              body_5: `${paymentLink.replace(/_/g, '%5F').replace(/-/g, '%2D')}`,
              body_6: `${footerText.replace("Team", "").trim()}`
            };
            await sendWhatsappMC(
              tenant?.mobile,
              Number(clientId),
              Number(occupancy.propId),
              bodyValues,
              CONSTANTS.WHATSAPP_TEMPLATE_TYPES.TENANT.DUE_WITH_LINK,
              CONSTANTS.USER_TYPE.TENANT,
              CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL
            );
          } else {
            let whatsAppProvider = await clientConfigDB.getClientConfig({
              clientId,
              provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
              type: CONSTANTS.CLIENT_CONFIG_TYPE.KEY,
            });
            if (whatsAppProvider && whatsAppProvider?.value != '') {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending Through Chat Mitra`
              );
              let bodyValues = [
                { type: "text", text: `${tenant?.name}` },
                { type: "text", text: `${property.name}` },
                { type: "text", text: `${roomName}` },
                { type: "text", text: `${totalDues}` },
                { type: "text", text: `${paymentLink.replace(/_/g, '%5F').replace(/-/g, '%2D')}` },
                { type: "text", text: `${footerText.replace("Team", "").trim()}` },
              ];
              sendWhatsapp(
                tenant?.mobile,
                tenant.name,
                Number(clientId),
                Number(occupancy.propId),
                bodyValues,
                CONSTANTS.WHATSAPP_TEMPLATE_TYPES.TENANT.DUE_WITH_LINK,
                CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA
              );
            } else {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending Through Intrakt`
              );
              sendWhatsappTenantSingleDue(
                tenant.mobile,
                tenant.name,
                property.name,
                roomName,
                totalDues,
                paymentLink,
                footerText,
                Number(clientId)
              );
            }
          }
        }
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], [With Link], Manual Reminder message in response`
        );
        if (Number(dueId) != 0) {
          let dueData = await duesDB.getById({ id: Number(dueId) });
          if (dueData) {
            totalDues = dueData?.balance;
            dueDate = dueData?.dueDate;
            paymentLink = `${paymentLink}/${dueId}`;
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Single Due Amount [${totalDues}], sharing message`
            );
          }
        }
        replyMessage = CONSTANTS.MSG.SHARE_DUE_PAYMENT_WITH_LINK
          .replace("{#var1#}", tenant?.name || "")
          .replace("{#var2#}", totalDues.toString())
          .replace("{#var3#}", paymentLink)
          .replace("{#var4#}", footerText || "");
        return res.status(200).json({
          msg: "Tenant reminder message shared successfully",
          tenantMobile: tenant.mobile,
          sendManualReminder: manualReminder,
          whatsAppMsg: replyMessage,
          isSuccess: true,
        });
      }
    } else {
      if (0 === manualReminder) {
        if (remindedToday) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Already Reminded Today`
          );
          return res.status(200).json({
            msg: "Tenant reminder sent successfully",
            isSuccess: true,
          });
        }
        sendWhatsappTenantDues(
          tenant.mobile,
          tenant.name,
          totalDues,
          dueDate,
          footerText,
          Number(clientId)
        );
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], [No Link], Tenant manual reminder message shared `
        );
        replyMessage = CONSTANTS.MSG.SHARE_DUE_PAYMENT_WITHOUT_LINK
          .replace("{#var1#}", tenant?.name || "")
          .replace("{#var2#}", totalDues.toString())
          .replace("{#var3#}", footerText || "");
        return res.status(200).json({
          msg: "Tenant reminder message shared successfully",
          tenantMobile: tenant.mobile,
          whatsAppMsg: replyMessage,
          sendManualReminder: manualReminder,
          isSuccess: true,
        });
      }
    }

    await tenantDB.updateLastReminded({
      id: tenantId,
      lastRemindedOn: moment().format("YYYY-MM-DD HH:mm:ss"),
    });

    if (movedOutTenant) {
      await moveOutDB.updateLastReminded({
        clientId,
        tenantId,
        lastRemindedOn: moment().format("YYYY-MM-DD HH:mm:ss"),
      });
    } else {
      await occupancyDB.updateLastReminded({
        clientId,
        tenantId,
        lastRemindedOn: moment().format("YYYY-MM-DD HH:mm:ss"),
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Last reminded datetime updated successfully`
    );

    return res.status(200).json({
      msg: "Tenant reminder sent successfully",
      sendManualReminder: manualReminder,
      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,
    });
  }
};

clients.GetTenantWhoseAgrementExpiring = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Client Controller";
  const F = "GetTenantWhoseAgrementExpiring";

  try {
    let { e, pageNum, s, days = 90 } = req.query;
    const userType = req.userType;

    if (days === "" || days === undefined || days === null || days === 'undefined' || !Number(days)) days = 90;

    const isExpired = e === "1";

    log.info(`[${C}], [${F}], Days [${days}], isExpired [${isExpired}], PageNum [${pageNum}], Search Value [${s}]`);

    // let clientId = req.id;

    let list = [];
    let tenantCount = 0;
    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" : "Client Id"} [${req.id}], isExpired [${isExpired}], PageNum [${pageNum}], Search Value [${s}], ${isPartner ? "Partner Requested...." : "Client Requested...."}`
      );

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

      if (isExpired) {
        if (s && s !== "" && s !== 'undefined') {
          list = await occupancyDB.getTenantWhoseAgrementExpiredBySearch({
            clientId,
            pageNum: Number(pageNum),
            limit,
            searchVal: s,
          });
        } else {
          list = await occupancyDB.getTenantWhoseAgrementExpired({
            clientId,
            pageNum: Number(pageNum),
            limit,
          });
        }
        tenantCount = await occupancyDB.getTenantWhoseAgrementExpiredCount({
          clientId,
        });
      } else {
        if (s && s !== "" && s !== 'undefined') {
          list = await occupancyDB.getTenantWhoseAgrementExpiringBySearch({
            clientId,
            pageNum: Number(pageNum),
            limit,
            searchVal: s,
          });
        } else {
          list = await occupancyDB.getTenantWhoseAgrementExpiring({
            clientId,
            pageNum: Number(pageNum),
            limit,
            days: Number(days),
          });
        }
        tenantCount = await occupancyDB.getTenantWhoseAgrementExpiringCount({
          clientId,
          days: Number(days),
        });
      }
    } else {
      const staffId = req.id;
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], isExpired [${isExpired}], PageNum [${pageNum}], Search Value [${s}], Staff Requested....`
      );

      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Staff Id [${staffId}], isExpired [${isExpired}], PageNum [${pageNum}], Search Value [${s}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

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

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

        if (isExpired) {
          if (s && s !== "" && s !== 'undefined') {
            list = await occupancyDB.getTenantWhoseAgrementExpiredBySearchForStaff({
              clientId,
              propertiesIds,
              pageNum: Number(pageNum),
              limit,
              searchVal: s,
            });
          } else {
            list = await occupancyDB.getTenantWhoseAgrementExpiredForStaff({
              clientId,
              propertiesIds,
              pageNum: Number(pageNum),
              limit,
            });
          }
          tenantCount = await occupancyDB.getTenantWhoseAgrementExpiredCountForStaff({
            clientId,
            propertiesIds,
          });
        } else {
          if (s && s !== "" && s !== 'undefined') {
            list = await occupancyDB.getTenantWhoseAgrementExpiringBySearchForStaff({
              clientId,
              propertiesIds,
              pageNum: Number(pageNum),
              limit,
              searchVal: s,
            });
          } else {
            list = await occupancyDB.getTenantWhoseAgrementExpiringForStaff({
              clientId,
              propertiesIds,
              pageNum: Number(pageNum),
              limit,
              days: Number(days),
            });
          }
          tenantCount = await occupancyDB.getTenantWhoseAgrementExpiringCountForStaff({
            clientId,
            propertiesIds,
            days: Number(days),
          });
        }
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], isExpired [${isExpired}], PageNum [${pageNum}], Search Value [${s}], List sent successfully`
    );

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

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,

      isSuccess: false,
    });
  }
};

clients.RenewRentAgreement = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "RenewRentAgreement";

  try {
    const {
      occupancyId,
      noticePeriod,
      lockInPeriod,
      rent,
      agreementPeriod,
      agreementStartDate,
      agreementCharges = 0,
      security,
    } = req.body;

    const userType = req.userType;

    let doneByName = "";

    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Occupancy Id [${occupancyId}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Rent [${rent}], Agreement Period [${agreementPeriod}], Agreement Start Date [${agreementStartDate}], Security [${security}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;
      doneByName = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Occupancy Id [${occupancyId}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Rent [${rent}], Agreement Period [${agreementPeriod}], Agreement Start Date [${agreementStartDate}], Security [${security}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Occupancy Id [${occupancyId}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Rent [${rent}], Agreement Period [${agreementPeriod}], Agreement Start Date [${agreementStartDate}], Security [${security}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Occupancy Id [${occupancyId}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Rent [${rent}], Agreement Period [${agreementPeriod}], Agreement Start Date [${agreementStartDate}], Security [${security}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      const curClient = await clientDB.getById({ id: req.parentClientId });
      doneByName = curClient?.name || client?.name || "";
    }

    const occupancy = await occupancyDB.getById({ id: occupancyId });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Occupancy Id [${occupancyId}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Rent [${rent}], Agreement Period [${agreementPeriod}], Agreement Start Date [${agreementStartDate}], Security [${security}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const tenant = await tenantDB.getById({ id: occupancy.tenantId });
    if (!tenant) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Occupancy Id [${occupancyId}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Rent [${rent}], Agreement Period [${agreementPeriod}], Agreement Start Date [${agreementStartDate}], Security [${security}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const property = await propertyDB.getById({ id: occupancy.propId });
    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Occupancy Id [${occupancyId}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Rent [${rent}], Agreement Period [${agreementPeriod}], Agreement Start Date [${agreementStartDate}], Security [${security}], No Property Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const room = await roomDB.getById({ id: occupancy.roomId });
    if (!room) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Occupancy Id [${occupancyId}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Rent [${rent}], Agreement Period [${agreementPeriod}], Agreement Start Date [${agreementStartDate}], Security [${security}], No Room Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const oldAgreement = await documentDB.getIDByType({
      tenantId: occupancy.tenantId,
      clientId,
      type: CONSTANTS.DOCUMENT_TYPES.RENT_AGREEMENT,
      moveOut: 0,
    });

    await rentAgreementRecordDB.add({
      clientId,
      tenantId: occupancy.tenantId,
      propId: occupancy.propId,
      roomId: occupancy.roomId,
      bedId: occupancy.bedId,
      floor: occupancy.floor,
      rent,
      prevRent: occupancy.rent,
      noticePeriod,
      prevNoticePeriod: occupancy.noticePeriod,
      lockInPeriod,
      prevLockInPeriod: occupancy.lockInPeriod,
      agreementPeriod,
      prevAgreementPeriod: occupancy.agreementPeriod,
      agreementStartDate,
      prevAgreementStartDate: occupancy.agreementStartDate,
      security: Number(security) || Number(occupancy.security),
      prevSecurity: Number(occupancy.security),
      doneByName: doneByName || "",
      agreementUrl: oldAgreement && oldAgreement?.value ? oldAgreement?.value : null,
    });

    await occupancyDB.renewAgreementByTenantIdAndClientId({
      tenantId: occupancy.tenantId,
      clientId,
      agreementStartDate,
      noticePeriod,
      lockInPeriod,
      rent,
      agreementPeriod,
      isRentAgreementSigned: 0,
      security: Number(security) || Number(occupancy.security),
    });

    await documentDB.updateOldAgreementByTenantId({
      tenantId: occupancy.tenantId,
      clientId,
      status: 3,
      type: CONSTANTS.DOCUMENT_TYPES.RENT_AGREEMENT,
    });

    if (Number(agreementCharges) && Number(agreementCharges) > 0) {
      let referenceId = await generateLedgerReferenceId({ clientId });
      await AddDues(
        occupancy.tenantId,
        agreementCharges,
        CONSTANTS.DUES_TYPES.AGREEMENT_CHARGE,
        occupancy,
        occupancy.clientId,
        moment(agreementStartDate).startOf("month").format("YYYY-MM-DD"),
        moment(agreementStartDate).endOf("month").format("YYYY-MM-DD"),
        moment(agreementStartDate).startOf("month").format("YYYY-MM-DD"),
        0,
        "Agreement charges",
        "Agreement charges",
        "Agreement charges",
        referenceId,
      );
    }

    if (Number(security)) {
      await occupancyDB.updateSecurity({
        tenantId: occupancy.security,
        clientId,
        security,
      });
    }

    if (Number(security) && Number(occupancy.security) < Number(security)) {
      const securityDiff = Number(security) - Number(occupancy.security);
      const isSecurityAdded = await ledgerDB.getByTenantIdAndDueType({
        tenantId: occupancy.tenantId,
        clientId,
        type: CONSTANTS.DUES_TYPES.SECURITY,
      });
      if (!isSecurityAdded) {
        const referenceId = await generateLedgerReferenceId({ clientId });
        await duesDB.addWithStartEndDateX({
          tenantId: occupancy.tenantId,
          amount: security,
          occupancyId: occupancy.id,
          roomId: occupancy.roomId,
          propId: occupancy.propId,
          clientId,
          rentStartDate: null,
          rentEndDate: null,
          type: CONSTANTS.DUES_TYPES.SECURITY,
          dueDate: moment().format("YYYY-MM-DD"),
          balance: security,
          ledgerReferenceId: referenceId,
          description: getDueDescription(CONSTANTS.DUES_TYPES.SECURITY),
          title: "Security",
        });
        await ledgerDB.add({
          tenantId: occupancy.tenantId,
          roomId: occupancy.roomId,
          propId: occupancy.propId,
          clientId,
          amount: security,
          balance: security,
          referenceId: referenceId,
          transactionId: null,
          type: CONSTANTS.DUES_TYPES.SECURITY,
          rentStartDate: null,
          rentEndDate: null,
          dueDate: moment().format("YYYY-MM-DD"),
          description: `${getDueDescription(CONSTANTS.DUES_TYPES.SECURITY)} for ${moment().format("DD MMM, YY")}`,
          title: "Security",
        });
      } else {
        const securityDue = await duesDB.getByTenantIdAndLedgerReferenceId({
          tenantId: occupancy.tenantId,
          ledgerReferenceId: isSecurityAdded.referenceId,
        });
        if (securityDue) {
          await duesDB.updateAmount({
            id: securityDue[0].id,
            amount: security,
          });
          await duesDB.addBalance({ id: securityDue[0].id, balance: securityDiff });
          await ledgerDB.updateAmount({
            tenantId: securityDue[0].tenantId,
            clientId: clientId,
            referenceId: securityDue[0].ledgerReferenceId,
            type: securityDue[0].type,
            amount: security,
          });
          // await ledgerDB.updateBalance({
          //   tenantId: occupancy.tenantId,
          //   clientId: clientId,
          //   referenceId: securityDue[0].ledgerReferenceId,
          //   type: securityDue[0].type,
          //   balance: security,
          // });
          await ledgerDB.addBalance({
            tenantId: occupancy.tenantId,
            clientId: clientId,
            referenceId: securityDue[0].ledgerReferenceId,
            type: securityDue[0].type,
            balance: securityDiff,
          });
        } else {
          const ledgerRecords = await ledgerDB.getDueByLedgerReferenceId({
            referenceId: isSecurityAdded.referenceId,
          });
          if (ledgerRecords && ledgerRecords.length > 0) {
            let isFirstEntry = true;
            for (let record of ledgerRecords) {
              record.description = record.description.replace(/final/i, "").trim();
              if (isFirstEntry) {
                isFirstEntry = false;
                continue;
              }
              await ledgerDB.updateDescription({
                id: record.id,
                description: `Partial ${record.description}`
              });
            }
          }
          await duesDB.addWithStartEndDateX({
            tenantId: occupancy.tenantId,
            amount: securityDiff,
            occupancyId: occupancy.id,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            rentStartDate: isSecurityAdded.rentStartDate,
            rentEndDate: isSecurityAdded.rentEndDate,
            type: CONSTANTS.DUES_TYPES.SECURITY,
            dueDate: isSecurityAdded.dueDate,
            balance: securityDiff,
            ledgerReferenceId: isSecurityAdded.referenceId,
            description: isSecurityAdded.description,
            title: "Security",
          });
          await ledgerDB.updateAmount({
            tenantId: isSecurityAdded.tenantId,
            clientId: clientId,
            referenceId: isSecurityAdded.referenceId,
            type: isSecurityAdded.type,
            amount: isSecurityAdded.amount + securityDiff,
          });
          await ledgerDB.addBalance({
            tenantId: occupancy.tenantId,
            clientId: clientId,
            referenceId: isSecurityAdded.referenceId,
            type: isSecurityAdded.type,
            balance: securityDiff,
          });
        }
      }
    }

    const propSettings = await settingsDB.getByClientIdAndPropId({
      clientId: client.id,
      propId: occupancy.propId,
    });

    let footerText = propSettings.footer || "The Kipinn Team";

    const endDate = moment(agreementStartDate).add(Number(agreementPeriod), "months").format("YYYY-MM-DD");
    const occupancyName = `${property.name}_${room.roomNum}`;

    sendWhatsappTenantRentAgreementRenew(
      tenant.mobile,
      tenant.name,
      occupancyName,
      rent,
      agreementStartDate,
      endDate,
      footerText,
      Number(clientId),
    );

    logTenantAgreementRenewActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.RENEW_AGREEMENT,
      occupancy.propId,
      Number(tenant.id),
      tenant.name,
      occupancy.roomId,
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Occupancy Id [${occupancyId}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Rent [${rent}], Agreement Period [${agreementPeriod}], Agreement Start Date [${agreementStartDate}], Security [${security}], Agreement Renewed Successfully`
    );

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

clients.EditRent = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditRent";

  try {
    const { tenantId, rent } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

    const tenant = await tenantDB.getById({ id: tenantId });

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

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Rent [${rent}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await occupancyDB.updateRent({
      tenantId,
      clientId,
      rent,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Rent [${rent}], Rent Updated Successfully`
    );

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

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

clients.EditTenantName = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditTenantName";

  try {
    const { tenantId, name } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

    const tenant = await tenantDB.getById({ id: tenantId });

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

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Name [${name}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await tenantDB.updateNameGender({ name, id: tenantId });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Name [${name}], Tenant Name Updated Successfully`
    );

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

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

clients.EditSecurity = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditSecurity";

  try {
    const { tenantId, security, isSecurityConfirmed } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Security [${security}], Update Dues [${isSecurityConfirmed}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Security [${security}], Update Dues [${isSecurityConfirmed}], Client Requested....`
      );
    }

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

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

    const tenant = await tenantDB.getById({ id: tenantId });

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

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Security [${security}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await occupancyDB.updateSecurity({
      tenantId,
      clientId,
      security,
    });

    const updatedOccupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });

    if (isSecurityConfirmed) {
      const isSecurityAdded = await ledgerDB.getByTenantIdAndDueType({
        tenantId,
        clientId,
        type: CONSTANTS.DUES_TYPES.SECURITY,
      });

      let securityDiff = Number(security) - Number(occupancy.security);
      if (Number(securityDiff) >= 0) {
        // below code has functionality for when security is descreasing if required remove abvoe > 0 check
        if (!isSecurityAdded) {
          const referenceId = await generateLedgerReferenceId({ clientId });
          await duesDB.addWithStartEndDateX({
            tenantId,
            amount: security,
            occupancyId: occupancy.id,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            rentStartDate: null,
            rentEndDate: null,
            type: CONSTANTS.DUES_TYPES.SECURITY,
            dueDate: moment().format("YYYY-MM-DD"),
            balance: security,
            ledgerReferenceId: referenceId,
            description: getDueDescription(CONSTANTS.DUES_TYPES.SECURITY),
            title: "Security",
          });
          await ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            amount: security,
            balance: security,
            referenceId: referenceId,
            transactionId: null,
            type: CONSTANTS.DUES_TYPES.SECURITY,
            rentStartDate: null,
            rentEndDate: null,
            dueDate: moment().format("YYYY-MM-DD"),
            description: `${getDueDescription(CONSTANTS.DUES_TYPES.SECURITY)} for ${moment().format("DD MMM, YY")}`,
            title: "Security",
          });
        } else if (Number(security) === 0) {
          //delete security
          const securityDue = await duesDB.getByTenantIdAndLedgerReferenceId({
            tenantId,
            ledgerReferenceId: isSecurityAdded.referenceId,
          });
          if (securityDue) {
            if (securityDue.amount === securityDue.balance) {
              await ledgerDB.remove({
                referenceId: securityDue.ledgerReferenceId,
              });
            } else {
              await ledgerDB.addBalance({
                tenantId: securityDue.tenantId,
                clientId,
                referenceId: securityDue.ledgerReferenceId,
                type: securityDue.type,
                balance: -securityDue.balance,
              });
              await ledgerDB.updateAmount({
                tenantId: securityDue.tenantId,
                clientId,
                referenceId: securityDue.ledgerReferenceId,
                type: securityDue.type,
                amount: securityDue.amount - (securityDue.balance - security),
              });
            }
          }
        } else {
          const securityDue = await duesDB.getByTenantIdAndLedgerReferenceId({
            tenantId,
            ledgerReferenceId: isSecurityAdded.referenceId,
          });

          if (securityDue) {
            //security is partially paid or not paid
            if ((updatedOccupancy.secuirty < occupancy.secuirty) && (securityDue.balance > security)) {
              //nerw security is less than old
              await duesDB.removeDue({ id: securityDue.id })
              await ledgerDB.addBalance({
                tenantId: securityDue.tenantId,
                clientId,
                referenceId: securityDue.ledgerReferenceId,
                type: securityDue.type,
                balance: -securityDue.balance,
              });
              await ledgerDB.updateAmount({
                tenantId: securityDue.tenantId,
                clientId,
                referenceId: securityDue.ledgerReferenceId,
                type: securityDue.type,
                amount: securityDue.amount - (securityDue.balance - security),
              });
            } else if (securityDue[0].amount === securityDue[0].balance) {
              //due not paid
              await duesDB.updateAmount({
                id: securityDue[0].id,
                amount: security,
              });
              await duesDB.updateBalance({ id: securityDue[0].id, balance: security });
              await ledgerDB.updateAmount({
                tenantId: securityDue[0].tenantId,
                clientId: clientId,
                referenceId: securityDue[0].ledgerReferenceId,
                type: securityDue[0].type,
                amount: security,
              });
              await ledgerDB.updateBalance({
                tenantId: tenantId,
                clientId: clientId,
                referenceId: securityDue[0].ledgerReferenceId,
                type: securityDue[0].type,
                balance: security,
              });
            } else {
              await duesDB.updateAmount({
                id: securityDue[0].id,
                amount: securityDue[0].amount + securityDiff,
              });
              await duesDB.updateBalance({ id: securityDue[0].id, balance: securityDue[0].balance + securityDiff });
              await ledgerDB.updateAmount({
                tenantId: securityDue[0].tenantId,
                clientId: clientId,
                referenceId: securityDue[0].ledgerReferenceId,
                type: securityDue[0].type,
                amount: securityDue[0].amount + securityDiff,
              });
              await ledgerDB.addBalance({
                tenantId: tenantId,
                clientId: clientId,
                referenceId: securityDue[0].ledgerReferenceId,
                type: securityDue[0].type,
                balance: securityDiff,
              });
            }
          } else if (!securityDue && (Number(occupancy.security) < Number(updatedOccupancy.security))) {
            const ledgerRecords = await ledgerDB.getDueByLedgerReferenceId({
              referenceId: isSecurityAdded.referenceId,
            });
            if (ledgerRecords && ledgerRecords.length > 0) {
              for (let record of ledgerRecords) {
                record.description = record.description.replace(/final/i, "").trim();
                await ledgerDB.updateDescription({
                  id: record.id,
                  description: `Partial ${record.description}`
                });
              }
            }
            await duesDB.addWithStartEndDateX({
              tenantId,
              amount: securityDiff,
              occupancyId: occupancy.id,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId,
              rentStartDate: isSecurityAdded.rentStartDate,
              rentEndDate: isSecurityAdded.rentEndDate,
              type: CONSTANTS.DUES_TYPES.SECURITY,
              dueDate: isSecurityAdded.dueDate,
              balance: securityDiff,
              ledgerReferenceId: isSecurityAdded.referenceId,
              description: isSecurityAdded.description,
              title: "Security",
            });
            await ledgerDB.updateAmount({
              tenantId: isSecurityAdded.tenantId,
              clientId: clientId,
              referenceId: isSecurityAdded.referenceId,
              type: isSecurityAdded.type,
              amount: isSecurityAdded.amount + securityDiff,
            });
            await ledgerDB.addBalance({
              tenantId: tenantId,
              clientId: clientId,
              referenceId: isSecurityAdded.referenceId,
              type: isSecurityAdded.type,
              balance: securityDiff,
            });
          }
        }
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Security [${security}], Update Dues [${isSecurityConfirmed}], Security Updated Successfully`
    );

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

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

clients.EditAgreementPeriod = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditAgreementPeriod";

  try {
    const { tenantId, agreementPeriod } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

    const tenant = await tenantDB.getById({ id: tenantId });

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

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Agreement Period [${agreementPeriod}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await occupancyDB.updateAgreementPeriod({
      tenantId,
      clientId,
      agreementPeriod,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Agreement Period [${agreementPeriod}], Agreement Period Updated Successfully`
    );

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

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

clients.EditMoveInDate = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditMoveInDate";

  try {
    const { tenantId, moveInDate } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move-In Date [${moveInDate}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move-In Date [${moveInDate}], Client Requested....`
      );
    }

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

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

    const tenant = await tenantDB.getById({ id: tenantId });

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move-In Date [${moveInDate}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move-In Date [${moveInDate}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await occupancyDB.updateMoveInDate({
      tenantId,
      clientId,
      moveInDate,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move-In Date [${moveInDate}], Move-In Date Updated Successfully`
    );

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

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

clients.EditFine = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditFine";

  try {
    const { tenantId, fine } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

    const tenant = await tenantDB.getById({ id: tenantId });

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

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Fine [${fine}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await occupancyDB.updateFine({ id: occupancy.id, fine });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Fine [${fine}], Fine Updated Successfully`
    );

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

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

clients.EditGracePeriod = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditGracePeriod";

  try {
    const { tenantId, gracePeriod } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

    const tenant = await tenantDB.getById({ id: tenantId });

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

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Grace Period [${gracePeriod}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await occupancyDB.updateGracePeriod({ id: occupancy.id, gracePeriod });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Grace Period [${gracePeriod}], Grace Period Updated Successfully`
    );

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

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

clients.ToggleFine = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ToggleFine";

  try {
    const { tenantId } = req.body;

    if (!Number(tenantId)) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Not a Valid Tenant Id`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

    const 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 });
    }

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (Number(occupancy.isFineEnabled))
      await occupancyDB.disableFine({ clientId, tenantId });
    else await occupancyDB.enableFine({ clientId, tenantId });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Fine ${Number(occupancy.isFineEnabled) ? "Disabled" : "Enabled"
      } Successfully`
    );

    return res.status(200).json({
      msg: `Fine ${Number(occupancy.isFineEnabled) ? "Disabled" : "Enabled"
        } 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,
    });
  }
};

clients.UploadID = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "UploadID";

  const files = req.files as Express.Multer.File[];
  const removeTmpImages = async () => {
    for (const file of files) {
      try {
        await fsPromises.unlink(file.path);
      } catch (err) { }
    }
  };

  try {
    const { tenantId } = req.body;
    const userType = req.userType;

    if (!Number(tenantId)) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Not a Valid Tenant Id`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

    if (!files) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No files uploaded`
      );
      return res
        .status(400)
        .json({ msg: "No files uploaded", isSuccess: false });
    }

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

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

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId: tenant?.id,
      clientId: client?.id,
    });

    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`
      );

      await removeTmpImages();

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

    const folderName = `tenant_${occupancy.tenantId}`;
    const folderPath = `uploads/documents/client_${occupancy.clientId}/prop_${occupancy.propId}/room_${occupancy.roomId}_bed_${occupancy.bedId}/${folderName}`;
    const urlBase = folderPath.replace(
      "uploads/",
      `${process.env.UPLOAD_PATH}/`
    );

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

    const oldFrontPath = `uploads/tmp/${files[0].filename}`;
    let oldBackPath = "";
    if (files[1]) oldBackPath = `uploads/tmp/${files[1].filename}`;

    const isExists = await documentDB.getIDByType({
      tenantId,
      clientId: occupancy?.clientId,
      type: CONSTANTS.DOCUMENT_TYPES.AADHAAR,
      moveOut: 0,
    });

    const ext = files[0].mimetype.split("/")[1];
    const fileName = `Id_document.${ext}`;

    const newPath = `${folderPath}/${fileName}`;
    await fsPromises.copyFile(oldFrontPath, newPath);
    const url = `${urlBase}/${fileName}`;

    if (isExists) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Type [AADHAAR], Aadhaar Front document re-uploaded successfully`
      );

      await documentDB.updateDoc({
        tenantId,
        clientId: occupancy.clientId,
        propId: occupancy.propId,
        roomId: occupancy.roomId,
        type: CONSTANTS.DOCUMENT_TYPES.AADHAAR,
        value: url,
        id: isExists.id,
      });
    } else {
      await documentDB.addDocWithStatus({
        tenantId,
        clientId: occupancy.clientId,
        propId: occupancy.propId,
        roomId: occupancy.roomId,
        type: CONSTANTS.DOCUMENT_TYPES.AADHAAR,
        value: url,
        status: CONSTANTS.DOCUMENT_STATUS.VERIFIED,
      });

      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Type [AADHAAR], Aadhaar Front document uploaded successfully`
      );
    }

    if (oldBackPath) {
      const ext = files[1].mimetype.split("/")[1];
      const fileName = `Id_document_back.${ext}`;

      const newPath = `${folderPath}/${fileName}`;
      await fsPromises.copyFile(oldBackPath, newPath);
      const url = `${urlBase}/${fileName}`;

      const isExists = await documentDB.getIDByType({
        tenantId,
        clientId: occupancy?.clientId,
        type: CONSTANTS.DOCUMENT_TYPES.AADHAAR_BACK,
        moveOut: 0,
      });

      if (isExists) {
        await documentDB.updateDoc({
          tenantId,
          clientId: occupancy.clientId,
          propId: occupancy.propId,
          roomId: occupancy.roomId,
          type: CONSTANTS.DOCUMENT_TYPES.AADHAAR_BACK,
          value: url,
          id: isExists.id,
        });

        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Type [AADHAAR], URL [${url}], Aadhaar Front document re-uploaded successfully`
        );
      } else {
        await documentDB.addDocWithStatus({
          tenantId,
          clientId: occupancy.clientId,
          propId: occupancy.propId,
          roomId: occupancy.roomId,
          type: CONSTANTS.DOCUMENT_TYPES.AADHAAR_BACK,
          value: url,
          status: CONSTANTS.DOCUMENT_STATUS.VERIFIED,
        });

        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Type [AADHAAR], URL [${url}], Aadhaar Back document uploaded successfully`
        );
      }
    }

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Id doc(s) uploaded sucessfully`
    );

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

    await removeTmpImages();

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

clients.UploadDocument = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "UploadDocument";

  const files = req.files as Express.Multer.File[];
  const removeTmpImages = async () => {
    for (const file of files) {
      try {
        await fsPromises.unlink(file.path);
      } catch (err) { }
    }
  };

  try {
    const { tenantId, type } = req.body;
    const userType = req.userType;

    if (!Number(tenantId)) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Not a Valid Tenant Id`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let clientId = req.id;

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Document Type [${type}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], File [${JSON.stringify(files)}], Document Type [${type}], Client Requested....`
      );
    }

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

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

    if (!files[0]) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No files uploaded`
      );
      return res
        .status(400)
        .json({ msg: "No files uploaded", isSuccess: false });
    }

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

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

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId: tenant?.id,
      clientId: client?.id,
    });

    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`
      );

      await removeTmpImages();

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

    const folderName = `tenant_${occupancy.tenantId}`;
    const folderPath = `uploads/documents/client_${occupancy.clientId}/prop_${occupancy.propId}/room_${occupancy.roomId}_bed_${occupancy.bedId}/${folderName}`;
    const urlBase = folderPath.replace(
      "uploads/",
      `${process.env.UPLOAD_PATH}/`
    );

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

    const oldFrontPath = `uploads/tmp/${files[0].filename}`;
    let oldBackPath = "";
    if (files[1]) oldBackPath = `uploads/tmp/${files[1].filename}`;

    const isExists = await documentDB.getIDByType({
      tenantId,
      clientId: occupancy?.clientId,
      type: type,
      moveOut: 0,
    });

    const ext = files[0].mimetype.split("/")[1];
    const fileName = `Id_document_${type}.${ext}`;

    const newPath = `${folderPath}/${fileName}`;
    await fsPromises.copyFile(oldFrontPath, newPath);
    const url = `${urlBase}/${fileName}`;

    if (isExists) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Type [${type}], Document re-uploaded successfully`
      );

      await documentDB.updateDoc({
        tenantId,
        clientId: occupancy.clientId,
        propId: occupancy.propId,
        roomId: occupancy.roomId,
        type: type,
        value: url,
        id: isExists.id,
      });
      if (Number(type) === CONSTANTS.DOCUMENT_TYPES.AADHAAR) {
        // await tenantDB.updateKycStatus({
        //   id: tenantId,
        //   kycStatus: CONSTANTS.KYC_STATUS.SELFI_UPLOADED,
        // });

        await occupancyDB.updateKycStatusWithClientId({
          tenantId: tenantId,
          clientId: occupancy.clientId,
          kycStatus: CONSTANTS.KYC_STATUS.SELFI_UPLOADED
        });
      }
      if (Number(type) === CONSTANTS.DOCUMENT_TYPES.RENT_AGREEMENT) {
        // Update rent agreement status for this occupancy record
        //isRentAgreementSigned 1 Signed , 0 Pending
        await occupancyDB.setRentAgreementStatus({
          tenantId: tenantId,
          clientId: occupancy.clientId,
          isRentAgreementSigned: 1
        });
        await occupancyDB.updateKycStatusWithClientId({
          tenantId: tenantId,
          clientId: occupancy.clientId,
          kycStatus: CONSTANTS.KYC_STATUS.RENT_AGREEMENT_DONE
        });
      }
      if (Number(type) === CONSTANTS.DOCUMENT_TYPES.POLICE_VERIFICATION) {
        // Update rent agreement status for this occupancy record
        //isPoliceVerified 1 Completed , 0 Pending
        await occupancyDB.setPoliceVerificationStatus({
          tenantId: tenantId,
          clientId: occupancy.clientId,
          isPoliceVerified: 1
        });
        //Not Updating KycStatus because IF PV is uploaded before KYC
        // await occupancyDB.updateKycStatusWithClientId({
        //   tenantId: tenantId,
        //   clientId: occupancy.clientId,
        //   kycStatus: CONSTANTS.KYC_STATUS.POLICE_VERIFICATION_DONE
        // });
      }
    } else {
      if (Number(type) === CONSTANTS.DOCUMENT_TYPES.AADHAAR) {
        await documentDB.addDocWithStatus({
          tenantId,
          clientId: occupancy.clientId,
          propId: occupancy.propId,
          roomId: occupancy.roomId,
          type: CONSTANTS.DOCUMENT_TYPES.AADHAAR,
          value: url,
          status: CONSTANTS.DOCUMENT_STATUS.VERIFIED,
        });

        // await tenantDB.updateKycStatus({
        //   id: tenantId,
        //   kycStatus: CONSTANTS.KYC_STATUS.SELFI_UPLOADED,
        // });

        await occupancyDB.updateKycStatusWithClientId({
          tenantId: tenantId,
          clientId: occupancy.clientId,
          kycStatus: CONSTANTS.KYC_STATUS.SELFI_UPLOADED
        });
      } else {
        if (Number(type) === CONSTANTS.DOCUMENT_TYPES.RENT_AGREEMENT) {
          // Update rent agreement status for this occupancy record
          await occupancyDB.setRentAgreementStatus({
            tenantId: tenantId,
            clientId: occupancy.clientId,
            isRentAgreementSigned: 1
          });
          await occupancyDB.updateKycStatusWithClientId({
            tenantId: tenantId,
            clientId: occupancy.clientId,
            kycStatus: CONSTANTS.KYC_STATUS.RENT_AGREEMENT_DONE
          });
        }
        if (Number(type) === CONSTANTS.DOCUMENT_TYPES.POLICE_VERIFICATION) {
          // Update rent agreement status for this occupancy record
          await occupancyDB.setPoliceVerificationStatus({
            tenantId: tenantId,
            clientId: occupancy.clientId,
            isPoliceVerified: 1
          });
          //Not Updating KycStatus because IF PV is uploaded before KYC
          // await occupancyDB.updateKycStatusWithClientId({
          //     tenantId: tenantId,
          //     clientId: occupancy.clientId,
          //     kycStatus: CONSTANTS.KYC_STATUS.POLICE_VERIFICATION_DONE
          //   });
        }
        await documentDB.addDocWithStatus({
          tenantId,
          clientId: occupancy.clientId,
          propId: occupancy.propId,
          roomId: occupancy.roomId,
          type: type,
          value: url,
          status: CONSTANTS.DOCUMENT_STATUS.VERIFIED,
        });
      }

      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Type [${type}], Document uploaded successfully`
      );
    }

    if (oldBackPath) {
      const ext = files[1].mimetype.split("/")[1];
      const fileName = `Id_document_back.${ext}`;

      const newPath = `${folderPath}/${fileName}`;
      await fsPromises.copyFile(oldBackPath, newPath);
      const url = `${urlBase}/${fileName}`;

      const isExists = await documentDB.getIDByType({
        tenantId,
        clientId: occupancy?.clientId,
        type: CONSTANTS.DOCUMENT_TYPES.AADHAAR_BACK,
        moveOut: 0,
      });

      if (isExists) {
        await documentDB.updateDoc({
          tenantId,
          clientId: occupancy.clientId,
          propId: occupancy.propId,
          roomId: occupancy.roomId,
          type: CONSTANTS.DOCUMENT_TYPES.AADHAAR_BACK,
          value: url,
          id: isExists.id,
        });

        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Type [AADHAAR], URL [${url}], Aadhaar Front document re-uploaded successfully`
        );
      } else {
        await documentDB.addDocWithStatus({
          tenantId,
          clientId: occupancy.clientId,
          propId: occupancy.propId,
          roomId: occupancy.roomId,
          type: CONSTANTS.DOCUMENT_TYPES.AADHAAR_BACK,
          value: url,
          status: CONSTANTS.DOCUMENT_STATUS.VERIFIED,
        });

        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Type [AADHAAR], URL [${url}], Aadhaar Back document uploaded successfully`
        );
      }
      // await tenantDB.updateKycStatus({
      //   id: tenantId,
      //   kycStatus: CONSTANTS.KYC_STATUS.SELFI_UPLOADED,
      // });
      await occupancyDB.updateKycStatusWithClientId({
        tenantId: tenantId,
        clientId: occupancy.clientId,
        kycStatus: CONSTANTS.KYC_STATUS.SELFI_UPLOADED
      });
    }

    const docs = await documentDB.getByTenantIdAndClientId({ tenantId, clientId, moveOut: 0 });
    if (docs && docs.length > 0) {
      for (let doc of docs) {
        const title = await getDocumentTitle(doc.type);
        doc.title = title;
      }
    }

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Id doc(s) uploaded sucessfully`
    );

    return res.status(200).json({
      msg: "Id doc(s) uploaded sucessfully",
      isSuccess: true,
      docs: docs || [],
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    await removeTmpImages();

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

// clients.GetDashInfo = async (req: CustomRequest, res: Response) => {
//   const C = "Dashboard Controller";
//   const F = "GetDashInfo";
//   try {
//     const clientId = req.id || "";
//     if (!clientId) {
//       log.info(`[${C}], [${F}], Client Id [${clientId}], Send Client ID...`);
//       return res
//         .status(400)
//         .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
//     }
//     log.info(`[${C}], [${F}], Client Id [${clientId}], Client Requesting...`);
//     const client = await clientDB.getById({ id: clientId });
//     if (!client) {
//       log.info(`[${C}], [${F}], Client Id [${clientId}], No Client Found`);
//       return res
//         .status(400)
//         .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
//     }
//     const data = await DashboardDetails(client);
//     const todaysCollection =
//       data.todaysCollection == null ? 0 : data.todaysCollection;
//     const tenantCount = data.dueTenantCount;
//     const totalIncomeStats = data.stats[0].totalIncome;
//     const totalDues = data.stats[0].totalDues;
//     const Responsedata = {
//       todaysCollection,
//       totalIncomeStats,
//       totalDues,
//       tenantCount,
//     };
//     log.info(
//       `[${C}], [${F}], Client Id [${clientId}], Dashboard data sent successfully`
//     );
//     return res.status(200).json({
//       msg: `Data shared successfully`,
//       data: Responsedata,
//       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,
//     });
//   }
// };

clients.Graph = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "Graph";

  try {
    const userType = req.userType;
    // let clientId = req.id || "";
    const { type, term, month, year } = req.query;
    let data = {};
    log.info(`[${C}], [${F}], Type [${type}], Term [${term}]`);

    // let isPartner = false;

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

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

    let isFinanceAdmin = false;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (staff && staff.role === CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) isFinanceAdmin = true;
    }

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

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

      if (!client) {
        log.info(`[${C}], [${F}], Client Id [${clientId}], No Client Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (CONSTANTS.REPORT_TYPE.COLLECTION == Number(type)) {
        data = await clientCollectionGraph(client, term);
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Term [${term}], Graph Collection data sent successfully`
        );
      } else if (CONSTANTS.REPORT_TYPE.OCCUPANCY == Number(type)) {
        // data = await clientOccupancyGraph(client);
        data = await clientOccupancyGraphFY(client);
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Term [${term}], Graph Occupancy data sent successfully`
        );
      } else if (CONSTANTS.REPORT_TYPE.COMPLAINTS == Number(type)) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Term [${term}], Month [${month}], Year [${year}], Graph Complaints data sent successfully`
        );
        data = await clientComplaintGraph(client, term, month, year);
      } else if (CONSTANTS.REPORT_TYPE.DAILY_RENT == Number(type)) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Month [${month}], Year [${year}], Graph Daily Rent data sent successfully`
        );
        data = await clientRentCollectedExpectedGraph(client, month, year);
      } else {
        data = await clientExpenseGraph(client, term);
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Term [${term}], Month [${month}], Year [${year}], Graph Expense data sent successfully`
        );
      }
    } else {
      log.info(`[${C}], [${F}], UserType [${userType}], Invalid access`);
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

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

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

clients.DashboardIncome = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "DashboardIncome";
  try {
    const userType = req.userType;
    let { y, m } = req.query;
    let dueTenantCount = 0;
    let totalIncome = 0;
    let totalDues = 0;
    let expectedRent = null;
    let expectedSecurity = null;
    let data: any = null;
    let year = y;
    let month = m;
    let movingInCurrentMonthCount = 0;
    let movingInNextMonthCount = 0;
    let movingOutCurrentMonthCount = 0;
    let movingOutNextMonthCount = 0;
    let currentMonthAmount = 0;
    let nextMonthAmount = 0;
    if (!year || !month || "undefined" == month) {
      year = moment().format("YYYY");
      month = moment().format("M");
    }

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

    log.info(
      `[${C}], [${F}], UserType [${userType}], Month [${month}], Year [${year}]`
    );

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staffId = req.id;
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (staff.role === CONSTANTS.STAFF_ROLES.ADMIN) {
        data = await getStaffAdminDashboardByYearMonth(staff, year, month);
      } else if (staff.role === CONSTANTS.STAFF_ROLES.PARTNER) {
        const client = await clientDB.getById({ id: staff.clientId });
        if (!client) {
          log.info(`[${C}], [${F}], Staff Id [${staffId}], No Client Found`);
          return res
            .status(400)
            .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
        }

        data = await clientIncomeDetails(client, year, month);
        expectedRent = await occupancyDB.getExpectedRentForClient({
          clientId: client.id,
          month: month,
          year: year,
        });
        expectedSecurity = await occupancyDB.getExpectedSecurityForClient({
          clientId: client.id,
          month: month,
          year: year,
        });
      } else {
        log.info(
          `[${C}], [${F}], Staff Id [${staffId}], Normal Staff Not Allowed`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (data) {
        // todaysCollection =
        //   data.todaysCollection == null ? 0 : data.todaysCollection;
        dueTenantCount = data.dueTenantCount;
        totalIncome = data.transactionStats[0]?.totalIncome || 0;
        totalDues =
          data.duesStats?.totalDues || data.duesStats?.duesForMonth || 0;
        expectedRent = expectedRent || data.expectedRent || 0;
        expectedSecurity = expectedSecurity || data.expectedSecurity || 0;
      }
      log.info(
        `[${C}], [${F}], StaffId [${staffId}], Dashboard Data Sent Successfully....`
      );
    } else {
      let clientId = req.id;
      const client = await clientDB.getById({ id: clientId });
      if (!client) {
        log.info(`[${C}], [${F}], Client Id [${clientId}], No Client Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      data = await clientIncomeDetails(client, year, month);
      if (data) {
        // todaysCollection =
        //   data.todaysCollection == null ? 0 : data.todaysCollection;
        dueTenantCount = data.dueTenantCount;
        totalIncome = data.transactionStats[0]?.totalIncome || 0;
        totalDues = data.duesStats?.duesForMonth || 0;
      }

      expectedRent = await occupancyDB.getExpectedRentForClient({
        clientId,
        month: month,
        year: year,
      });

      expectedSecurity = await occupancyDB.getExpectedSecurityForClient({
        clientId,
        month: month,
        year: year,
      });

      expectedSecurity = expectedSecurity || 0;

      const movingInCurrentMonth =
        await occupancyDB.getMovingInCurrentMonthCount({
          clientId: clientId,
        });

      const movingOutCurrentMonth =
        await occupancyDB.getMovingOutCurrentMonthCount({
          clientId: clientId,
        });

      const movingInNextMonth = await occupancyDB.getMovingInNextMonthCount({
        clientId: clientId,
      });

      const movingOutNextMonth = await occupancyDB.getMovingOutNextMonthCount({
        clientId: clientId,
      });

      movingInCurrentMonthCount = movingInCurrentMonth.count;
      movingInNextMonthCount = movingInNextMonth.count;
      movingOutCurrentMonthCount = movingOutCurrentMonth.count;
      movingOutNextMonthCount = movingOutNextMonth.count;
      currentMonthAmount =
        Number(movingInCurrentMonth.totalRent) -
        Number(movingOutCurrentMonth.totalRent);
      nextMonthAmount =
        Number(movingInNextMonth.totalRent) -
        Number(movingOutNextMonth.totalRent);

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

    // const totalExpected = Number(expectedRent) + Number(expectedSecurity);
    let totalExpected = Number(expectedRent);
    if (clientId === 168) {
      totalExpected = Number(expectedRent) + Number(expectedSecurity);
    }

    const Responsedata = {
      //todaysCollection,
      totalIncome,
      totalDues,
      dueTenantCount,
      year,
      month,
      expectedRent: totalExpected || 0,
      movingInCurrentMonthCount,
      movingInNextMonthCount,
      movingOutCurrentMonthCount,
      movingOutNextMonthCount,
      currentMonthAmount,
      nextMonthAmount,
    };

    return res.status(200).json({
      msg: `Data shared successfully`,
      data: Responsedata,
      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,
    });
  }
};

clients.PendingRent = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "PendingRent";
  try {
    const userType = req.userType;
    let monthlyData: any = null;
    let dailyData: any = null;
    // let perDayStats = {};

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staffId = req.id;
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Staff Requesting....`);
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (staff.role === CONSTANTS.STAFF_ROLES.ADMIN) {
        let clientId = staff.clientId;
        monthlyData = await staffMonthlyPendingRent(staff);
        dailyData = await staffDailyPendingRent(staff);
        log.info(
          `[${C}], [${F}], Client Id [${clientId}] , Pending Rent data sent successfully....`
        );
      } else if (staff.role === CONSTANTS.STAFF_ROLES.PARTNER) {
        // let clientId = staff.clientId;
        const client = await clientDB.getById({ id: staff.clientId });
        if (!client) {
          log.info(`[${C}], [${F}], Partner Id [${staffId}], No Client Found`);
          return res
            .status(400)
            .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
        }
        monthlyData = await clientMonthlyPendingRent(client);
        dailyData = await clientDailyPendingRent(client);
        log.info(
          `[${C}], [${F}], Partner Id [${req.id}] , Pending Rent data sent successfully....`
        );
      } else {
        log.info(
          `[${C}], [${F}], Staff Id [${staffId}], Normal Staff Not Allowed`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
    } else {
      let clientId = req.id;
      log.info(`[${C}], [${F}], Client Id [${clientId}], Client Requesting....`);
      const client = await clientDB.getById({ id: clientId });
      if (!client) {
        log.info(`[${C}], [${F}], Client Id [${clientId}], No Client Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      monthlyData = await clientMonthlyPendingRent(client);
      dailyData = await clientDailyPendingRent(client);
      // perDayStats = {
      //   todayExpectedRent: await occupancyDB.getExpectedRentForClientForADay({clientId}),
      //   todayReceivedRent: await ledgerDB.getRentPaidTodayForClient({clientId}),
      // };
      log.info(
        `[${C}], [${F}], Client Id [${clientId}] , Pending Rent data sent successfully....`
      );
    }

    return res.status(200).json({
      msg: `Data shared successfully`,
      monthlyData: monthlyData,
      dailyData: dailyData,
      // perDayStats,
      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,
    });
  }
};

clients.ToggleOnlinePayment = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ToggleOnlinePayment";

  try {
    const { tenantId, isOnlinePaymentEnabled } = req.body;

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

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Staff Id [${req.id}], Is Online Payment Enabled [${isOnlinePaymentEnabled}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Is Online Payment Enabled [${isOnlinePaymentEnabled}], Client Requested....`
      );
    }

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

    const 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 });
    }

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      clientId: clientId,
      tenantId: tenantId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

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

    if (property.isOnlinePaymentEnabled === 0 && isOnlinePaymentEnabled === 1) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Property Id [${property.id}], Online Payment Not Enabled For This Property`
      );
      return res.status(400).json({
        msg: "Online payment is not enabled for this property",
        isSuccess: false,
      });
    }

    await occupancyDB.updateOnlinePaymentStatus({
      tenantId: tenant.id,
      clientId: client.id,
      isOnlinePaymentEnabled: Number(isOnlinePaymentEnabled),
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Online Payment ${Number(isOnlinePaymentEnabled) === 0 ? "Disabled" : "Enabled"
      } Successfully`
    );

    return res.status(200).json({
      msg: `Online Payment ${Number(isOnlinePaymentEnabled) === 0 ? "Disabled" : "Enabled"
        } 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,
    });
  }
};

clients.ToggleGst = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ToggleGst";

  try {
    const { tenantId, isGstEnabled } = req.body;

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

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Staff Id [${req.id}], Is Gst Enabled [${isGstEnabled}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Is Gst Enabled [${isGstEnabled}], Client Requested....`
      );
    }

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

    const 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 });
    }

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      clientId: clientId,
      tenantId: tenantId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

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

    if (property.isGstEnabled === 0 && isGstEnabled === 1) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Property Id [${property.id}], Gst Not Enabled For This Property`
      );
      return res.status(400).json({
        msg: "Gst is not enabled for this property",
        isSuccess: false,
      });
    }

    await occupancyDB.updateGstStatus({
      tenantId: tenant.id,
      clientId: client.id,
      isGstEnabled: Number(isGstEnabled),
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Gst ${Number(isGstEnabled) === 0 ? "Disabled" : "Enabled"
      } Successfully`
    );

    return res.status(200).json({
      msg: `Gst ${Number(isGstEnabled) === 0 ? "Disabled" : "Enabled"
        } 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,
    });
  }
};

clients.ListStaffAttendance = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListStaffAttendance";

  try {
    const { date } = req.params;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Date [${date}]`);

    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.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin or Back Office Requesting....`);
    }

    const staffs = await staffDB.getActiveByClientIdForAttendance({
      clientId: clientId,
    });

    const staffAttendances = await staffAttendanceDB.getByClientIdAndDate({
      clientId: clientId,
      date: date,
    });

    const attendanceMap = staffAttendances.reduce((acc: any, record: any) => {
      if (!acc[record.staffId]) acc[record.staffId] = [];
      acc[record.staffId].push(record);
      return acc;
    }, {});

    let present = 0;
    let absent = 0;
    const finalAttendanceList: any[] = [];
    let serialNo = 1;

    if (staffs && staffs.length > 0) {
      for (const staff of staffs) {
        if (Number(userType) === CONSTANTS.USER_TYPE.STAFF && Number(staff.id) === Number(req.id)) continue;
        const records = attendanceMap[staff.id] || [];

        if (records.length > 0) {
          // present += 1;
          if (records.some((record: any) => record.attendance === 1)) {
            present += 1;
          } else {
            absent += 1;
          }

          records.forEach((record: any) => {
            finalAttendanceList.push({
              id: record.id,
              serialNo: serialNo++,
              ...staff,
              day: moment(record.attendanceDate, "YYYY-MM-DD").format("ddd").toUpperCase(),
              date: moment(record.attendanceDate, "YYYY-MM-DD").format("DD-MM-YYYY"),
              attendance: record.attendance,
              punchIn: record.checkIn
                ? moment(record.checkIn).format("hh:mm A")
                : "--",
              punchOut: record.checkOut
                ? moment(record.checkOut).format("hh:mm A")
                : "--",
              propertyName: record.propName || "N/A",
              // propId: record.propId || null,
              propId: record.propId == 0 ? 0 : record.propId ? record.propId : null,
              // longitude: record.propName ? record.longitude : staff.reportingLongitude, 
              // latitude: record.propName ? record.latitude : staff.reportingLatitude,
              longitude: record.propName !== "Office" ? record.longitude : staff.reportingLongitude,
              latitude: record.propName !== "Office" ? record.latitude : staff.reportingLatitude,
            });
          });
        } else {
          absent += 1;

          finalAttendanceList.push({
            id: `absent-${staff.id}`,
            serialNo: serialNo++,
            ...staff,
            day: moment(date, "YYYY-MM-DD").format("ddd").toUpperCase(),
            date: moment(date, "YYYY-MM-DD").format("DD-MM-YYYY"),
            punchIn: "--",
            punchOut: "--",
            propertyName: "N/A",
            propId: null,
            longitude: "-",
            latitude: "-",
          });
        }
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Date [${date}], Staff Attendance List Fetched Successfully`
    );

    return res.status(200).json({
      msg: "Staff attendance list fetched successfully",
      isSuccess: true,
      data: finalAttendanceList,
      summary: {
        present: present,
        absent: absent,
      },
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

clients.ListParticularStaffAttendance = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Client Controller";
  const F = "ListParticularStaffAttendance";

  try {
    const { m, y } = req.query;
    const { staffId } = req.params;
    const userType = req.userType;
    let workingHours = 9;
    log.info(
      `[${C}], [${F}], Month [${m}], Year [${y}], Staff Id [${staffId}]`
    );

    let present = 0;
    let halfDay = 0;
    let absent = 0;

    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,
      });

      let { staffAttendancePermission } = await staffAttendanceModulePermission(
        Number(userType),
        Number(req.id)
      );

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

    const staff = await staffDB.getById({
      id: staffId,
    });
    if (!staff) {
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Staff Not Found`);
      return res.status(400).json({
        msg: "Staff Not Found",
        isSuccess: false,
      });
    }

    const date = moment(`${y}-${m}-01`).startOf("month");
    const endOfMonth = moment(date).endOf("month");
    const today = moment();

    const endDate =
      date.month() === today.month() && date.year() === today.year()
        ? today
        : endOfMonth;

    const dateArray: string[] = [];
    // let dateCursor = moment(date).isAfter(moment(staff.createdAt).format("YYYY-MM-DD"), "days") ? moment(date) : moment(staff.createdAt);

    // while (dateCursor.isSameOrBefore(endDate)) {
    //   dateArray.push(dateCursor.format("YYYY-MM-DD"));
    //   dateCursor.add(1, "day");
    // }

    let dateCursor = moment(date).isAfter(moment(staff.createdAt), "day")
      ? moment(date)
      : moment(staff.createdAt);

    while (dateCursor.isSameOrBefore(endDate, "day")) {
      dateArray.push(dateCursor.format("YYYY-MM-DD"));
      dateCursor.add(1, "day");
    }

    let staffAttendances = await staffAttendanceDB.getByStaffIdForMonth({
      staffId,
      date: date.format("YYYY-MM-DD"),
    });

    const attendanceMap = staffAttendances.reduce((acc: any, cur: any) => {
      const day = moment(cur.attendanceDate).format("YYYY-MM-DD");
      if (!acc[day]) acc[day] = [];
      acc[day].push(cur);
      return acc;
    }, {});

    // log.info(`Attendance Map - ${JSON.stringify(attendanceMap)}`)

    // present and absent summary
    // dateArray.forEach((date) => {
    //   const records = attendanceMap[date];
    //   if (records && records.length > 0) {
    //     if (records.some((record: any) => record.attendance == 1)) {
    //       present += 1;
    //     } else {
    //       absent += 1;
    //     }
    //     // present++;
    //   } else {
    //     absent++;
    //   }
    // });

    // present, halfDay and absent summary
    // dateArray.forEach((date) => {
    //   const records = attendanceMap[date];

    //   if (!records || records.length === 0) {
    //     absent++;
    //     return;
    //   }

    //   const presentRecord = records.find(
    //     (record: any) => Number(record.attendance) === 1
    //   );

    //   if (!presentRecord) {
    //     absent++;
    //     return;
    //   }

    //   if (isHalfDay(presentRecord)) {
    //     halfDay++;
    //   } else {
    //     present++;
    //   }
    // });

    dateArray.forEach((date) => {
      const records = attendanceMap[date];

      if (!records || records.length === 0) {
        absent++;
        return;
      }

      const presentRecord = records.find(
        (record: any) => Number(record.attendance) === 1
      );

      if (!presentRecord || !presentRecord.checkIn) {
        absent++;
        return;
      }

      const checkIn = moment(presentRecord.checkIn);

      // Check-in + Check-out
      if (presentRecord.checkOut) {
        const checkOut = moment(presentRecord.checkOut);
        const hoursWorked = checkOut.diff(checkIn, "hours", true);

        if (hoursWorked >= workingHours) {
          present++;
        } else {
          halfDay++;
        }

        return;
      }

      // Check-in but no Check-out
      const hoursSinceCheckIn = moment().diff(checkIn, "hours", true);

      if (hoursSinceCheckIn >= workingHours) {
        halfDay++;
      }
    });

    //log.info(`Date Array --- ${JSON.stringify(dateArray)}`)

    const formattedAttendance: any[] = [];
    let serialId = 1;

    // dateArray.reverse().forEach((date) => {
    //   const records = attendanceMap[date];

    //   if (records && records.length > 0) {
    //     records.forEach((record: any) => {
    //       formattedAttendance.push({
    //         id: serialId++,
    //         day: moment(date).format("ddd").toUpperCase(),
    //         date: moment(date).format("DD-MM-YYYY"),
    //         attendance: record?.attendance,
    //         punchIn: record.checkIn
    //           ? moment(record.checkIn).format("hh:mm A")
    //           : "--",
    //         punchOut: record.checkOut
    //           ? moment(record.checkOut).format("hh:mm A")
    //           : "--",
    //         // propertyName: record.propName || "N/A",
    //         propertyName: record.propName ? record.propName : Number(record.attendance) === 1 ? "Office work" : "N/A",
    //         // propId: record.propId || null,
    //         propId: record.propId == 0 ? 0 : record.propId ? record.propId : null,
    //         // longitude: record.propName ? record.longitude : staff.reportingLongitude, 
    //         // latitude: record.propName ? record.latitude : staff.reportingLatitude,
    //         longitude: record.propName !== "Office" ? record.longitude : staff.reportingLongitude,
    //         latitude: record.propName !== "Office" ? record.latitude : staff.reportingLatitude,
    //       });
    //     });
    //   } else {
    //     // Absent record
    //     formattedAttendance.push({
    //       id: serialId++,
    //       day: moment(date).format("ddd").toUpperCase(),
    //       date: moment(date).format("DD-MM-YYYY"),
    //       punchIn: "-",
    //       punchOut: "-",
    //       propertyName: "N/A",
    //       propId: null,
    //       attendance: 0,
    //       longitude: "-",
    //       latitude: "-",
    //     });
    //   }
    // });
    // 0 Absent 1- present - 2 Halfday
    dateArray.reverse().forEach((date) => {
      const records = attendanceMap[date];

      if (records && records.length > 0) {
        records.forEach((record: any) => {
          let attendance = Number(record.attendance);

          if (!record.checkIn) {
            // No check-in = Absent
            attendance = 0;
          } else if (record.attendance === 0) {
            // Already marked as absent
            attendance = 0;
          } else if (record.checkOut) {
            // Check-in + Check-out
            const checkIn = moment(record.checkIn);
            const checkOut = moment(record.checkOut);

            const hoursWorked = checkOut.diff(checkIn, "hours", true);

            // 9 hours completed = Present, otherwise Half Day
            attendance = hoursWorked >= workingHours ? 1 : 2;
          } else {
            // Check-in but no Check-out
            const checkIn = moment(record.checkIn);
            const hoursSinceCheckIn = moment().diff(checkIn, "hours", true);

            // 9 hours crossed = Half Day
            // Otherwise keep Present
            attendance = hoursSinceCheckIn >= workingHours ? 2 : 1;
          }

          formattedAttendance.push({
            id: serialId++,
            day: moment(date).format("ddd").toUpperCase(),
            date: moment(date).format("DD-MM-YYYY"),
            attendance,

            punchIn: record.checkIn
              ? moment(record.checkIn).format("hh:mm A")
              : "--",

            punchOut: record.checkOut
              ? moment(record.checkOut).format("hh:mm A")
              : "--",

            propertyName: record.propName
              ? record.propName
              : attendance === 1
                ? "Office work"
                : "N/A",

            propId:
              record.propId == 0
                ? 0
                : record.propId
                  ? record.propId
                  : null,

            longitude:
              record.propName !== "Office"
                ? record.longitude
                : staff.reportingLongitude,

            latitude:
              record.propName !== "Office"
                ? record.latitude
                : staff.reportingLatitude,
          });
        });
      } else {
        // No attendance record = Absent
        formattedAttendance.push({
          id: serialId++,
          day: moment(date).format("ddd").toUpperCase(),
          date: moment(date).format("DD-MM-YYYY"),
          punchIn: "-",
          punchOut: "-",
          propertyName: "N/A",
          propId: null,
          attendance: 0,
          longitude: "-",
          latitude: "-",
        });
      }
    });
    const salaryDeductionDays =
      Number(absent || 0) + (Number(halfDay || 0) / 2);

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Month [${m}], Year [${y}], Attendance List Sent Successfully`
    );

    return res.status(200).json({
      msg: "Staff attendance list fetched successfully",
      isSuccess: true,
      data: formattedAttendance,
      staffName: staff.name,
      role: staff.role,
      isSuperAdmin: staff.isSuperAdmin,
      present,
      halfDay,
      absent,
      salaryDeductionDays
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

clients.ManageStaffPermissions = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ManageStaffPermissions";

  try {
    let { staffId, permissions } = req.body;

    log.info(
      `[${C}], [${F}], StaffId [${staffId}], Permissions [${permissions}]`
    );

    if (Number(permissions) === 0) {
      log.info(`[${C}], [${F}], StaffId [${staffId}], Permissions set to 1`);
      permissions = 1;
    }

    const userType = req.userType;

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

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

      if (!staff) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], No Staff Found`
        );
        return res.status(400).json({
          msg: "Staff Not Found",
          isSuccess: false,
        });
      }

      const hasPermission = await checkPermission(staff.permissions, CONSTANTS.PERMISSIONS.MANAGE_STAFF_PERMISSION);

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

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

    const staff = await staffDB.getById({
      id: staffId,
    });
    if (!staff) {
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Staff Not Found`);
      return res.status(400).json({
        msg: "Staff Not Found",
        isSuccess: false,
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Permissions [${permissions}]`
    );

    await staffDB.updateStaffPermissions({
      id: staffId,
      permissions: Number(parseInt(`${permissions}`, 2)),
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Permissions Updated Successfully`
    );

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

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

clients.ListStaffPermissions = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListStaffPermissions";

  try {
    const { staffId } = req.params;

    log.info(`[${C}], [${F}], Staff Id [${staffId}]`);

    const userType = req.userType;

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

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

      const hasPermission = await checkPermission(staff.permissions, CONSTANTS.PERMISSIONS.MANAGE_STAFF_PERMISSION);

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && !hasPermission) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Or Back Office Requesting....`
        );
      }
    }

    const staff = await staffDB.getById({
      id: staffId,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Staff Permissions [${JSON.stringify(
        staff.permissions
      )}], Staff Permissons Sent Successfully`
    );

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

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

clients.PendingRentX = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "PendingRentX";
  try {
    const userType = req.userType;
    const { m, y } = req.query;
    // let monthlyData: any = null;
    // let dailyData: any = null;

    let securityInHand = 0;
    let data: any = null;

    log.info(`[${C}], [${F}], Month [${m}], Year [${y}]`);

    let month = m;
    let year = y;

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

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

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

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

      if (month && year) {
        data = await clientDailyPendingRentForMonth(client, month, year);
      } else {
        data = await clientMonthlyPendingRent(client);
      }

      securityInHand = await occupancyDB.getTotalSecurityInHand({
        clientId: clientId,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Pending Rent data sent successfully....`
      );
    } else {
      const staffId = req.id;
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Staff Requesting....`);
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (staff.role === CONSTANTS.STAFF_ROLES.ADMIN || staff.role === CONSTANTS.STAFF_ROLES.WARDEN || staff.role === CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        let clientId = staff.clientId;

        if (month && year) {
          data = await staffDailyPendingRentForMonth(staff, year, month);
        } else {
          data = await staffMonthlyPendingRent(staff);
        }

        const staffLinkedProps = await propertyDB.getPropsByStaffId({
          staffId,
        });
        if (staffLinkedProps) {
          const propertiesIds = staffLinkedProps
            .map((prop: propertiesTypes) => prop.id)
            .join(",");

          securityInHand = await occupancyDB.getTotalSecurityInHandForStaff({
            clientId: clientId,
            propertiesIds,
          });
        }

        log.info(
          `[${C}], [${F}], Client Id [${clientId}] , Pending Rent data sent successfully....`
        );
      } else {
        log.info(
          `[${C}], [${F}], Staff Id [${staffId}], Normal Staff Not Allowed`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
    }

    return res.status(200).json({
      msg: `Data shared successfully`,
      data,
      securityInHand: Math.abs(securityInHand),
      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,
    });
  }
};

clients.GetNetProfitLossData = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "GetNetProfitLossData";
  try {
    const userType = req.userType;
    const { m, y } = req.query;

    let month = m;
    let year = y;

    if (!month) {
      month = `${moment().month() + 1}`;
    }

    if (!year) {
      year = `${moment().year()}`;
    }

    log.info(`[${C}], [${F}], Month [${month}], Year [${year}]`);

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${isPartner ? "Partner Requesting" : "Client Requesting"}....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      log.info(
        `[${C}], [${F}],Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );
      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}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
    }

    let income = await transactionDB.getTotalIncomeStatsByMonth({
      clientId: clientId,
      status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
      year: year,
      month: month,
    });
    income = income[0].total;

    let expense = await expenseDB.getTotalByClientId({
      clientId,
      month: moment(`${year}-${month}-01`).format("YYYY-MM-DD"),
    });

    if (expense === null) {
      expense = 0;
    }

    const netProfitLoss = Number(income) - Number(expense);

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Income [${income}], Expenses [${expense}], Net Diff [${netProfitLoss}], Net Profit-Loss data sent successfully....`
    );

    return res.send(200).json({
      msg: "Net profit-loss data fetched successfully",
      data: {
        income,
        expense,
        netProfitLoss,
      },
      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,
    });
  }
};

clients.GetBedsStats = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "GetBedsStats";
  try {
    let { propId, locationId = 0, checkDate = moment().format("YYYY-MM-DD") } = req.query;

    if (locationId === 'undefined') {
      locationId = 0;
    }

    const propIdNum = typeof propId === 'string' ? Number(propId) : 0;
    const locationIdNum = typeof locationId === 'string' ? Number(locationId) : 0;

    log.info(`[${C}], [${F}], Property Id [${propId}], Location Id [${locationId}], Check Date [${checkDate}]`);

    const userType = req.userType;
    let data: any = [];
    let flatData = [];
    let newData = [];
    let pgData = [];
    let ownerData: any = {};
    let newOwnerData: any = {};
    let newOwnerDataX: any = {};

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

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

    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 (Number(propIdNum) === 0 && Number(locationIdNum) === 0) {
        //all prop

        flatData = await roomDB.getBedStatsForClientDetailedFlat({
          clientId: clientId,
        });

        newData = await roomDB.getBedStatsForClientDetailedX({
          clientId: clientId,
          checkDate: checkDate,
        });

        pgData = await roomDB.getBedStatsForClientDetailedPg({
          clientId: clientId,
        });

        if (Array.isArray(flatData) && flatData.length > 0) {
          data = data.concat(flatData);
        }

        if (Array.isArray(pgData) && pgData.length > 0) {
          data = data.concat(pgData);
        }

        const propIdSet = new Set<number>();
        ownerData = {};

        for (const row of data) {
          const { propId, flatFloor, sharingType, roomCount, totalBeds, vacantBeds } = row;

          propIdSet.add(propId);

          if (!ownerData[propId]) {
            ownerData[propId] = {
              floorStats: {},
              propName: row.propName,
              propType: row.propType,
            };
          }

          const floor = row.flatFloor;
          if (!ownerData[propId].floorStats[floor]) {
            ownerData[propId].floorStats[floor] = {
              flatFloor: floor,
            };
          }
          ownerData[propId].floorStats[floor][row.sharingType] = row.roomCount;
        }

        for (const propId in ownerData) {

          const floorDataObj = ownerData[propId];
          const floorDataArray = Object.values(floorDataObj.floorStats);

          const result = {
            propName: floorDataObj.propName,
            type: floorDataObj.propType,
            floors: floorDataArray,
          };

          ownerData[propId] = result;
        }

        if (newData && newData.length > 0) {
          for (const row of newData) {
            const { propId, gId, propName, propType, propAddress, tenantPreference, flatFloor, roomNum, totalBeds, vacantBeds, movingOutBeds, isFullyVacant, hasAC, amenities, gender } = row;

            // if(0 === Number(vacantBeds)) {
            //   continue;
            // }

            if (!newOwnerData[propId]) {
              newOwnerData[propId] = {
                gId,
                propName,
                propType,
                tenantPreference,
                propAddress,
                floors: {},
              };
            }

            if (!newOwnerData[propId].floors[flatFloor]) {
              newOwnerData[propId].floors[flatFloor] = {};
            }

            newOwnerData[propId].floors[flatFloor][roomNum] = {
              totalBeds,
              vacantBeds: Number(vacantBeds),
              movingOutBeds: Number(movingOutBeds) || 0,
              hasAC,
              amenities,
            };

            if (!newOwnerDataX[propId]) {
              newOwnerDataX[propId] = {
                gId,
                propName,
                propType,
                tenantPreference,
                propAddress,
                floors: {},
              };
            }

            if (!newOwnerDataX[propId].floors[flatFloor]) {
              newOwnerDataX[propId].floors[flatFloor] = {};
              newOwnerDataX[propId].floors[flatFloor].gender = gender;
              newOwnerDataX[propId].floors[flatFloor].isFullyVacant = isFullyVacant;
            }

            if (!newOwnerDataX[propId].floors[flatFloor].rooms) {
              newOwnerDataX[propId].floors[flatFloor].rooms = {};
            }

            newOwnerDataX[propId].floors[flatFloor].rooms[roomNum] = {
              totalBeds,
              vacantBeds: Number(vacantBeds),
              movingOutBeds: Number(movingOutBeds) || 0,
              hasAC,
              amenities,
            };
          }
        }
      } else if (Number(propIdNum) && Number(propIdNum) !== 0) {
        // particular prop

        const property = await propertyDB.getById({ id: propIdNum });

        newData = await roomDB.getBedStatsForPropDetailedX({
          propId: propId,
          checkDate: checkDate,
        });

        if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
          data = await roomDB.getBedStatsForPropDetailedFlat({
            propId: propId,
          });
        } else {
          data = await roomDB.getBedStatsForPropDetailedPg({
            propId: propId,
          });
        }

        if (data?.length > 0) {
          const row = data[0];
          ownerData[propIdNum] = {
            floorStats: {},
            propName: row.propName,
            propType: row.propType,
          };

          for (const row of data) {
            const floor = row.flatFloor;
            if (!ownerData[propIdNum].floorStats[floor]) {
              ownerData[propIdNum].floorStats[floor] = {
                flatFloor: floor,
              };
            }
            ownerData[propIdNum].floorStats[floor][row.sharingType] = row.roomCount;
          }

          const floorDataArray = Object.values(ownerData[propIdNum].floorStats);
          ownerData[propIdNum] = {
            propName: ownerData[propIdNum].propName,
            type: ownerData[propIdNum].propType,
            floors: floorDataArray,
          };
        }
        if (newData && newData.length > 0) {
          for (const row of newData) {
            const { propId, gId, propName, propType, propAddress, tenantPreference, flatFloor, roomNum, totalBeds, vacantBeds, movingOutBeds, isFullyVacant, hasAC, amenities, gender } = row;
            // if(0 === Number(vacantBeds)) {
            //   continue;
            // }
            if (!newOwnerData[propId]) {
              newOwnerData[propId] = {
                gId,
                propName,
                propType,
                tenantPreference,
                propAddress,
                floors: {}
              };
            }

            if (!newOwnerData[propId].floors[flatFloor]) {
              newOwnerData[propId].floors[flatFloor] = {};
            }

            newOwnerData[propId].floors[flatFloor][roomNum] = {
              totalBeds,
              vacantBeds: Number(vacantBeds),
              movingOutBeds: Number(movingOutBeds) || 0,
              hasAC,
              amenities,
            };

            if (!newOwnerDataX[propId]) {
              newOwnerDataX[propId] = {
                gId,
                propName,
                propType,
                tenantPreference,
                propAddress,
                floors: {}
              };
            }

            if (!newOwnerDataX[propId].floors[flatFloor]) {
              newOwnerDataX[propId].floors[flatFloor] = {};
              newOwnerDataX[propId].floors[flatFloor].gender = gender;
              newOwnerDataX[propId].floors[flatFloor].isFullyVacant = isFullyVacant;
            }

            if (!newOwnerDataX[propId].floors[flatFloor].rooms) {
              newOwnerDataX[propId].floors[flatFloor].rooms = {};
            }

            newOwnerDataX[propId].floors[flatFloor].rooms[roomNum] = {
              totalBeds,
              vacantBeds: Number(vacantBeds),
              movingOutBeds: Number(movingOutBeds) || 0,
              hasAC,
              amenities,
            };
          }
        }
      } else if (locationIdNum) {
        // particular location

        newData = await roomDB.getBedStatsForLocation({
          locationId: locationId,
          checkDate: checkDate,
        });

        if (newData && newData.length > 0) {
          for (const row of newData) {
            const { propId, gId, propName, propType, propAddress, tenantPreference, flatFloor, roomNum, totalBeds, vacantBeds, movingOutBeds, isFullyVacant, hasAC, amenities, gender } = row;
            // if(0 === Number(vacantBeds)) {
            //   continue;
            // }
            if (!newOwnerData[propId]) {
              newOwnerData[propId] = {
                gId,
                propName,
                propType,
                tenantPreference,
                propAddress,
                floors: {}
              };
            }

            if (!newOwnerData[propId].floors[flatFloor]) {
              newOwnerData[propId].floors[flatFloor] = {};
            }

            newOwnerData[propId].floors[flatFloor][roomNum] = {
              totalBeds,
              vacantBeds: Number(vacantBeds),
              movingOutBeds: Number(movingOutBeds) || 0,
              hasAC,
              amenities,
            };

            if (!newOwnerDataX[propId]) {
              newOwnerDataX[propId] = {
                gId,
                propName,
                propType,
                tenantPreference,
                propAddress,
                floors: {}
              };
            }

            if (!newOwnerDataX[propId].floors[flatFloor]) {
              newOwnerDataX[propId].floors[flatFloor] = {};
              newOwnerDataX[propId].floors[flatFloor].gender = gender;
              newOwnerDataX[propId].floors[flatFloor].isFullyVacant = isFullyVacant;
            }

            if (!newOwnerDataX[propId].floors[flatFloor].rooms) {
              newOwnerDataX[propId].floors[flatFloor].rooms = {};
            }

            newOwnerDataX[propId].floors[flatFloor].rooms[roomNum] = {
              totalBeds,
              vacantBeds: Number(vacantBeds),
              movingOutBeds: Number(movingOutBeds) || 0,
              hasAC,
              amenities,
            };
          }
        }
      }
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });

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

      }

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

      if (Number(propIdNum) === 0 && Number(locationIdNum) === 0) {

        const staffLinkedProps = await propertyDB.getPropsByStaffId({
          staffId: staff.id,
        });
        if (staffLinkedProps) {
          const propertiesIds = staffLinkedProps
            .map((prop: propertiesTypes) => prop.id)
            .join(",");
          //all prop

          flatData = await roomDB.getBedStatsForStaffDetailedFlat({
            clientId: clientId,
            propertiesIds: propertiesIds,
          });

          pgData = await roomDB.getBedStatsForStaffDetailedPg({
            clientId: clientId,
            propertiesIds: propertiesIds,
          });

          newData = await roomDB.getBedStatsForStaffDetailedX({
            clientId: clientId,
            propertiesIds: propertiesIds,
            checkDate: checkDate,
          });

          if (Array.isArray(flatData) && flatData.length > 0) {
            data = data.concat(flatData);
          }

          if (Array.isArray(pgData) && pgData.length > 0) {
            data = data.concat(pgData);
          }

          const propIdSet = new Set<number>();
          ownerData = {};

          for (const row of data) {
            const { propId, flatFloor, sharingType, roomCount } = row;

            propIdSet.add(propId);

            if (!ownerData[propId]) {
              ownerData[propId] = {
                floorStats: {},
                propName: row.propName,
                propType: row.propType,
              };
            }

            const floor = row.flatFloor;
            if (!ownerData[propId].floorStats[floor]) {
              ownerData[propId].floorStats[floor] = {
                flatFloor: floor,
              };
            }
            ownerData[propId].floorStats[floor][row.sharingType] = row.roomCount;
          }

          for (const propId in ownerData) {

            const floorDataObj = ownerData[propId];
            const floorDataArray = Object.values(floorDataObj.floorStats);

            const result = {
              propName: floorDataObj.propName,
              type: floorDataObj.propType,
              floors: floorDataArray,
            };

            ownerData[propId] = result;
          }
          if (newData && newData.length > 0) {
            for (const row of newData) {
              const { propId, gId, propName, propType, propAddress, tenantPreference, flatFloor, roomNum, totalBeds, vacantBeds, movingOutBeds, isFullyVacant, hasAC, amenities, gender } = row;

              // if(0 === Number(vacantBeds)) {
              //   continue;
              // }
              if (!newOwnerData[propId]) {
                newOwnerData[propId] = {
                  gId,
                  propName,
                  propType,
                  tenantPreference,
                  propAddress,
                  floors: {}
                };
              }

              if (!newOwnerData[propId].floors[flatFloor]) {
                newOwnerData[propId].floors[flatFloor] = {};
              }

              newOwnerData[propId].floors[flatFloor][roomNum] = {
                totalBeds,
                vacantBeds: Number(vacantBeds),
                movingOutBeds: Number(movingOutBeds) || 0,
                hasAC,
                amenities,
              };

              if (!newOwnerDataX[propId]) {
                newOwnerDataX[propId] = {
                  gId,
                  propName,
                  propType,
                  tenantPreference,
                  propAddress,
                  floors: {}
                };
              }

              if (!newOwnerDataX[propId].floors[flatFloor]) {
                newOwnerDataX[propId].floors[flatFloor] = {};
                newOwnerDataX[propId].floors[flatFloor].gender = gender;
                newOwnerDataX[propId].floors[flatFloor].isFullyVacant = isFullyVacant;
              }

              if (!newOwnerDataX[propId].floors[flatFloor].rooms) {
                newOwnerDataX[propId].floors[flatFloor].rooms = {};
              }

              newOwnerDataX[propId].floors[flatFloor].rooms[roomNum] = {
                totalBeds,
                vacantBeds: Number(vacantBeds),
                movingOutBeds: Number(movingOutBeds) || 0,
                hasAC,
                amenities,
              };
            }
          }
        }
      } else if (Number(propIdNum) && Number(propIdNum) !== 0) {
        //particular prop

        const property = await propertyDB.getById({ id: propIdNum });

        if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
          data = await roomDB.getBedStatsForPropDetailedFlat({
            propId: propId,
          });
        } else {
          data = await roomDB.getBedStatsForPropDetailedPg({
            propId: propId,
          });
        }

        if (data?.length > 0) {
          const row = data[0];
          ownerData[propIdNum] = {
            floorStats: {},
            propName: row.propName,
            propType: row.propType,
          };

          for (const row of data) {
            const floor = row.flatFloor;
            if (!ownerData[propIdNum].floorStats[floor]) {
              ownerData[propIdNum].floorStats[floor] = {
                flatFloor: floor,
              };
            }
            ownerData[propIdNum].floorStats[floor][row.sharingType] = row.roomCount;
          }

          const floorDataArray = Object.values(ownerData[propIdNum].floorStats);
          ownerData[propIdNum] = {
            propName: ownerData[propIdNum].propName,
            type: ownerData[propIdNum].propType,
            floors: floorDataArray,
          };
        }

        newData = await roomDB.getBedStatsForPropDetailedX({
          propId: propId,
          checkDate: checkDate,
        });

        if (newData && newData.length > 0) {
          for (const row of newData) {
            const { propId, gId, propName, propType, propAddress, tenantPreference, flatFloor, roomNum, totalBeds, vacantBeds, movingOutBeds, isFullyVacant, hasAC, amenities, gender } = row;

            // if(0 === Number(vacantBeds)) {
            //   continue;
            // }

            if (!newOwnerData[propId]) {
              newOwnerData[propId] = {
                gId,
                propName,
                propType,
                tenantPreference,
                propAddress,
                floors: {}
              };
            }

            if (!newOwnerData[propId].floors[flatFloor]) {
              newOwnerData[propId].floors[flatFloor] = {};
            }

            newOwnerData[propId].floors[flatFloor][roomNum] = {
              totalBeds,
              vacantBeds: Number(vacantBeds),
              movingOutBeds: Number(movingOutBeds) || 0,
              hasAC,
              amenities,
            };

            if (!newOwnerDataX[propId]) {
              newOwnerDataX[propId] = {
                gId,
                propName,
                propType,
                tenantPreference,
                propAddress,
                floors: {}
              };
            }

            if (!newOwnerDataX[propId].floors[flatFloor]) {
              newOwnerDataX[propId].floors[flatFloor] = {};
              newOwnerDataX[propId].floors[flatFloor].gender = gender;
              newOwnerDataX[propId].floors[flatFloor].isFullyVacant = isFullyVacant;
            }

            if (!newOwnerDataX[propId].floors[flatFloor].rooms) {
              newOwnerDataX[propId].floors[flatFloor].rooms = {};
            }

            newOwnerDataX[propId].floors[flatFloor].rooms[roomNum] = {
              totalBeds,
              vacantBeds: Number(vacantBeds),
              movingOutBeds: Number(movingOutBeds) || 0,
              hasAC,
              amenities,
            };
          }
        }
      } else if (locationIdNum) {
        // particular location
        const staffLinkedProps = await propertyDB.getPropsByStaffId({
          staffId: staff.id,
        });
        if (staffLinkedProps) {
          const propertiesIds = staffLinkedProps
            .map((prop: propertiesTypes) => prop.id)
            .join(",");

          newData = await roomDB.getBedStatsForLocationStaff({
            locationId: locationId,
            propertiesIds: propertiesIds,
            checkDate: checkDate,
          });

          if (newData && newData.length > 0) {
            for (const row of newData) {
              const { propId, gId, propName, propType, propAddress, tenantPreference, flatFloor, roomNum, totalBeds, vacantBeds, movingOutBeds, isFullyVacant, hasAC, amenities, gender } = row;
              // if(0 === Number(vacantBeds)) {
              //   continue;
              // }
              if (!newOwnerData[propId]) {
                newOwnerData[propId] = {
                  gId,
                  propName,
                  propType,
                  tenantPreference,
                  propAddress,
                  floors: {}
                };
              }

              if (!newOwnerData[propId].floors[flatFloor]) {
                newOwnerData[propId].floors[flatFloor] = {};
              }

              newOwnerData[propId].floors[flatFloor][roomNum] = {
                totalBeds,
                vacantBeds: Number(vacantBeds),
                movingOutBeds: Number(movingOutBeds) || 0,
                hasAC,
                amenities,
              };

              if (!newOwnerDataX[propId]) {
                newOwnerDataX[propId] = {
                  gId,
                  propName,
                  propType,
                  tenantPreference,
                  propAddress,
                  floors: {}
                };
              }

              if (!newOwnerDataX[propId].floors[flatFloor]) {
                newOwnerDataX[propId].floors[flatFloor] = {};
                newOwnerDataX[propId].floors[flatFloor].gender = gender;
                newOwnerDataX[propId].floors[flatFloor].isFullyVacant = isFullyVacant;
              }

              if (!newOwnerDataX[propId].floors[flatFloor].rooms) {
                newOwnerDataX[propId].floors[flatFloor].rooms = {};
              }

              newOwnerDataX[propId].floors[flatFloor].rooms[roomNum] = {
                totalBeds,
                vacantBeds: Number(vacantBeds),
                movingOutBeds: Number(movingOutBeds) || 0,
                hasAC,
                amenities,
              };
            }
          }
        }
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Beds stats fetched successfully`
    );

    return res.status(200).json({
      msg: "Beds stats fetched successfully",
      isSuccess: true,
      data: ownerData,
      newData: newOwnerData,
      newDataX: newOwnerDataX,
    });

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

clients.Analytics = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "Analytics";

  try {
    let { startDate, endDate, term } = req.query;

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

    if (!startDate) {
      startDate = moment().startOf("month").format("YYYY-MM-DD");
    }
    if (!endDate) {
      endDate = moment().endOf("month").format("YYYY-MM-DD");
    }

    startDate = moment(startDate as string).format("YYYY-MM-DD");
    endDate = moment(endDate as string).format("YYYY-MM-DD");

    const userType = req.userType;

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${isPartner ? "Partner Requesting" : "Client Requesting"}....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );

      let isStaffAuthorized = await isPrivilegedStaff(
        staff.role,
        1,
      );

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

    // if (!startDate || !moment(startDate).isValid()) {
    //   startDate = moment().startOf("month");
    // }
    // if (!endDate || !moment(endDate).isValid()) {
    //   endDate = moment().endOf("month");
    // }

    const expenseAmount = await expenseDB.getTotalByClientIdAndDateRangeDueDate({
      clientId: clientId,
      startDate,
      endDate,
    });

    const transactionAmount = await transactionDB.getTotalByDateRangeWithoutSecurity({
      clientId: clientId,
      startDate,
      endDate,
    });

    const totalBeds = await bedDB.getCountByClientId({
      clientId: clientId,
    });

    const totalOccupiedBeds = await bedDB.getOccupiedCountByClientId({
      clientId: clientId,
    });

    // let profitPerBed = ((transactionAmount - expenseAmount) / totalBeds).toFixed(2);

    // let profitPerBedOccupied = ((transactionAmount - expenseAmount) / totalOccupiedBeds).toFixed(2);

    const avgTenantDuration = await occupancyDB.getAvgTenantDuration({
      clientId: clientId,
      startDate,
      endDate,
    });

    const marketingCost = await expenseDB.getTotalByClientIdAndDateRangeAndType({
      clientId: clientId,
      startDate,
      endDate,
      type: 19
    });

    const brokerageCost = await expenseDB.getTotalByClientIdAndDateRangeAndType({
      clientId: clientId,
      startDate,
      endDate,
      type: 20
    });

    const acquisitionCost = await expenseDB.getTotalByClientIdAndDateRangeAndType({
      clientId: clientId,
      startDate,
      endDate,
      type: 45
    });

    const tenantAcquisitionCost = Number(marketingCost) + Number(brokerageCost) + Number(acquisitionCost);

    const activeTenants = await occupancyDB.getActiveTenantCountForDateRange({
      clientId: clientId,
      startDate,
      endDate,
    });

    const revenuePerTenant = (transactionAmount / activeTenants).toFixed(2);

    let stats = [];
    let allYears = [];
    let client = await clientDB.getById({id: clientId});
    let clientCreatedOn = client.createdAt;
    let joiningYear = moment(clientCreatedOn).year();
    if (2 == Number(term)) {
      allYears = await years(joiningYear, 5);
      // let expenses = [
      //   { year: allYears[0], income: 0, expense: 0 },
      //   { year: allYears[1], income: 0, expense: 0 },
      //   { year: allYears[2], income: 0, expense: 0 },
      //   { year: allYears[3], income: 0, expense: 0 },
      //   { year: allYears[4], income: 0, expense: 0 },
      //   { year: allYears[5], income: 0, expense: 0 },
      // ];
      let expenses = allYears.map((year: number) => ({
        year,
        income: 0,
        expense: 0
      }));
      let newExpense = await expenseDB.getYearlyRentTransaction({
        clientId,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
      });
      stats = expenses.reduce((acc: any, cur: any) => {
        const expenseData = newExpense.find(
          (d: any) => d.year === cur.year && d.type === "expense"
        );
        const incomeData = newExpense.find(
          (d: any) => d.year === cur.year && d.type === "income"
        );
        const stat = {
          income: (incomeData && Number(incomeData.amount)) || 0,
          expense: (expenseData && Number(expenseData.amount)) || 0,
          year: cur.year,
        };
        acc.push(stat);
        return acc;
      }, []);
    } else {
      const start = moment(startDate as string, "YYYY-MM-DD");
      const end = moment(endDate as string, "YYYY-MM-DD");
      const year = start.format("YYYY");

      let expenses = [];
      const current = start.clone();

      while (current.isSameOrBefore(end, 'month')) {
        expenses.push({
          month: parseInt(current.format("M")),
          year: parseInt(current.format("YYYY")),
          income: 0,
          expense: 0,
        });
        current.add(1, 'month');
      }

      // Fetch income/expense data for the year
      let newExpense = await expenseDB.getMonthlyIncomeExpenseByDateRangeWithoutSecurity({
        clientId,
        startDate,
        endDate,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
      });

      stats = expenses.reduce((acc: any[], cur: any) => {
        const expenseData = newExpense.find(
          (d: any) => d.month === cur.month && d.year === cur.year && d.type === "expense"
        );
        const incomedData = newExpense.find(
          (d: any) => d.month === cur.month && d.year === cur.year && d.type === "income"
        );

        const stat = {
          income: (incomedData && Number(incomedData.amount)) || 0,
          expense: (expenseData && Number(expenseData.amount)) || 0,
          month: cur.month,
          year: cur.year,
        };

        acc.push(stat);
        return acc;
      }, []);
    }

    const occupancyData = await occupancyReportDB.getOccupancyReportForDate({
      clientId,
      date: moment(startDate as string).format("YYYY-MM-DD"),
    });

    const bedCounts = await bedDB.getCountsByClientId({
      clientId,
    });

    let occupancyRate = (occupancyData?.occupied / occupancyData?.total * 100).toFixed(2) || 0;
    let vacancyRate = (occupancyData?.vacant / occupancyData?.total * 100).toFixed(2) || 0;

    if (moment(startDate).month() === moment().month()) {
      occupancyRate = (bedCounts?.occupied / bedCounts?.total * 100).toFixed(2) || 0;
      vacancyRate = (bedCounts?.vacant / bedCounts?.total * 100).toFixed(2) || 0;
    }

    //above occupancy and vacany rate id not being used, 
    // leaving it for now so that previous build is not affected (2025-11-28) 
    //Remove when possible 
    const occupancyVacancyRate = await occupancyReportDB.getOccupancyAndVacanyRateByClientIdAndDateRange({
      clientId,
      startDate,
      endDate,
    });

    occupancyRate = occupancyVacancyRate.occupancyRate;
    vacancyRate = occupancyVacancyRate.vacancyRate;

    const newTenants = await occupancyDB.getNewTenantByDateRange({
      clientId,
      startDate,
      endDate,
    });

    const moveOutTenants = await occupancyDB.getMoveOutTenantByDateRange({
      clientId,
      startDate,
      endDate,
    });

    const cancelledReservationTenants = await occupancyDB.getCancelledReservationTenantByDateRange({
      clientId,
      startDate,
      endDate,
    });

    const newTenantsLastYear = await occupancyDB.getNewTenantByDateRange({
      clientId,
      startDate: moment(startDate).subtract(1, "year").format("YYYY-MM-DD"),
      endDate: moment(endDate).subtract(1, "year").format("YYYY-MM-DD"),
    });

    const moveOutTenantsLastYear = await occupancyDB.getMoveOutTenantByDateRange({
      clientId,
      startDate: moment(startDate).subtract(1, "year").format("YYYY-MM-DD"),
      endDate: moment(endDate).subtract(1, "year").format("YYYY-MM-DD"),
    });

    const cancelledReservationTenantsLastYear = await occupancyDB.getCancelledReservationTenantByDateRange({
      clientId,
      startDate: moment(startDate).subtract(1, "year").format("YYYY-MM-DD"),
      endDate: moment(endDate).subtract(1, "year").format("YYYY-MM-DD"),
    });

    let periodLength = moment(endDate).diff(moment(startDate), "months") + 1;

    const newTenantsLastPeriod = await occupancyDB.getNewTenantByDateRange({
      clientId,
      startDate: moment(startDate).subtract(periodLength, "months").startOf("month").format("YYYY-MM-DD"),
      endDate: moment(endDate).subtract(periodLength, "months").endOf("month").format("YYYY-MM-DD"),
    });

    const moveOutTenantsLastPeriod = await occupancyDB.getMoveOutTenantByDateRange({
      clientId,
      startDate: moment(startDate).subtract(periodLength, "months").startOf("month").format("YYYY-MM-DD"),
      endDate: moment(endDate).subtract(periodLength, "months").endOf("month").format("YYYY-MM-DD"),
    });

    const cancelledReservationTenantsLastPeriod = await occupancyDB.getCancelledReservationTenantByDateRange({
      clientId,
      startDate: moment(startDate).subtract(periodLength, "months").startOf("month").format("YYYY-MM-DD"),
      endDate: moment(endDate).subtract(periodLength, "months").endOf("month").format("YYYY-MM-DD"),
    });

    const tenantStats = {
      newTenants: newTenants || 0,
      moveOutTenants: moveOutTenants || 0,
      cancelledReserveTenants: cancelledReservationTenants || 0,
      newTenantsLastPeriod: newTenantsLastPeriod || 0,
      moveOutTenantsLastPeriod: moveOutTenantsLastPeriod || 0,
      cancelledReserveTenantsLastPeriod: cancelledReservationTenantsLastPeriod || 0,
      newTenantsLastYear: newTenantsLastYear || 0,
      moveOutTenantsLastYear: moveOutTenantsLastYear || 0,
      cancelledReserveTenantsLastYear: cancelledReservationTenantsLastYear || 0,
    };

    let profitPerBed = 0;
    let profitPerBedOccupied = 0;

    //Revenue Per tenant AND Profit per bed (monthly graph data)
    let monthlyRevenuePerTenant: any = [];
    let monthlyProfitPerBed: any = [];
    let curDate = moment(startDate).endOf("month").startOf("day");
    log.info(`Start Date [${startDate}], End Date [${endDate}], Cur Date [${curDate}]`);

    while (moment(curDate).isSameOrBefore(moment(endDate).startOf("day"))) {

      const activeTenants = await occupancyDB.getActiveTenantCountForDateRange({
        clientId: clientId,
        startDate: moment(curDate).startOf("month").format("YYYY-MM-DD"),
        endDate: moment(curDate).endOf("month").format("YYYY-MM-DD"),
      });
      const transactionAmount = await transactionDB.getTotalByDateRangeWithoutSecurity({
        clientId: clientId,
        startDate: moment(curDate).startOf("month").format("YYYY-MM-DD"),
        endDate: moment(curDate).endOf("month").format("YYYY-MM-DD"),
      });
      const expenseAmount = await expenseDB.getTotalByClientIdAndDateRangeDueDate({
        clientId: clientId,
        startDate: moment(curDate).startOf("month").format("YYYY-MM-DD"),
        endDate: moment(curDate).endOf("month").format("YYYY-MM-DD"),
      });

      monthlyRevenuePerTenant.push({
        month: moment(curDate).month() + 1,
        year: moment(curDate).year(),
        revenue: (transactionAmount / activeTenants).toFixed(2)
      });

      const bedCount = await occupancyReportDB.getBedCountByClientIdAndYearMonth({
        clientId,
        month: moment(curDate).month() + 1,
        year: moment(curDate).year(),
      });

      let rawProfitPerBed = Number(((Number(transactionAmount) - Number(expenseAmount)) / bedCount?.totalBeds).toFixed(2));
      rawProfitPerBed = Number.isFinite(rawProfitPerBed) ? rawProfitPerBed : 0;

      let rawProfitPerBedOccupied = Number(((Number(transactionAmount) - Number(expenseAmount)) / bedCount?.occupiedBeds).toFixed(2));
      rawProfitPerBedOccupied = Number.isFinite(rawProfitPerBedOccupied) ? rawProfitPerBedOccupied : 0;

      monthlyProfitPerBed.push({
        month: moment(curDate).month() + 1,
        year: moment(curDate).year(),
        allBedProfit: rawProfitPerBed || 0,
        occupiedBedProfit: rawProfitPerBedOccupied || 0,
        // allBedProfit: Number(((Number(transactionAmount) - Number(expenseAmount)) / bedCount?.totalBeds).toFixed(2)) || 0,
        // occupiedBedProfit: Number(((Number(transactionAmount) - Number(expenseAmount)) / bedCount?.occupiedBeds).toFixed(2)) || 0,
      });

      profitPerBed += rawProfitPerBed || 0;
      profitPerBedOccupied += rawProfitPerBedOccupied || 0;
      // profitPerBed += Number(((Number(transactionAmount) - Number(expenseAmount)) / bedCount?.totalBeds).toFixed(2)) || 0;
      // profitPerBedOccupied += Number(((Number(transactionAmount) - Number(expenseAmount)) / bedCount?.occupiedBeds).toFixed(2)) || 0;
      curDate = moment(curDate).add(1, "month").endOf("month").startOf("day");
    }

    let noOfMonths = moment(endDate).diff(startDate, "months") + 1;
    profitPerBed = Number(profitPerBed / noOfMonths);
    profitPerBedOccupied = Number(profitPerBedOccupied / noOfMonths);

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Start Date [${startDate}], End Date [${endDate}], Term [${term}], Analytics data fetched successfully`
    );

    return res.status(200).json({
      msg: "Analytics data fetched successfully",
      isSuccess: true,
      profitPerBed: Number(profitPerBed).toFixed(2) || 0,
      profitPerBedOccupiedOnly: Number(profitPerBedOccupied).toFixed(2) || 0,
      avgTenantDuration: Number(avgTenantDuration) || 0,
      tenantAcquisitionCost: Number(tenantAcquisitionCost) || 0,
      revenuePerTenant: Number(revenuePerTenant) || 0,
      activeTenants: Number(activeTenants) || 0,
      graphData: stats || [],
      occupancyRate: Number(Number(occupancyRate).toFixed(2)) || 0,
      vacancyRate: Number(Number(vacancyRate).toFixed(2)) || 0,
      tenantStats: tenantStats,
      monthlyRevenuePerTenant: monthlyRevenuePerTenant || [],
      monthlyProfitPerBed: monthlyProfitPerBed || [],
    });

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

clients.ProfitLossAnalytics = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ProfitLossAnalytics";

  try {
    let { startDate, endDate, propId } = req.query;

    let userType = req.userType;

    log.info(`[${C}], [${F}], Start Date [${startDate}], End Date [${endDate}], Prop Id [${propId}]`);

    if (!startDate) {
      startDate = moment().startOf("month").format("YYYY-MM-DD");
    }
    if (!endDate) {
      endDate = moment().endOf("month").format("YYYY-MM-DD");
    }

    startDate = moment(startDate as string).format("YYYY-MM-DD");
    endDate = moment(endDate as string).format("YYYY-MM-DD");

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${isPartner ? "Partner Requesting" : "Client Requesting"}....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );
      if (
        staff.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
        staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN &&
        staff.role !== CONSTANTS.STAFF_ROLES.WARDEN &&
        staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE
      ) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
    }

    if (propId && Number(propId)) {
      const incomePerMonthForProp = await transactionDB.getIncomeByMonthAndforProp({
        clientId,
        propId,
        startDate,
        endDate,
      }) || [];
      const securityPerMonthForProp = await transactionDB.getSecurityByMonthAndforProp({
        clientId,
        propId,
        startDate,
        endDate,
      }) || [];
      const expensePerMonthForProp = await expenseDB.getExpenseByMonthForProp({
        clientId,
        propId,
        startDate,
        endDate,
      }) || [];

      const incomeMap = new Map<string, { income: number; propId: any; propName: string; month: number; year: number }>();
      incomePerMonthForProp.forEach(({ month, year, totalIncome, propId, propName }: any) => {
        const key = `${month}-${year}`;
        incomeMap.set(
          key,
          {
            income: totalIncome || 0,
            propId,
            propName,
            month,
            year,
          }
        );
      });

      const securityMap = new Map<string, { income: number; propId: any; propName: string; month: number; year: number }>();
      // securityPerMonthForProp.forEach(({  month, year, totalIncome, propId, propName }: any) => {
      //   const key = `${month}-${year}`;
      //   securityMap.set(
      //     key,
      //     {
      //       income: totalIncome || 0,
      //       propId,
      //       propName,
      //       month,
      //       year,
      //     }
      //   );
      // });

      for (const { month, year, totalIncome, propId, propName } of securityPerMonthForProp) {
        const usedSecurity = await ledgerDB.getSecurityUsedByMonthYearForProp({
          propId,
          startDate: moment().month(month - 1).year(year).startOf("month").format("YYYY-MM-DD"),
          endDate: moment().month(month - 1).year(year).endOf("month").format("YYYY-MM-DD"),
        });

        let security = Number(totalIncome || 0) + Number(usedSecurity || 0);
        const key = `${month}-${year}`;
        securityMap.set(key, {
          income: security,
          propId,
          propName,
          month,
          year,
        });
      }

      const expenseMap = new Map<string, { expense: number; propId: any; propName: string; month: number; year: number }>();
      expensePerMonthForProp.forEach(({ month, year, totalExpense, propId, propName }: any) => {
        const key = `${month}-${year}`;
        expenseMap.set(
          key,
          {
            expense: totalExpense || 0,
            propId,
            propName,
            month,
            year,
          }
        );
      });

      const allMonthYearKeys = new Set<string>();
      incomePerMonthForProp.forEach(({ month, year }: any) => allMonthYearKeys.add(`${month}-${year}`));
      securityPerMonthForProp.forEach(({ month, year }: any) => allMonthYearKeys.add(`${month}-${year}`));
      expensePerMonthForProp.forEach(({ month, year }: any) => allMonthYearKeys.add(`${month}-${year}`));

      const profitPerMonthForProp = Array.from(allMonthYearKeys).map(key => {
        const incomeEntry = incomeMap.get(key);
        const securityEntry = securityMap.get(key);
        const expenseEntry = expenseMap.get(key);

        const income = incomeEntry?.income || 0;
        const security = securityEntry?.income || 0;
        const expense = expenseEntry?.expense || 0;
        const propName = incomeEntry?.propName || expenseEntry?.propName || securityEntry?.propName || "";
        const month = incomeEntry?.month || expenseEntry?.month || securityEntry?.month || "";
        const year = incomeEntry?.year || expenseEntry?.year || securityEntry?.year || "";

        return {
          propId,
          propName,
          income: Number(income),
          security: Number(security),
          expense: Number(expense),
          profit: Number(income) - Number(expense),
          month: month,
          year: year,
        };
      }).sort((a: any, b: any) => {
        if (b.year !== a.year) return b.year - a.year;
        return b.month - a.month;
      });

      const totalIncome = await transactionDB.getRentByPropIdandDateRangeWithoutSecurity({
        propId,
        startDate,
        endDate,
      });

      const totalSecurity = await transactionDB.getTotalSecurityInHandByDateRangeAndPropId({
        propId,
        startDate,
        endDate,
      });

      // const totalSecurityOld = await transactionDB.getSecurityByPropIdandDateRange({
      //   propId,
      //   startDate,
      //   endDate,
      // });

      const totalExpense = await expenseDB.getTotalByDateRangeForProp({
        propId,
        startDate,
        endDate,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Start Date [${startDate}], End Date [${endDate}], Prop Id [${propId}] Property Analytics Sent Successfully`
      );
      return res.status(200).json({
        msg: "Property Analytics fetched successfully.",
        isSuccess: true,
        data: {
          profitPerProperty: profitPerMonthForProp || [],
          summary: {
            totalIncome: totalIncome.amount,
            totalExpense: totalExpense,
            totalSecurity: totalSecurity,
            totalProfit: Number(totalIncome.amount) - Number(totalExpense),
          }
        },
      });
    } else {
      const incomePerProperty = await transactionDB.getIncomeByDateRangeAndPropForClient({
        clientId,
        startDate,
        endDate,
      }) || [];

      const expensePerProperty = await expenseDB.getExpenseByDateRangeAndPropForClient({
        clientId,
        startDate,
        endDate,
      }) || [];

      const securityPerProperty = await transactionDB.getSecurityByDateRangeAndPropForClient({
        clientId,
        startDate,
        endDate,
      }) || [];

      const incomeMap = new Map<string, number>();
      incomePerProperty.forEach(({ propId, totalIncome }: { propId: any; totalIncome: any }) => {
        incomeMap.set(propId, Number(totalIncome));
      });

      const securityMap = new Map<string, number>();
      securityPerProperty.forEach(({ propId, totalIncome }: { propId: any; totalIncome: any }) => {
        securityMap.set(propId, Number(totalIncome));
      });

      const expenseMap = new Map<string, number>();
      expensePerProperty.forEach(({ propId, totalExpense }: { propId: any; totalExpense: any }) => {
        expenseMap.set(propId, Number(totalExpense));
      });

      const allPropIds = new Set<string>();
      incomePerProperty.forEach(({ propId }: any) => allPropIds.add(propId));
      securityPerProperty.forEach(({ propId }: any) => allPropIds.add(propId));
      expensePerProperty.forEach(({ propId }: any) => allPropIds.add(propId));

      const profitPerProperty = await Promise.all(Array.from(allPropIds).map(async (propId) => {
        const incomeEntry = incomePerProperty.find((e: any) => e.propId === propId);
        const securityEntry = securityPerProperty.find((e: any) => e.propId === propId);
        const expenseEntry = expensePerProperty.find((e: any) => e.propId === propId);

        const usedSecurity = await ledgerDB.getSecurityUsedByMonthYearForProp({
          propId,
          startDate,
          endDate,
        });

        let totalSecurity = securityEntry?.totalIncome || 0;
        totalSecurity = Number(totalSecurity || 0) + Number(usedSecurity || 0);

        const income = incomeEntry?.totalIncome || 0;
        // const security = securityEntry?.totalIncome || 0;
        const security = totalSecurity;
        const expense = expenseEntry?.totalExpense || 0;
        const propName = incomeEntry?.propName || expenseEntry?.propName || securityEntry?.propName || "";

        return {
          propId,
          propName,
          income: Number(income),
          security: Number(security),
          expense: Number(expense),
          profit: Number(income) - Number(expense),
        };
      }));

      const totalIncome = await transactionDB.getTotalByDateRangeWithoutSecurity({
        clientId,
        startDate,
        endDate,
      });

      // const totalSecurity = await transactionDB.getTotalSecurityByDateRange({
      //   clientId,
      //   startDate,
      //   endDate,
      // });

      const totalSecurity = await transactionDB.getTotalSecurityInHandByDateRange({
        clientId,
        startDate,
        endDate,
      });

      const totalExpense = await expenseDB.getTotalByClientIdAndDateRangeDueDate({
        clientId,
        startDate,
        endDate,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Start Date [${startDate}], End Date [${endDate}], Analytics Sent Successfully`
      );
      return res.status(200).json({
        msg: "Analytics fetched successfully.",
        isSuccess: true,
        data: {
          profitPerProperty: profitPerProperty || [],
          summary: {
            totalIncome: totalIncome,
            totalSecurity: totalSecurity,
            totalExpense: totalExpense,
            totalProfit: Number(totalIncome) - Number(totalExpense),
          }
        },
      });
    }
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

clients.UploadLandlordDocs = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "UploadLandlordDocs";

  const files = req.files as Express.Multer.File[];
  const removeTmpImages = async () => {
    for (const file of files) {
      try {
        await fsPromises.unlink(file.path);
      } catch (err) { }
    }
  };

  try {
    const { landlordId, type, propId = null, flatId = null, } = req.body;

    log.info(
      `[${C}], [${F}], Landlord Id [${landlordId}], Document Type [${type}], Prop Id [${propId}], Flat Id [${flatId}], Is File Uploaded [${files ? `Yes, File [${JSON.stringify(files)}]` : "No"}]`
    );

    const userType = req.userType;

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`}, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        await removeTmpImages();
        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}], Unauthorized Access By Staff`
        );
        await removeTmpImages();
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

    if (!files[0]) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], No files uploaded`
      );
      return res
        .status(400)
        .json({ msg: "No files uploaded", isSuccess: false });
    }

    const folderName = `landlord_${landlordId}`;
    const folderPath = `uploads/documents/client_${clientId}/${folderName}`;
    const urlBase = folderPath.replace(
      "uploads/",
      `${process.env.UPLOAD_PATH}/`
    );

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

    const oldFrontPath = `uploads/tmp/${files[0].filename}`;
    let oldBackPath = "";
    if (files[1]) oldBackPath = `uploads/tmp/${files[1].filename}`;

    // const isExists = await landlordDocumentDB.getIDByType({
    //   landlordId,
    //   clientId: clientId,
    //   type: type,
    // });
    const isExists = await landlordDocumentDB.getIDByClientIdAndPropIdAndFlatIdAndType({
      landlordId,
      clientId: clientId,
      type: type,
      propId: propId || null,
      flatId: flatId || null,
    });

    const ext = files[0].mimetype.split("/")[1];
    const fileName = `Id_document_${type}.${ext}`;

    const newPath = `${folderPath}/${fileName}`;
    await fsPromises.copyFile(oldFrontPath, newPath);
    const url = `${urlBase}/${fileName}`;

    if (isExists) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Type [${type}], Document re-uploaded successfully`
      );

      await landlordDocumentDB.updateDoc({
        landlordId,
        clientId: clientId,
        flatId: flatId || null,
        propId: propId || null,
        type: type,
        value: url,
        id: isExists.id,
      });
    } else {
      await landlordDocumentDB.addDoc({
        landlordId,
        clientId: clientId,
        propId: propId || null,
        type: type,
        value: url,
        flatId: flatId || null,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Type [${type}], Document uploaded successfully`
      );
    }

    const kycStatus = Number(type) === CONSTANTS.DOCUMENT_TYPES.AADHAAR
      ? CONSTANTS.KYC_STATUS.SELFI_UPLOADED
      : Number(type) === CONSTANTS.DOCUMENT_TYPES.RENT_AGREEMENT
        ? CONSTANTS.KYC_STATUS.RENT_AGREEMENT_DONE
        : Number(type) === CONSTANTS.DOCUMENT_TYPES.PAN
          ? CONSTANTS.KYC_STATUS.PAN_VERIFICATION_DONE
          : false;


    if (kycStatus) {
      await clientLandlordDB.updateKycStatus({
        clientId,
        landlordId,
        kycStatus: kycStatus,
      });
    }

    if (oldBackPath) {
      const ext = files[1].mimetype.split("/")[1];
      const fileName = `Id_document_back.${ext}`;

      const newPath = `${folderPath}/${fileName}`;
      await fsPromises.copyFile(oldBackPath, newPath);
      const url = `${urlBase}/${fileName}`;

      // const isExists = await landlordDocumentDB.getIDByType({
      //   landlordId,
      //   clientId: clientId,
      //   type: CONSTANTS.DOCUMENT_TYPES.AADHAAR_BACK,
      // });
      const isExists = await landlordDocumentDB.getIDByClientIdAndPropIdAndFlatIdAndType({
        landlordId,
        clientId: clientId,
        type: CONSTANTS.DOCUMENT_TYPES.AADHAAR_BACK,
        propId: propId || null,
        flatId: flatId || null,
      });

      if (isExists) {
        await landlordDocumentDB.updateDoc({
          landlordId,
          clientId: clientId,
          propId: propId || null,
          type: CONSTANTS.DOCUMENT_TYPES.AADHAAR_BACK,
          value: url,
          id: isExists.id,
          flatId: flatId || null,
        });

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Type [AADHAAR], URL [${url}], Aadhaar Back document re-uploaded successfully`
        );
      } else {
        await landlordDocumentDB.addDoc({
          landlordId,
          clientId: clientId,
          propId: propId || null,
          type: CONSTANTS.DOCUMENT_TYPES.AADHAAR_BACK,
          value: url,
          flatId: flatId || null,
        });

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Type [AADHAAR], URL [${url}], Aadhaar Back document uploaded successfully`
        );
      }
    }

    const docs = await landlordDocumentDB.getByClientIdAndLandlordId({ landlordId, clientId });
    if (docs && docs.length > 0) {
      for (let doc of docs) {
        const title = await getDocumentTitle(doc.type);
        doc.title = title;
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Type [${type}] Document uploaded sucessfully`
    );

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

    await removeTmpImages();

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

clients.AddLandlordBankDetails = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "AddLandlordBankDetails";

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

  try {
    const { holderName, accountNum, ifsc, bankAddress, bankName, landlordId } = req.body;

    log.info(
      `[${C}], [${F}], Landlord Id [${landlordId}], Holder Name [${holderName}], Account Number [${accountNum}], IFSC [${ifsc}], Bank Address [${bankAddress}], Bank Name [${bankName}], Is File Uploaded [${file ? `Yes, File [${JSON.stringify(file)}]` : "No"}]`
    );

    const userType = req.userType;

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`}, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        await removeTmpImages();
        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}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

    const landlordBankAccountId = await landlordBankAccountsDB.addBank({
      clientId,
      landlordId,
      holderName,
      accountNum,
      ifsc,
      bankAddress,
      bankName,
    });

    if (file) {
      const folderName = `landlord_${landlordId}`;
      const folderPath = `uploads/documents/client_${clientId}/${folderName}`;
      const fileExtension = file.mimetype.split("/")[1];
      const filename = `cancelledCheque_${landlordBankAccountId}.${fileExtension}`;

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

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

      await fsPromises.copyFile(oldPath, newPath);

      const isExists = await landlordDocumentDB.getIDByType({
        landlordId,
        clientId: clientId,
        type: CONSTANTS.DOCUMENT_TYPES.CANCELLED_CHEQUE,
      });

      if (isExists) {
        await landlordDocumentDB.updateDoc({
          landlordId,
          clientId: clientId,
          propId: null,
          type: CONSTANTS.DOCUMENT_TYPES.CANCELLED_CHEQUE,
          value: url,
          id: isExists.id,
        });

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Type [AADHAAR], URL [${url}], Cancelled Cheque re-uploaded successfully`
        );
      } else {
        await landlordDocumentDB.addDoc({
          landlordId,
          clientId: clientId,
          propId: null,
          type: CONSTANTS.DOCUMENT_TYPES.CANCELLED_CHEQUE,
          value: url,
        });

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Type [AADHAAR], URL [${url}], Cancelled Cheque uploaded successfully`
        );
      }

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

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Holder Name [${holderName}], Account Number [${accountNum}], IFSC [${ifsc}], Bank Address [${bankAddress}], Bank Name [${bankName}], Is File Uploaded [${file ? `Yes, File [${JSON.stringify(file)}]` : "No"}], Bank Added sucessfully`
    );

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

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

clients.EditLandlordBankDetails = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "AddLandlordBankDetails";

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

  try {
    const { holderName, accountNum, ifsc, bankAddress, bankName, bankAccountId } = req.body;

    log.info(
      `[${C}], [${F}], Bank Account Id [${bankAccountId}], Holder Name [${holderName}], Account Number [${accountNum}], IFSC [${ifsc}], Bank Address [${bankAddress}], Bank Name [${bankName}], Is File Uploaded [${file ? `Yes, File [${JSON.stringify(file)}]` : "No"}]`
    );

    const userType = req.userType;

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`}, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        await removeTmpImages();
        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}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

    const landlordBankAccount = await landlordBankAccountsDB.getById({
      id: bankAccountId,
    });

    if (!landlordBankAccount) {
      log.info(
        `[${C}], [${F}], Bank Account Id [${bankAccountId}], No Record Found With Id`
      );

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

    await landlordBankAccountsDB.update({
      id: bankAccountId,
      holderName,
      accountNum,
      ifsc,
      bankAddress,
      bankName,
    });

    const landlordId = landlordBankAccount.landlordId;

    if (file) {
      const folderName = `landlord_${landlordId}`;
      const folderPath = `uploads/documents/client_${clientId}/${folderName}`;
      const fileExtension = file.mimetype.split("/")[1];
      const filename = `cancelledCheque_${bankAccountId}.${fileExtension}`;

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

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

      await fsPromises.copyFile(oldPath, newPath);

      const isExists = await landlordDocumentDB.getIDByType({
        landlordId,
        clientId: clientId,
        type: CONSTANTS.DOCUMENT_TYPES.CANCELLED_CHEQUE,
      });

      if (isExists) {
        await landlordDocumentDB.updateDoc({
          landlordId,
          clientId: clientId,
          propId: null,
          type: CONSTANTS.DOCUMENT_TYPES.CANCELLED_CHEQUE,
          value: url,
          id: isExists.id,
        });

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Type [AADHAAR], URL [${url}], Cancelled Cheque re-uploaded successfully`
        );
      } else {
        await landlordDocumentDB.addDoc({
          landlordId,
          clientId: clientId,
          propId: null,
          type: CONSTANTS.DOCUMENT_TYPES.CANCELLED_CHEQUE,
          value: url,
        });

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Type [AADHAAR], URL [${url}], Cancelled Cheque uploaded successfully`
        );
      }

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

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Holder Name [${holderName}], Account Number [${accountNum}], IFSC [${ifsc}], Bank Address [${bankAddress}], Bank Name [${bankName}], Is File Uploaded [${file ? `Yes, File [${JSON.stringify(file)}]` : "No"}], Bank Added sucessfully`
    );

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

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

clients.ListLandlordBankDetails = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListLandlordBankDetails";

  try {
    const { landlordId } = req.query;

    log.info(
      `[${C}], [${F}], Landlord Id [${landlordId}]`
    );

    const userType = req.userType;

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

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

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

    const landlordBanks = await landlordBankAccountsDB.getByClientIdAndLandlordId({
      clientId,
      landlordId,
    });

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

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

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

clients.DeleteLandlordBankDetails = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "DeleteLandlordBankDetails";

  try {
    const { bankAccountId } = req.body;

    log.info(
      `[${C}], [${F}], Landlord Bank Account Id [${bankAccountId}]`
    );

    const userType = req.userType;

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

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

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

    const bankAccountDetails = await landlordBankAccountsDB.getById({
      id: bankAccountId,
    });

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

    await landlordBankAccountsDB.deleteById({
      id: bankAccountId,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${bankAccountDetails.landlordId}], Bank Account Id [${bankAccountId}], Landlord Bank Details Deleted Successfully`
    )

    return res.status(200).json({
      msg: "Bank details deleted sucessfully",
      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,
    });
  }
};

clients.AddPartner = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "AddPartner";

  try {
    const { name, mobile, email, ownershipPercentage, pan, firmName, gstNo } = req.body;
    let personalAndPartnerProperties = [];

    log.info(
      `[${C}], [${F}], Name [${name}], Mobile [${mobile}], Email [${email}], Ownership Percentage [${ownershipPercentage}], Pan [${pan}], Firm Name [${firmName}], GST Number [${gstNo}]`
    );

    const userType = req.userType;

    if (userType !== CONSTANTS.USER_TYPE.CLIENT) {
      log.info(
        `[${C}], [${F}], User Id [${req.id}], User Type [${userType}], Only Client Can Add Partner`
      );

      return res.status(400).json({
        msg: "Unauthorized Access",
        isSuccess: false,
      });
    }

    const client = await clientDB.getById({
      id: req.id,
    });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], No Client Found`
      );

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

    // if (client.parentId) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${client.id}], Parent Id [${client.parentId}], Partner Client Cannot Make A Partner`
    //   );

    //   return res.status(400).json({
    //     msg: "Parnter clients cannot make partners.",
    //     isSuccess: true,
    //   });
    // }

    const isExists = await clientDB.getByMobile({
      mobile,
    });
    if (isExists) {
      log.info(
        `[${C}], [${F}], Client Id [${client.id}], Parent Id [${client.parentId}], Client Already Exists`
      );

      return res.status(400).json({
        msg: "Client already registered with mobile",
        isSuccess: true,
      });
    }

    const isStaffExists = await staffDB.getActiveByMobileAndClientId({
      mobile,
      clientId: client.id,
    });
    if (isStaffExists) {
      log.info(
        `[${C}], [${F}], Client Id [${client.id}], Staff Id [${isStaffExists.mobile}], Staff Exists With Mobile`
      );

      return res.status(400).json({
        msg: "Staff registered with mobile, cannot make partner",
        isSuccess: true,
      });
    }

    await clientDB.addPartner({
      mobile,
      name,
      businessEmail: email,
      ownershipPercentage,
      parentId: client.id,
      panNumber: pan,
      city: client.city || null,
      businessName: firmName || null,
      gstNo: gstNo || null,
    });

    personalAndPartnerProperties = await propertyDB.getPersonalAndPartnerProperties({
      clientId: client?.id,
    });
    return res.status(200).json({
      msg: "Partner added successfully.",
      isSuccess: true,
      personalAndPartnerProperties: personalAndPartnerProperties || [],
    });

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

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

clients.EditPartner = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditPartner";

  try {
    const { id, name, email, ownershipPercentage, pan, firmName, gstNo } = req.body;
    let personalAndPartnerProperties = [];
    log.info(
      `[${C}], [${F}], Name [${name}], Partner Id [${id}], Email [${email}], Ownership Percentage [${ownershipPercentage}], Pan [${pan}], Business Name [${firmName}], GST Number [${gstNo}]`
    );

    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}], Partner` : `Client Id [${clientId}]`} Requesting....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], No Staff Found`
        );

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

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

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

        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }

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

    const partnerClient = await clientDB.getById({
      id,
    });
    if (!partnerClient) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Partner Id [${id}], No Partner Found`
      );

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

    await clientDB.editPartner({
      name,
      businessEmail: email,
      ownershipPercentage,
      panNumber: pan,
      businessName: firmName,
      gstNo: gstNo || null,
      id,
    });
    personalAndPartnerProperties = await propertyDB.getPersonalAndPartnerProperties({
      clientId: clientId,
    });
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Partner Id [${id}], Name [${name}], Email [${email}], Ownership Percentage [${ownershipPercentage}], Pan [${pan}], Partner Details Edited Successfully`
    )

    return res.status(200).json({
      msg: "Partner edited successfully.",
      isSuccess: true,
      personalAndPartnerProperties: personalAndPartnerProperties || [],
    });

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

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

clients.ListPartners = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListPartners";

  try {
    const { s, t, pageNum = 1 } = req.query;
    const userType = req.userType;

    let limit = 10;

    log.info(`Page Number [${pageNum}], Limit [${limit}], Search Value [${s}], Search Type [${t}], Platform [${req.platform}]`);

    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}], Partner` : `Client Id [${clientId}]`} Requesting....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], No Staff Found`
        );

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

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

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

        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }

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

    let partners = [];
    if (s && s !== "" && s !== 'undefined') {
      partners = req.platform === CONSTANTS.TENANT_DEVICE_TYPE.WEB ? await clientDB.getPartnersBySearch({
        id: clientId,
        searchVal: s,
        searchType: t,
      })
        : await clientDB.getPartnersBySearchApp({
          id: clientId,
          searchVal: s,
        });
    } else {
      partners = await clientDB.getParnters({
        id: clientId,
        pageNum: pageNum,
        limit: limit,
        platform: req.platform,
      });
    }

    if (partners && partners.length > 0) {
      for (let partner of partners) {
        const propCount = await propertyDB.getCount({
          clientId: partner.id,
        });

        partner.propCount = propCount?.count || 0;
      }
    }

    log.info(`[${C}], [${F}], Client Id [${clientId}], Partner List Fetched Successfully`);

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

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

clients.ListTenantsWithMissingRent = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListTenantsWithMissingRent";

  try {
    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}], Partner` : `Client Id [${clientId}]`} Requesting....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], No Staff Found`
        );

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

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

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

        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }

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

    const tenants = await occupancyDB.getTenantsWithMissingRent({
      clientId,
    });

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

    return res.status(200).json({
      msg: "Tenants with missing rent fetched successfully.",
      isSuccess: true,
      list: tenants || [],
      count: tenants.length || 0,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

clients.DeleteTenant = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "DeleteTenant";

  try {
    const { tenantId } = req.body;

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}],`
    );

    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}], Partner` : `Client Id [${clientId}]`} Requesting....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], No Staff Found`
        );

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

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

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

        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }

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


    let allActiveOccupancies = await occupancyDB.getByClientIdAndTenantId({
      clientId,
      tenantId: tenantId,
    });
    if (allActiveOccupancies) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Active Occupancy Found With Id`
      );

      return res.status(400).json({
        msg: `Cannot delete active tenants.`,
        isSuccess: false,
      });
    }

    let allOccupancies = await moveOutDB.getByClientIdAndTenantId({
      clientId,
      tenantId: tenantId,
    });
    if (!allOccupancies) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found With Id`
      );

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

    //deleting from necessary tables

    await duesDB.removeAllDues({
      tenantId,
      clientId,
    });

    await moveOutDuesDB.removeAllByClientIdAndTenantId({
      tenantId,
      clientId,
    });

    await transactionDB.deleteByClientIdAndTenantId({
      clientId,
      tenantId,
    });

    await ledgerDB.removeAllEntries({
      clientId,
      tenantId,
    });

    await documentDB.removeAllByTenantIdandClientId({
      clientId,
      tenantId,
    });

    await complaintDB.removeByClientIdAndTenantId({
      clientId,
      tenantId,
    });

    await requestDB.removeByClientIdAndTenantId({
      clientId,
      tenantId,
    });

    await hiddenDuesDB.removeByTenantIdAndClientId({
      clientId,
      tenantId,
    });

    await parcelDB.deleteAllByClientIdAndTenantId({
      clientId,
      tenantId,
    });

    await tenantAttendanceDB.deleteAllByClientIdAndTenantId({
      clientId,
      tenantId,
    });

    // if (allOccupancies && !isEvicted) {
    //   for (const occupancy of allOccupancies) {

    //     const bed = await bedDB.getById({ id : occupancy.bedId});
    //     if (bed.status === CONSTANTS.BED_STATUS.RESERVED) {
    //       await bedDB.updateStatus({
    //         id: occupancy.bedId,
    //         status: CONSTANTS.BED_STATUS.VACANT_RESERVED,
    //       });
    //     } else {
    //       await bedDB.updateStatus({
    //         id: occupancy.bedId,
    //         status: CONSTANTS.BED_STATUS.VACANT,
    //       });
    //     }

    //     const room = await roomDB.getById({ id: occupancy.roomId });
    //     const bedCount = await bedDB.getCountsByRoomId({
    //       id: occupancy.roomId,
    //     });
    //     const totalBeds = bedCount.totalBeds;

    //     const vacantBed = await bedDB.getVacantBeds({
    //       roomId: room.id,
    //       status: CONSTANTS.BED_STATUS.VACANT,
    //     });
    //     if (vacantBed && vacantBed.length === Number(totalBeds)) {
    //       await roomDB.updateStatus({
    //         id: room.id,
    //         status: CONSTANTS.ROOM_STATUS.VACANT,
    //       });
    //     } else {
    //       await roomDB.updateStatus({
    //         id: room.id,
    //         status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
    //       });
    //     }
    //   }
    // }

    if (allOccupancies) {
      for (const occupancy of allOccupancies) {
        await moveOutDB.removeById({
          id: occupancy.id,
        });
      }
    }

    await logActivity(
      Number(req.userType),
      Number(req.id),
      Number(req.parentClientId!),
      Number(req.platform),
      Number(tenantId),
      CONSTANTS.ACTIVITY_TYPES.DELETE_TENANT,
      null,
      null,
      null,
      null,
      null
    );

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

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

clients.PendingTasks = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "PendingTasks";

  try {
    const userType = req.userType;

    log.info(`[${C}], [${F}], User Id [${req.id}], User Type [${userType}], Get Pending Tasks`);

    let pendingTasks;

    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"}....`
      );
      pendingTasks = await ClientPendingTasks(clientId);
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });

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

      pendingTasks = await StaffPendingTasks(clientId, Number(req.id));
    }
    const isManualMoveInEnabled = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_MANUAL_MOVEIN,
    });

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

    return res.status(200).json({
      msg: "Pending tasks fetched successfully.",
      isSuccess: true,
      totalPendingTasks: pendingTasks.totalPendingTasks,
      data: {
        tenantActions: pendingTasks.tenantActions,
        financeActions: pendingTasks.financeActions,
        operationActions: pendingTasks.operationActions,
      },
      isManualMoveInEnabled: isManualMoveInEnabled ? Number(isManualMoveInEnabled?.value) : 0,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

clients.ListRentAgreementRenewalSetting = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListRentAgreementRenewalSetting";

  try {
    const { propId } = req.query;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Property Id [${propId}], Platform [${req.platform}], User Type [${userType}]`);

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

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

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

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(
          `[${C}], [${F}], 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....`
      );
    }

    const agreementSettings = await argeementRenewConfigDB.getByClientIdAndPropId({ clientId, propId: Number(propId) });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Rent Agreement Renewal Setting Sent Successfully`
    );

    return res.status(200).json({
      msg: "Rent agreement renewal setting fetched successfully.",
      isSuccess: true,
      data: agreementSettings || [],
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

clients.EditRentAgreementRenewalSetting = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditRentAgreementRenewalSetting";

  try {
    let { autoRenew, rentIncrement, rentIncrementType, agreementCharges, updateSecurity, propIds, } = req.body;
    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Property Ids [${propIds}], Auto Renew [${autoRenew}], Rent Increment [${rentIncrement}], Rent Increment Type [${rentIncrementType}], Agreement Charges [${agreementCharges}], Update Security [${updateSecurity}], Platform [${req.platform}], User Type [${userType}]`
    );
    if (!Number(rentIncrementType)) {
      rentIncrementType = null;
    }

    if (!Number(rentIncrement)) {
      rentIncrement = null;
    }

    if (propIds && typeof propIds === "string" && propIds.trim() !== "") {
      propIds = propIds.split(",").map((id) => Number(id.trim()));
    }

    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: "Unauthorized Access",
          isSuccess: false,
        });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(
          `[${C}], [${F}], 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....`
      );
    }


    if (propIds && propIds.length > 0) {
      for (let propId of propIds) {

        const agreementSettings = await argeementRenewConfigDB.getByClientIdAndPropId({ clientId, propId: Number(propId) });

        if (!agreementSettings) {
          await argeementRenewConfigDB.create({
            clientId,
            propId,
            autoRenew,
            rentIncrementType,
            rentIncrementValue: Number(rentIncrement),
            updateSecurity,
            agreementCharges,
          });
        } else {
          await argeementRenewConfigDB.update({
            clientId,
            propId,
            autoRenew,
            rentIncrementType,
            rentIncrementValue: Number(rentIncrement),
            updateSecurity,
            agreementCharges,
          });
        }
      }
    } else {
      const agreementSettings = await argeementRenewConfigDB.getByClientIdAndPropId({ clientId, propId: Number(propIds) });

      if (!agreementSettings) {
        await argeementRenewConfigDB.create({
          clientId,
          propId: propIds,
          autoRenew,
          rentIncrementType,
          rentIncrementValue: Number(rentIncrement),
          updateSecurity,
          agreementCharges,
        });
      } else {
        await argeementRenewConfigDB.update({
          clientId,
          propId: propIds,
          autoRenew,
          rentIncrementType,
          rentIncrementValue: Number(rentIncrement),
          updateSecurity,
          agreementCharges,
        });
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Property Ids [${propIds}], Auto Renew [${autoRenew}], Rent Increment [${rentIncrement}], Rent Increment Type [${rentIncrementType}], Agreement Charges [${agreementCharges}], Update Security [${updateSecurity}], Rent Agreement Renewal Setting Updated Successfully`
    );

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

clients.ListTenantOnboardingSetting = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListTenantOnboardingSetting";

  try {

    const { propId } = req.query;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Property Id [${propId}], Platform [${req.platform}], User Type [${userType}]`);

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

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

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

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(
          `[${C}], [${F}], 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....`
      );
    }

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

    const isOnlinePaymentEnabled = property.isOnlinePaymentEnabled || 0;
    const isIdVerificationEnabled = property.isIdVerificationEnabled || 0;
    const isPoliceVerificationEnabled = property.isPoliceVerificationEnabled || 0;
    const isRentAgreementEnabled = property.isRentAgreementEnabled || 0;

    let isSkipKycEnabled = await clientConfigDB.getClientConfigByPropId({
      clientId,
      propId: Number(propId),
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.TENANT_ONBOARDING_SETTINGS,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.TENANT_ONBOARDING_SETTINGS.SKIP_KYC,
    });

    if (!isSkipKycEnabled) {
      isSkipKycEnabled = 0;
    } else {
      isSkipKycEnabled = isSkipKycEnabled.value || 0;
    }

    const tenantOnBoardingSettings = {
      isOnlinePaymentEnabled: Number(isOnlinePaymentEnabled),
      isIdVerificationEnabled: Number(isIdVerificationEnabled),
      isPoliceVerificationEnabled: Number(isPoliceVerificationEnabled),
      isRentAgreementEnabled: Number(isRentAgreementEnabled),
      isSkipKycEnabled: Number(isSkipKycEnabled),
    };

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Onboarding Setting Sent Successfully`
    );

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

clients.EditTenantOnboardingSetting = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditTenantOnboardingSetting";

  try {

    let { propIds, ekyc, onlinePayment, policeVerification, rentAgreement, skipKyc } = req.body;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Property Ids [${propIds}], Ekyc [${ekyc}], Online Payment [${onlinePayment}], Police Verification [${policeVerification}], Rent Agreement [${rentAgreement}], Skip Kyc [${skipKyc}], Platform [${req.platform}], User Type [${userType}]`);

    if (propIds && typeof propIds === "string" && propIds.trim() !== "") {
      propIds = propIds.split(",").map((id) => Number(id.trim()));
    } else {
      propIds = [propIds];
    }

    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: "Unauthorized Access",
          isSuccess: false,
        });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(
          `[${C}], [${F}], 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....`
      );
    }

    if (propIds && propIds.length > 0) {
      for (let propId of propIds) {

        await propertyDB.updateIsIdVerificationEnabled({
          id: propId,
          isIdVerificationEnabled: ekyc,
        });

        let isSkipKycConfig = await clientConfigDB.getClientConfigByPropId({
          clientId,
          propId: Number(propId),
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.TENANT_ONBOARDING_SETTINGS,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.TENANT_ONBOARDING_SETTINGS.SKIP_KYC,
        });

        if (!isSkipKycConfig) {
          await clientConfigDB.create({
            clientId,
            propId: Number(propId),
            provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.TENANT_ONBOARDING_SETTINGS,
            type: CONSTANTS.CLIENT_CONFIG_TYPE.TENANT_ONBOARDING_SETTINGS.SKIP_KYC,
            value: skipKyc,
          });
        } else {
          await clientConfigDB.update({
            clientId,
            propId: Number(propId),
            provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.TENANT_ONBOARDING_SETTINGS,
            type: CONSTANTS.CLIENT_CONFIG_TYPE.TENANT_ONBOARDING_SETTINGS.SKIP_KYC,
            value: skipKyc,
          });
        }

        let toggleOnlinePayment = true;
        if (Number(onlinePayment) === 1) {
          toggleOnlinePayment = await canToggleOnlinePayment({
            clientId,
            propId: Number(propId),
          })
        }
        if (toggleOnlinePayment) {
          await propertyDB.updateIsOnlinePaymentEnabled({
            id: propId,
            isOnlinePaymentEnabled: onlinePayment,
          });
        } else {
          log.info(`[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Online Payment [${onlinePayment}], Online Payment Toggle Skipping Due To Missing Relevant Values`)
        }

        await propertyDB.updateIsRentAgreementEnabled({
          id: propId,
          isRentAgreementEnabled: rentAgreement,
        });

        await propertyDB.updateIsPoliceVerificationEnabled({
          id: propId,
          isPoliceVerificationEnabled: policeVerification,
        });
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propIds}], Ekyc [${ekyc}], Online Payment [${onlinePayment}], Police Verification [${policeVerification}], Rent Agreement [${rentAgreement}], Skip Kyc [${skipKyc}], Tenant Onboarding Setting Updated Successfully`
    );

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

clients.ListFinanceSetting = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListFinanceSetting";

  try {
    const userType = req.userType;

    log.info(`[${C}], [${F}], Platform [${req.platform}], User Type [${userType}]`);

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

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

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

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(
          `[${C}], [${F}], 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....`
      );
    }

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

    let canHideDues = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.CAN_HIDE_DUES,
    });
    if (!canHideDues) {
      canHideDues = 0;
    } else {
      canHideDues = canHideDues.value || 0;
    }

    let expenseAllocationByBed = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EXPENSE_ALLOCATION_BY_BEDS,
    });
    if (!expenseAllocationByBed) {
      expenseAllocationByBed = 0;
    } else {
      expenseAllocationByBed = expenseAllocationByBed.value || 0;
    }

    let partialPayment = 1;
    properties.forEach((property: any) => {
      if (Number(property.isPartialPaymentEnabled) === 0) {
        partialPayment = 0;
      }
    });

    let autoReminders = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.DUE_AUTO_REMINDERS,
    });
    if (!autoReminders) {
      autoReminders = 0;
    } else {
      autoReminders = autoReminders.value || 0;
    }

    let manualReminders = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.SEND_RENT_REMINDER,
    });
    if (!manualReminders) {
      manualReminders = 0;
    } else {
      manualReminders = manualReminders.value || 0;
    }

    const dueSettings = {
      canHideDues: Number(canHideDues),
      partialPayment: Number(partialPayment),
      autoReminders: Number(autoReminders),
      manualReminders: Number(manualReminders),
      expenseAllocationByBed: Number(expenseAllocationByBed),
    };

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

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

clients.EditFinanceSetting = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditFinanceSetting";

  try {

    let { propIds, canHideDues, partialPayment, autoReminders, manualReminders, expenseAllocationByBed } = req.body;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Property Ids [${propIds}], Can Hide Dues [${canHideDues}], Partial Payment [${partialPayment}], Auto Reminders [${autoReminders}], Manual Reminders [${manualReminders}], Expense Allocation By Bed [${expenseAllocationByBed}], Platform [${req.platform}], User Type [${userType}]`);

    if (propIds && typeof propIds === "string" && propIds.trim() !== "") {
      propIds = propIds.split(",").map((id) => Number(id.trim()));
    } else {
      propIds = [propIds];
    }

    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: "Unauthorized Access",
          isSuccess: false,
        });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(
          `[${C}], [${F}], 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 autoRemindersConfig = await clientConfigDB.getClientConfigByPropId({
    //   clientId,
    //   propId: Number(propId),
    //   provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
    //   type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.DUE_AUTO_REMINDERS,
    // });
    // if (!autoRemindersConfig) {
    //   await clientConfigDB.create({
    //     clientId,
    //     propId: Number(propId),
    //     provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
    //     type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.DUE_AUTO_REMINDERS,
    //     value: Number(autoReminders),
    //   });
    // } else {
    //   await clientConfigDB.update({
    //     clientId,
    //     propId: Number(propId),
    //     provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
    //     type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.DUE_AUTO_REMINDERS,
    //     value: Number(autoReminders),
    //   });
    // }

    let canHideDueConfig = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.CAN_HIDE_DUES,
    });

    if (!canHideDueConfig) {
      await clientConfigDB.create({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.CAN_HIDE_DUES,
        value: Number(canHideDues),
      });
    } else {
      await clientConfigDB.update({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.CAN_HIDE_DUES,
        value: Number(canHideDues),
      });
    }

    let expenseAllocationByBedConfig = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EXPENSE_ALLOCATION_BY_BEDS,
    });

    if (!expenseAllocationByBedConfig) {
      await clientConfigDB.create({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EXPENSE_ALLOCATION_BY_BEDS,
        value: Number(expenseAllocationByBed),
      });
    } else {
      await clientConfigDB.update({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EXPENSE_ALLOCATION_BY_BEDS,
        value: Number(expenseAllocationByBed),
      });
    }

    let manualReminderConfig = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.SEND_RENT_REMINDER,
    });

    if (!manualReminderConfig) {
      await clientConfigDB.create({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.SEND_RENT_REMINDER,
        value: Number(manualReminders),
      });
    } else {
      await clientConfigDB.update({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.SEND_RENT_REMINDER,
        value: Number(manualReminders),
      });
    }

    // let partialPaymentConfig = property.isPartialPaymentEnabled;
    const properties = await propertyDB.getAllByClientId({
      clientId,
    });

    properties.forEach(async (property: any) => {
      await propertyDB.updateIsPartialPaymentEnabled({
        id: property.id,
        isPartialPaymentEnabled: partialPayment,
      });
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propIds}], Can Hide Dues [${canHideDues}], Partial Payment [${partialPayment}], Auto Reminders [${autoReminders}], Manual Reminders [${manualReminders}], Due Setting Updated Successfully`
    );

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

clients.ListEvictionSetting = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListEvictionSetting";

  try {

    const { propId } = req.query;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Property Id [${propId}], Platform [${req.platform}], User Type [${userType}]`);

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

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

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

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(
          `[${C}], [${F}], 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....`
      );
    }

    const evictionSetting = await evictionConfigDB.getByClientIdAndPropId({
      clientId,
      propId,
    });

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

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

clients.EditEvictionSetting = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditEvictionSetting";

  try {

    let { propIds, lockInServe, clearDues, restrictMoveOut, midCycleCharge } = req.body;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Property Ids [${propIds}], Lock-In Serve [${lockInServe}], Clear Dues [${clearDues}], Restrict Move Out [${restrictMoveOut}], Mid Cycle Move-Out Charge [${midCycleCharge}], Platform [${req.platform}], User Type [${userType}]`);

    if (propIds && typeof propIds === "string" && propIds.trim() !== "") {
      propIds = propIds.split(",").map((id) => Number(id.trim()));
    } else {
      propIds = [propIds];
    }

    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: "Unauthorized Access",
          isSuccess: false,
        });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(
          `[${C}], [${F}], 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....`
      );
    }

    if (propIds && propIds.length > 0) {
      for (let propId of propIds) {

        const property = await propertyDB.getById({
          id: propId,
        });
        if (!property) {
          log.info(`[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], No Property Found With Id, Skipping`);
          continue;
        }

        const evictionSetting = await evictionConfigDB.getByClientIdAndPropId({
          clientId,
          propId,
        });

        if (!evictionSetting) {
          await evictionConfigDB.create({
            clientId,
            propId,
            lockInServe,
            clearDues,
            midCycleCharge,
            restrictMoveOut,
          });
        } else {
          await evictionConfigDB.update({
            clientId,
            propId,
            lockInServe,
            clearDues,
            midCycleCharge,
            restrictMoveOut,
          });
        }
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propIds}], Lock-In Serve [${lockInServe}], Clear Dues [${clearDues}], Restrict Move Out [${restrictMoveOut}], Mid Cycle Move-Out Charge [${midCycleCharge}], Eviction Setting Updated Successfully`
    );

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

clients.TallySync = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "TallySync";

  try {
    const userType = req.userType;

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

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

      if (!staff) {
        log.info(
          `[${C}], [${F}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          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....`
      );
    }

    const isTallyEnabled = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.TALLY,
    });

    if (!isTallyEnabled || Number(isTallyEnabled.value) !== 1) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tally Not Enabled`
      );

      return res.status(400).json({
        msg: "Tally not enabled.",
        isSuccess: false,
      });
    }

    const isTallyUp = await checkTallyOnlineStatus(Number(clientId));

    if (!isTallyUp) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Tally Is Offline`);

      return res.status(400).json({
        msg: "Tally is down currently, make sure that Tally is online before syncing",
        isSuccess: false,
      });
    }

    await syncOccupancyTallyStatus(Number(clientId));
    await syncDuesTallyStatus(Number(clientId));
    await syncTransactionTallyStatus(Number(clientId));
    await syncExpenseJournalTally(Number(clientId));
    await syncExpensePaymentTally(Number(clientId));
    await syncPropertyIncomeSecurityLedgerTally(Number(clientId));

    log.info(`[${C}], [${F}], Client Id [${clientId}], Tally Sync Initiated`);

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

clients.ListReceiptTerms = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListReceiptTerms";

  try {
    const userType = req.userType;

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

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

      if (!staff) {
        log.info(
          `[${C}], [${F}], 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 receiptTerms = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.RECEIPT_TERMS,
    });

    if (!receiptTerms) {
      receiptTerms = `<ul>
      <li>
      This is an acknowledgment receipt of the payment made by the tenant
      through whatsoever mode of payment for the corresponding services.
      </li>
      <li>
      If an online payment fails for any reason, this receipt will be considered null and void.
      </li>
      <li>
      Refunds will not be provided under any circumstances for this receipt.
      </li>
      </ul>`;
    } else {
      receiptTerms = receiptTerms.value;
    }

    let stayRules = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.STAY_RULES,
    });

    if (stayRules) {
      stayRules = stayRules?.value;
    } else {
      stayRules = null;
    }

    log.info(`[${C}], [${F}], Client Id [${clientId}], Receipt Terms Fetched`);

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

clients.EditReceiptTerms = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditReceiptTerms";

  try {
    const userType = req.userType;

    const { receiptTerms, stayRules } = req.body;

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

    log.info(`[${C}], [${F}], Client Id [${clientId}], Rent Terms [${receiptTerms}], Stay Rules [${stayRules}]`);

    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 terms = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.RECEIPT_TERMS,
    });

    if (!terms) {
      await clientConfigDB.create({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.RECEIPT_TERMS,
        value: receiptTerms,
      });
    } else {
      await clientConfigDB.update({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.RECEIPT_TERMS,
        value: receiptTerms,
        propId: null,
      });
    }

    let stayRulesConfig = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.STAY_RULES,
    });

    if (typeof stayRules !== "undefined") {
      if (!stayRulesConfig) {
        await clientConfigDB.create({
          clientId,
          propId: null,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.STAY_RULES,
          value: stayRules,
        });
      } else {
        await clientConfigDB.update({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.STAY_RULES,
          value: stayRules,
          propId: null,
        });
      }
    }

    log.info(`[${C}], [${F}], Client Id [${clientId}], Receipt/Stay Terms Updated`);

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

clients.ListTallySettings = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListTallySettings";

  try {
    const userType = req.userType;

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

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

      if (!staff) {
        log.info(
          `[${C}], [${F}], 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 ip = null;
    let port = null;
    let companyName = null;
    let billRefPrefix = null;
    let lastSync = null;

    let isTallyEnabled = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.TALLY,
    });

    isTallyEnabled = isTallyEnabled ? Number(isTallyEnabled.value) : 0;

    const tallySettings = await tallyConfigDB.getByClientId({
      clientId,
    });

    if (tallySettings) {
      ip = tallySettings.ip;
      port = tallySettings.port;
      companyName = tallySettings.companyName;
      billRefPrefix = tallySettings.billRefPrefix;
      lastSync = tallySettings.lastSync ? moment(tallySettings.lastSync).format("DD MMM YY, hh:mm A") : null;
    }

    log.info(`[${C}], [${F}], Client Id [${clientId}], Tally Settings Fetched`);

    return res.status(200).json({
      msg: "Tally settings fetched successfully.",
      isSuccess: true,
      data: {
        ip,
        port,
        companyName,
        billRefPrefix,
        isTallyEnabled,
        lastSync,
      },
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

clients.UpdateTallySettings = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "UpdateTallySettings";

  try {

    const { ip, port, billRefPrefix, isTallyEnabled } = req.body;
    const userType = req.userType;

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

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

      if (!staff) {
        log.info(
          `[${C}], [${F}], 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....`
      );
    }

    const tallySettings = await tallyConfigDB.getByClientId({
      clientId,
    });

    if (Number(isTallyEnabled) === 1 && !tallySettings) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Is Tally Enabled [${isTallyEnabled}], Trying To Enable Tally, But No Config Found`);

      return res.status(400).json({
        msg: "Please contact Kipinn support to enable this feature",
        isSuccess: false,
      });
    }

    if (tallySettings) {
      await tallyConfigDB.update({
        clientId,
        ip,
        port,
        billRefPrefix,
      });
    }

    log.info(`[${C}], [${F}], Client Id [${clientId}], Tally Settings Updated`);

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

// clients.ListRentAgreementTerms = async (req: CustomRequest, res: Response) => {
//   const C = "Client Controller";
//   const F = "ListRentAgreementTerms";

//   try {
//     const userType = req.userType;

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

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

//       if (!staff) {
//         log.info(
//           `[${C}], [${F}], 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 rentAgreement = `uploads/documents/clients_${clientId}/rent_agreement.html`;

//     if (!fs.existsSync(rentAgreement)) {
//       rentAgreement = `uploads/documents/defaults/rent_agreement.html`;
//     }

//     rentAgreement = fs.readFileSync(rentAgreement, "utf8");

//     log.info(`[${C}], [${F}], Client Id [${clientId}], Rent Agreement Fetched Successfully`);

//     return res.status(200).json({
//       msg: "Rent agreement base document fetched successfully.",
//       isSuccess: true,
//       rentAgreement,
//     });
//   } catch (error: any) {
//     log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
//     return res.status(500).json({
//       msg: CONSTANTS.MSG.ERROR_MESSAGE,
//       isSuccess: false,
//     });
//   }
// };

// clients.EditRentAgreementTerms = async (req: CustomRequest, res: Response) => {
//   const C = "Client Controller";
//   const F = "EditRentAgreementTerms";

//   try {
//     const userType = req.userType;

//     const { rentAgreementHtml } = req.body;

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

//     log.info(`[${C}], [${F}], Client Id [${clientId}], Rent Agreement HTML [${rentAgreementHtml}]`);

//     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 rentAgreement = `uploads/documents/clients_${clientId}/rent_agreement.html`;

//     if (!fs.existsSync(rentAgreement)) {
//       await fs.mkdirSync(`uploads/documents/clients_${clientId}`, { recursive: true });
//     }

//     await fs.writeFileSync(rentAgreement, rentAgreementHtml, "utf8");

//     log.info(`[${C}], [${F}], Client Id [${clientId}], Rent Agreement Updated`);

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

clients.ListGlobalConfiguration = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListGlobalConfiguration";
  try {
    const userType = req.userType;

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

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

      if (!staff) {
        log.info(
          `[${C}], [${F}], 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 manualMoveIn = 0;
    let manualMoveOut = 0;
    let autoAssignComplaint = 0;
    let bookingAmt = 0;

    let isExsitManualMoveIn = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_MANUAL_MOVEIN
    });
    if (isExsitManualMoveIn) manualMoveIn = isExsitManualMoveIn?.value;

    let isExsitManualMoveOut = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_MANUAL_MOVEOUT,
    });
    if (isExsitManualMoveOut) manualMoveOut = isExsitManualMoveOut?.value;

    let isExsitComplaintAutoAssign = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.AUTO_ASSIGN_COMPLAINT,
    });
    if (isExsitComplaintAutoAssign) autoAssignComplaint = isExsitComplaintAutoAssign?.value;

    let isExsitBookingAmt = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.BOOKING_AMT_ENABLED,
    });
    if (isExsitBookingAmt) bookingAmt = isExsitBookingAmt?.value;

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Manual Move-in, [${manualMoveIn}], Manual Move-out [${manualMoveOut}], Auto Assign Complaint [${autoAssignComplaint}], Booking Amt Enabled [${bookingAmt}], Global Config Fetched Successfully`
    );

    return res.status(200).json({
      msg: "Global configuration fetched successfully",
      data: {
        manualMoveIn: Number(manualMoveIn) || 0,
        manualMoveOut: Number(manualMoveOut) || 0,
        autoAssignComplaint: Number(autoAssignComplaint) || 0,
        bookingAmt: Number(bookingAmt) || 0,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

clients.EditGlobalConfiguration = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditGlobalConfiguration";
  try {
    let { manualMoveIn, manualMoveOut, autoAssignComplaint, bookingAmt, } = req.body;
    const userType = req.userType;

    // if (Number(manualMoveIn) === 0 && Number(manualMoveOut) === 1) manualMoveIn = 1;

    log.info(
      `[${C}], [${F}], Manual Move-in, [${manualMoveIn}], Manual Move-out [${manualMoveOut}], Auto Assign Complaint [${autoAssignComplaint}], Booking Amt Enabled [${bookingAmt}]`
    );



    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 isExsitManualMoveIn = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_MANUAL_MOVEIN
    });

    let isExsitManualMoveOut = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_MANUAL_MOVEOUT,
    });

    let manualMoveInOriginal = isExsitManualMoveIn ? isExsitManualMoveIn?.value : 0;
    let manualMoveOutOriginal = isExsitManualMoveOut ? isExsitManualMoveOut?.value : 0;

    if (Number(manualMoveInOriginal) === 1 && Number(manualMoveIn) === 0) manualMoveOut = 0;
    if (Number(manualMoveOutOriginal) === 0 && Number(manualMoveOut) === 1) manualMoveIn = 1;

    if (isExsitManualMoveIn) {
      await clientConfigDB.update({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_MANUAL_MOVEIN,
        value: manualMoveIn,
      });
    } else {
      await clientConfigDB.create({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_MANUAL_MOVEIN,
        value: manualMoveIn,
      });
    }

    if (isExsitManualMoveOut) {
      await clientConfigDB.update({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_MANUAL_MOVEOUT,
        value: manualMoveOut,
      });
    } else {
      await clientConfigDB.create({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_MANUAL_MOVEOUT,
        value: manualMoveOut,
      });
    }

    let isExsitComplaintAutoAssign = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.AUTO_ASSIGN_COMPLAINT,
    });
    if (isExsitComplaintAutoAssign) {
      await clientConfigDB.update({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.AUTO_ASSIGN_COMPLAINT,
        value: autoAssignComplaint,
      });
    } else {
      await clientConfigDB.create({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.AUTO_ASSIGN_COMPLAINT,
        value: autoAssignComplaint,
      });
    }

    let isExsitBookingAmt = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.BOOKING_AMT_ENABLED,
    });
    if (isExsitBookingAmt) {
      await clientConfigDB.update({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.BOOKING_AMT_ENABLED,
        value: bookingAmt,
      });
    } else {
      await clientConfigDB.create({
        clientId,
        propId: null,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.BOOKING_AMT_ENABLED,
        value: bookingAmt,
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Manual Move-in, [${manualMoveIn}], Manual Move-out [${manualMoveOut}], Auto Assign Complaint [${autoAssignComplaint}], Booking Amt Enabled [${bookingAmt}], Global Config Updated Successfully`
    );

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

clients.EditProfile = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditProfile";
  try {
    const { name, mobile, email, businessName, gstNo, panNumber, city, businessEmail, businessAddress, } = req.body;
    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Name [${name}], Email [${email}], Mobile [${mobile}], Business Name [${businessName}], GST No [${gstNo}], PAN Number [${panNumber}], City [${city}], Business Email [${businessEmail}], Business Address [${businessAddress}]`
    );

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

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

    await clientDB.updateCompleteProfile({
      name: name || null,
      businessEmail: businessEmail || null,
      businessName: businessName || null,
      panNumber: panNumber || null,
      gstNo: gstNo || null,
      businessAddress: businessAddress || null,
      id: clientId,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Email [${email}], Mobile [${mobile}], Business Name [${businessName}], GST No [${gstNo}], PAN Number [${panNumber}], City [${city}], Business Email [${businessEmail}], Business Address [${businessAddress}], Client Profile Updated Successfully`
    );

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

clients.ListStampPapers = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ListStampPapers";
  try {
    const userType = req.userType;

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

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

      if (!staff) {
        log.info(
          `[${C}], [${F}], 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 && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN) {
        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 = await stampPaperDB.getStatsByClientId({
    //   clientId,
    // });
    // let stats = {
    //   total: 0,
    //   available: 0,
    //   used: 0,
    // }
    // if (list) {
    //   let rowCount = 1;
    //   for (let item of list) {
    //     item.id = rowCount;
    //     stats.available = stats.available + item.available;
    //     stats.used = stats.used + item.used;
    //     rowCount++;
    //   }
    // }
    // stats.total = stats.available + stats.used;

    let stats = {
      total: 0,
      available: 0,
      used: 0,
    }

    let summary = await stampPaperDB.getSummaryByClientId({ clientId });
    let stamps = await stampPaperDB.getStampsByClientId({ clientId });

    const propertyMap: any = {};
    if (false != summary) {
      summary.forEach((row: any) => {
        propertyMap[row.propId] = {
          ...row,
          stamps: [],
        };
        stats.available += Number(row.available || 0);
        stats.used += Number(row.used || 0);
        stats.total += Number(row.available || 0) + Number(row.used || 0);
      });
    }
    if (false != stamps) {
      stamps.forEach((stamp: any) => {
        if (!propertyMap[stamp.propId]) return;

        propertyMap[stamp.propId].stamps.push({
          stampId: stamp.stampId,
          status: stamp.status,
          tenantName: stamp.tenantName,
          updatedOn: stamp.updatedAt,
          rentAgreement: stamp.rentAgreementDoc,
        });
        //stats.total++;
      });
    }
    const list = Object.values(propertyMap);
    let properties = await propertyDB.getStampEnabledByClientId({ clientId });
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], stamppaper list sent successfully`
    );

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

clients.ShiftStamps = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ShiftStamps";
  try {
    const { propId, toPropId, noOfStamps } = req.body;
    const userType = req.userType;

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

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], From Property Id [${propId}], To Property Id [${toPropId}], No Of Stamps [${noOfStamps}], 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 {
      // log.info(
      //   `[${C}], [${F}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
      // );
      // return res.status(400).json({
      //   msg: "Unauthorized Access",
      //   isSuccess: false,
      // });
    }

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

    let getAvailableStaffs = await stampPaperDB.getAvailableStampsByClientIdAndPropId({
      clientId,
      propId,
    });

    if (!getAvailableStaffs || getAvailableStaffs.length < 1) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Failed to shift stamps as stamps are not available on the source property`);
      return res.status(400).json({
        msg: "Stamps can only be shift if available",
        isSuccess: false,
      });
    }

    if (getAvailableStaffs.length < noOfStamps) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Failed to shift stamps as available stamps [${getAvailableStaffs.length}] are less than requested shift count [${noOfStamps}]`);
      return res.status(400).json({
        msg: "Entered stamps are greater then the available stamps",
        isSuccess: false,
      });
    }
    await stampPaperDB.ShiftStampPapers({
      fromPropId: propId,
      toPropId,
      noOfStamps
    });
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], From Property Id [${propId}], To Property Id [${toPropId}], No Of Stamps [${noOfStamps}], Stamps shifted successfully`
    );

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

clients.EditMoveOutDate = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "EditMoveOutDate";

  try {
    const { tenantId, moveOutDate } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move-Out Date [${moveOutDate}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move-In Date [${moveOutDate}], Client Requested....`
      );
    }

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

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

    const tenant = await tenantDB.getById({ id: tenantId });

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move-In Date [${moveOutDate}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move-In Date [${moveOutDate}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await occupancyDB.updateMoveOutDateNew({
      tenantId,
      clientId,
      moveOutDate,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Move-In Date [${moveOutDate}], Move-Out Date Updated Successfully`
    );

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

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

clients.GetTenantDataForQRCode = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "GetTenantDataForQRCode";

  try {
    const { tenantId, qrType = 1, mealType, startDate, endDate, propIds, markMealConsumed = 1 } = req.query;
    // start and endDate only for laundry currently 2026-07-07
    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], QR Type [${qrType}], Meal Type [${mealType}], Start Date [${startDate}], endDate [${endDate}], Prop Ids [${propIds}], Mark Meal Consumed [${markMealConsumed}]`
    )

    let propFilter: null | any[] = null;

    if (propIds && typeof propIds === 'string' && String(propIds).toLowerCase().trim() !== "undefined" && String(propIds).toLowerCase().trim() !== "null") {
      propFilter = propIds.split(',').map(v => v.trim()).filter(Boolean);
      if (Array.isArray(propIds) && propIds.includes('0')) {
        propFilter = null;
      }
    }

    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}], Client Id [${clientId}], Staff Id [${req.id}], No Staff Found`
        );

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

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`
      );
      return res.status(400).json({
        msg: "No Occupancy Found",
        isSuccess: false
      });
    }

    const 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
      });
    }

    let mealSelection = [];
    let isAlreadyConsumed = 0;
    let laundryData = [];
    let weightLimit: any;
    let laundryWeightUsed = 0;
    let laundryWeightUsedCurMonth = 0;
    let isBusEnabled = occupancy.isBusOpted;

    weightLimit = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.LAUNDRY_LIMIT_PER_TENANT,
    });


    if (Number(qrType) === CONSTANTS.QR_TYPE.FOOD) {
      if (propFilter && Array.isArray(propFilter) && propFilter.length > 0) {
        if (!propFilter.includes(String(occupancy.propId))) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Prop Ids [${propFilter}], Occupancy Prop Id [${occupancy.propId}], Tenant Does Not Belong To Selected Properties`
          );
          return res.status(400).json({
            msg: "Tenant does not belong to selected properties",
            isSuccess: false
          });
        }
      }
      if (mealType && Number(mealType)) {

        const isSelectionEnforced = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.ENFORCE_TENANT_MEAL_SELECTION,
        });

        mealSelection = await foodDB.getMealSelectionByTenantIdAndFilters({
          tenantId,
          clientId,
          mealDate: moment().format("YYYY-MM-DD"),
          mealType: Number(mealType),
          propIds: propFilter || null,
        });
        if (mealSelection && mealSelection.length === 1 && Number(mealSelection[0]?.status) !== CONSTANTS.MEAL_CONSUMPTION_STATUS.OPT_OUT) {
          if (mealSelection[0].status === CONSTANTS.MEAL_CONSUMPTION_STATUS.CONSUMED) {
            isAlreadyConsumed = 1;
          } else if (Number(markMealConsumed) === 1) {
            await foodDB.updateTenantMealStatus({
              id: mealSelection[0].id,
              status: CONSTANTS.MEAL_CONSUMPTION_STATUS.CONSUMED,
            });
          }

          // mealSelection = await foodDB.getMealSelectionByTenantIdAndDateAndType({
          //   tenantId,
          //   clientId,
          //   mealDate: moment().format("YYYY-MM-DD"),
          //   mealType: Number(mealType),
          // });
        } else if (!mealSelection && isSelectionEnforced && Number(isSelectionEnforced?.value) === 1 && Number(markMealConsumed) === 1) {
          // CreateMealSelection For Tenant
        } else {
          //Already consumed 0 - Not Consumed , 1 - Consumed,  2 No Meal Selection
          if (false === mealSelection) {
            isAlreadyConsumed = 2;
          }
        }
      }
      mealSelection = await foodDB.getTenantMealSelectionByFilters({
        tenantId,
        clientId,
        mealDate: moment().format("YYYY-MM-DD"),
        propIds: propFilter || null,
      });
    } else if (Number(qrType) === CONSTANTS.QR_TYPE.LAUNDRY) {
      laundryData = await laundryRequestsDB.getByTenantIdAndClientIdAndDate({
        tenantId,
        clientId,
        startDate: startDate || moment().startOf("month").format("YYYY-MM-DD"),
        endDate: endDate || moment().endOf("month").format("YYYY-MM-DD"),
      });

      laundryWeightUsedCurMonth = await laundryRequestsDB.getWeightUsedByClientIdAndTenantIdAndDate({
        tenantId,
        clientId,
        startDate: moment().startOf("month").format("YYYY-MM-DD"),
        endDate: moment().endOf("month").format("YYYY-MM-DD"),
      })

      if (laundryData && laundryData.length > 0) {
        for (const laundry of laundryData) {
          if (laundry.weight) {
            laundryWeightUsed += Number(laundry.weight.toFixed(2));
          }
        }
      }
    }

    const tenantInfo = {
      tenantName: tenant.name,
      tenantGender: tenant.gender,
      profilePic: occupancy.profilePicture,
      propName: occupancy.propName,
      roomNum: occupancy.roomNum,
      moveInDate: occupancy.moveInDate,
      isFoodOpted: occupancy.isFoodOpted,
      kycStatus: occupancy.kycStatus,
      stayType: occupancy.stayType,
      rent: occupancy.rent,
      rentalCycle: occupancy.rentalCycle,
      noticePeriod: occupancy.noticePeriod,
      lockInPeriod: occupancy.lockInPeriod,
      security: occupancy.security,
      agreementPeriod: occupancy.agreementPeriod,
      agreementStartDate: occupancy.agreementStartDate,
      rentalType: occupancy.rentalType,
      isPoliceVerified: occupancy.isPoliceVerified,
      isRentAgreementSigned: occupancy.isRentAgreementSigned,
      isOnlinePaymentEnabled: occupancy.isOnlinePaymentEnabled,
    }
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], QR Type [${qrType}], Meal Type [${mealType}], isAlreadyConsumed [${isAlreadyConsumed}], Details Sent Successfully`
    );

    return res.status(200).json({
      msg: "Tenant data fetched successfully",
      isSuccess: true,
      tenant: tenantInfo,
      mealSelection: mealSelection || [],
      isAlreadyConsumed: isAlreadyConsumed,
      laundryData: {
        data: laundryData || [],
        weightLimit: weightLimit?.value || null,
        laundryWeightUsed: laundryWeightUsed ? laundryWeightUsed.toFixed(2) : 0,
        laundryWeightUsedCurMonth: laundryWeightUsedCurMonth ? laundryWeightUsedCurMonth.toFixed(2) : 0,
      },
      qrType: qrType || 1,
      isBusEnabled: isBusEnabled || 0,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

clients.ConsentToMovementRequest = async (req: CustomRequest, res: Response) => {
  const C = "Client Controller";
  const F = "ConsentToMovementRequest";

  try {
    const userType = req.userType;
    const { requestId, status, reason = null } = req.body;
    let clientId = req.id;
    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      const clientId = req.id;
      log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Status [${status}], Client Requesting....`);

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

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

    } else {
      const staffId = req.id;
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Request Id [${requestId}], Status [${status}], Staff Requesting....`);

      const staff = await staffDB.getById({ id: staffId });

      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      clientId = staff?.clientId || 0;
    }

    const requestDetail = await requestDB.getById({ id: requestId });
    let message = CONSTANTS.MSG.ERROR_MESSAGE;
    if (!requestDetail) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request does not exist`);
    } else if (CONSTANTS.REQUEST_STATUS.PENDING !== requestDetail?.status) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request has been processed already`);
    } else {
      const occupancy = await occupancyDB.getByTenantIdAndClientId({
        clientId,
        tenantId: requestDetail.tenantId,
      })
      if (!occupancy) {
        log.info(
          `[${C}], [${F}], Tenant Id [${requestDetail?.tenantId}], Client Id [${clientId}], No Occupancy Found`
        );
        return res.status(400).json({
          msg: "Unable to send Guest request because your are not occupied at any bed",
          isSuccess: false,
        });
      }
      const notiSettings = await settingsDB.getByClientIdAndPropId({
        clientId,
        propId: requestDetail.propId,
      });

      let footerText = notiSettings.footer || "Kipinn Team";
      const property = await propertyDB.getById({ id: requestDetail.propId });
      let flatName = "";
      if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
        const { name } = await flatDB.getById({ id: occupancy.flatId });
        flatName = name;
      } else {
        flatName =
          occupancy.floor === "G" ? "Ground Floor" : "Floor " + occupancy.floor;
      }
      const room = await roomDB.getById({ id: occupancy.roomId });
      let occupancyName = `${property?.name} (${flatName}, ${room.roomNum})`;
      const tenant = await tenantDB.getById({ id: requestDetail.tenantId });

      let movementDetail = await tenantMovementDB.getByRequestId({ requestId });

      if (CONSTANTS.REQUEST_STATUS.APPROVED === Number(status)) {
        log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request approved`);
        let requestName = getRequestName(CONSTANTS.REQUEST_TYPE.TENANT_MOVEMENT);
        if (movementDetail?.type === CONSTANTS.TENANT_MOVEMENT_TYPES.LATE_ARRIVAL) {
          requestName = `Late Arrival`;
        } else {
          requestName = `Going Home`;
        }
        await sendWhatsappRequestUpdates(
          tenant?.mobile,
          tenant?.name,
          requestName,
          occupancyName,
          `accepted`,
          footerText,
          Number(clientId)
        );

        logTenantRequestActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId),
          Number(req.platform),
          movementDetail?.type === CONSTANTS.TENANT_MOVEMENT_TYPES.LATE_ARRIVAL ? CONSTANTS.ACTIVITY_TYPES.TENANT_LATE_ARRIVAL_REQUEST_ACCEPTED : CONSTANTS.ACTIVITY_TYPES.TENANT_GOING_HOME_REQUEST_ACCEPTED,
          requestDetail.propId,
          Number(requestDetail.tenantId),
          tenant.name,
          requestDetail.roomId,
          0,
        );
        message = "Request approved successfully";
        await requestDB.updateStatus({ status, id: requestId });
      } else if (CONSTANTS.REQUEST_STATUS.REJECTED === Number(status)) {
        message = "Request rejected successfully";
        log.info(`[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request rejected`);
        let requestReason = "Your request has been rejected";
        if (!reason) {
          await requestDB.updateStatus({ status, id: requestId });
        } else {
          await requestDB.updateStatusWithReason({ status, reason, id: requestId });
          requestReason = reason;
        }
        if (Number(notiSettings?.whatsApp) === 1) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], WhatsApp Enabled`
          );
          //let requestName = getRequestName(CONSTANTS.REQUEST_TYPE.GUEST);
          let requestName = getRequestName(CONSTANTS.REQUEST_TYPE.TENANT_MOVEMENT);
          if (movementDetail?.type === CONSTANTS.TENANT_MOVEMENT_TYPES.LATE_ARRIVAL) {
            requestName = `Late Arrival`;
          } else {
            requestName = `Going Home`;
          }
          await sendWhatsappRequestRejectionWithReason(
            tenant?.mobile,
            tenant?.name,
            requestName,
            property?.name,
            requestReason,
            footerText,
            Number(clientId)
          );
        } else {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], WhatsApp Not Enabled`
          );
        }

        logTenantRequestActivity(
          req.userType!,
          Number(req.id)!,
          Number(req.parentClientId),
          Number(req.platform),
          movementDetail?.type === CONSTANTS.TENANT_MOVEMENT_TYPES.LATE_ARRIVAL ? CONSTANTS.ACTIVITY_TYPES.TENANT_LATE_ARRIVAL_REQUEST_REJECTED : CONSTANTS.ACTIVITY_TYPES.TENANT_GOING_HOME_REQUEST_REJECTED,
          requestDetail.propId,
          Number(requestDetail.tenantId),
          tenant.name,
          requestDetail.roomId,
          0
        );
      }
    }

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

clients.SendBulkReminders = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Client Controller";
  const F = "SendBulkReminders";

  try {
    //1 Ekyc
    const { toUserType = CONSTANTS.USER_TYPE.TENANT, broadcastType = CONSTANTS.BULK_REMINDER.EKYC } = req.body;

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], To UserType [${toUserType}], Staff Id [${req.id}], Broadcast Type [${broadcastType}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], To UserType [${toUserType}], Broadcast Type [${broadcastType}], Client Requested....`
      );
    }

    if (Number(toUserType) === CONSTANTS.USER_TYPE.TENANT && Number(broadcastType) === CONSTANTS.BULK_REMINDER.EKYC) { //E Kyc
      let remiderScript = process.env.KYC_REMIDER_PATH;
      exec(`php ${remiderScript} ${clientId} > /dev/null 2>&1 &`);

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], To UserType [${toUserType}], Broadcast Type [${broadcastType}], Reminder broadcast sent`
      );
    }

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

    return res.status(200).json({
      msg: "eKYC reminders are being processed.",
      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,
    });
  }
};

export default clients;
