import { Response } from "express";
import moment from "moment";
import jwt from "jsonwebtoken";
import CONSTANTS from "../config/constants";
import log from "../config/log";
import bedDB from "../models/beds.model";
import clientDB from "../models/client.model";
import duesDB from "../models/dues.model";
import occupancyDB from "../models/occupancy.model";
import propertyDB from "../models/property.model";
import requestDB from "../models/request.model";
import roomDB from "../models/room.model";
import roomOptionDB from "../models/roomOption.model";
import tenantDB from "../models/tenant.model";
import bedTypes from "../schemas/bed.schema";
import roomsTypes from "../schemas/room.schema";
import CustomRequest from "../types/requestType";
import generateRoomNum, { generateRoomNumX } from "../utils/generateRoomNum";
import sendNotification from "../utils/sendNotification";
import sendSMS from "../utils/sendSMS";
import occupanciesTypes from "../schemas/occupancy.schema";
import staffDB from "../models/staff.model";
import extraChargeDB from "../models/extraCharges.model";
import convertTypes from "../utils/convertTypes";
import {
  calculateMonthlyRent,
  calculateRentPerDay,
} from "../utils/calculateRentPerDay";
import switchRoomRecordDB from "../models/switchRoomRecord.model";
import flatDB from "../models/flat.model";
import ledgerDB from "../models/ledger.model";
import generateLedgerReferenceId from "../utils/generateLedgerReferenceId";
import { get } from "http";
import getDueDescription from "../utils/getDueDescription";
import { isUserPartner } from "../utils/isUserPartner";
import generateTenantGId from "../utils/generateTenantGId";
import generateTenantTId from "../utils/generateTenantTId";
import transactionDB from "../models/transaction.model";
import moveOutDB from "../models/moveOut.model";
import notificationDB from "../models/notification.model";
import generateOccupancyGId from "../utils/generateOccupancyGId";
import { logActivity, logPropertyActivity, logTenantRequestActivity } from "../utils/logActivity";
import { sendWhatsappNewRequest, sendWhatsappRequestRejectionWithReason, sendWhatsappRequestUpdates } from "../utils/sendWhatsappWithConfig";
import settingsDB from "../models/settings.model";
import getRequestName from "../utils/getRequestName";
import { getVacantSlots } from "../utils/calculateVacantSlots";

const rooms: any = {};

rooms.TenantRoomList = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "TenantRoomList";

  try {
    const { propGId } = req.params;
    const tenantId = req.id;

    log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Prop GId [${propGId}]`);

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

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

    // Sukhbir - MultiOccupancy
    // if (tenant?.status !== CONSTANTS.TENANT_STATUS.LINKED) {
    //   log.info(
    //     `[${C}], [${F}], Tenant Id [${tenantId}], Prop GId [${propGId}], Tenant is not linked`
    //   );
    //   return res.status(400).json({
    //     msg: "You are either occupied or your profile is not updated",
    //     isSuccess: false,
    //   });
    // }

    if (!propGId.includes(CONSTANTS.PROP_GID_SUFFIX)) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Prop GId [${propGId}], Invalid Property Id`
      );

      return res
        .status(400)
        .json({ msg: "Invalid Property Id", isSuccess: false });
    }

    let property = await propertyDB.getByGId({
      gId: propGId,
    });

    if (!property) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Prop GId [${propGId}], No Property Found`
      );

      return res
        .status(400)
        .json({ msg: "Invalid Property Id", isSuccess: false });
    }

    if (property.status !== CONSTANTS.PROPERTY_STATUS.ACTIVE) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Prop GId [${propGId}], Property is not active`
      );
      return res.status(400).json({
        msg: "This Property is not active",
        isSuccess: false,
      });
    }

    const rooms = await roomDB.getVacantRoomsByPropId({
      propId: property?.id,
    });

    if (!rooms) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Prop GId [${propGId}], No Room Available`
      );

      return res.status(400).json({
        msg: "This Property doesn't have any vacant room available.",
        isSuccess: false,
      });
    }

    if (rooms && property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
      for (let room of rooms) {
        const { name } = await flatDB.getById({ id: room.flatId });
        room.flatName = "Flat No " + name;
      }
    }

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Prop GId [${propGId}], Room List Sent Successfully`
    );

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

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

rooms.ActiveRoomListOnly = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "ActiveRoomListOnly";

  try {
    const { propId } = req.params;
    let clientId = req.id;
    const userType = req.userType;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], 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}],  Prop Id [${propId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(`[${C}], [${F}],  Prop Id [${propId}], Client Requested....`);
    }

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

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

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

    const rooms = await roomDB.getActiveAndVacantRoomsByPropId({
      propId: propId,
    });

    if (rooms !== false) {
      for (let room of rooms) {
        if (room.flatId) {
          const { name } = await flatDB.getById({ id: room.flatId });
          room.flatName = name;
          //room.flatName = name + ", " + room.roomNum;
        } else {
          room.flatName = room.floor;
          //room.flatName = room.roomNum;
        }
        const { totalBeds, vacantBeds } = await bedDB.getCountsByRoomId({
          id: room.id,
        });
        room.totalBeds = totalBeds;
        room.vacantBeds = vacantBeds;
      }
    }

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

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

rooms.ActiveRoomsWithAvailableBeds = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "ActiveRoomsWithAvailableBeds";

  try {
    const { propId } = req.params;
    let clientId = req.id;
    const userType = req.userType;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], 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}],  Prop Id [${propId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(`[${C}], [${F}],  Prop Id [${propId}], Client Requested....`);
    }

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

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

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

    const rooms = await roomDB.getRoomsWithAvailableBedsByPropId({
      propId: propId,
    });

    let finalRooms = [];

    if (rooms !== false) {
      for (let room of rooms) {
        if (room.flatId) {
          const { name } = await flatDB.getById({ id: room.flatId });
          room.flatName = name;
          //room.flatName = name + ", " + room.roomNum;
        } else {
          room.flatName = room.floor;
          //room.flatName = room.roomNum;
        }
        const { totalBeds, vacantBeds } = await bedDB.getCountsByRoomId({
          id: room.id,
        });
        room.totalBeds = totalBeds;
        room.vacantBeds = vacantBeds;

        const beds = await bedDB.getAvailableBedsByRoomId({
          roomId: room.id,
        });

        room.beds = beds || [];

        if (Number(totalBeds) > 0 && Number(vacantBeds) > 0) {
          finalRooms.push(room);
        }
      }
    }

    return res.status(200).json({
      msg: "Room list sent successfully",
      isSuccess: true,
      // data: rooms || [],
      data: finalRooms || [],
      security: property.security,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

rooms.SendRoomRequest = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "SendRoomRequest";

  try {
    const { propGId, roomId, floor } = req.body;
    const tenantId = req.id;
    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}]`
    );

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

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (!propGId.includes(CONSTANTS.PROP_GID_SUFFIX)) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], Invalid Property Id`
      );

      return res
        .status(400)
        .json({ msg: "Invalid Property Id", isSuccess: false });
    }

    // Sukhbir - MultiOccupancy
    // if (tenant?.status === CONSTANTS.TENANT_STATUS.OCCUPIED) {
    //   log.info(
    //     `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], Tenant already occupied`
    //   );

    //   return res
    //     .status(400)
    //     .json({ msg: "You've already occupied a room", isSuccess: false });
    // }

    // Sukhbir - MultiOccupancy
    // if (tenant?.status === CONSTANTS.TENANT_STATUS.REQUESTED) {
    //   log.info(
    //     `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], Tenant already requested for a room`
    //   );

    //   return res.status(400).json({
    //     msg: "You've already requested for a room",
    //     isSuccess: false,
    //   });
    // }

    const property = await propertyDB.getByGId({ gId: propGId });

    if (!property) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], No Property Found`
      );
      return res
        .status(400)
        .json({ msg: "Invalid Property Id", isSuccess: false });
    }

    const client = await clientDB.getById({ id: property.clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: "Invalid Property Id", isSuccess: false });
    }

    if (tenant.mobile === client.mobile) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], Client can't sent a room request to his property`
      );
      return res.status(400).json({
        msg: "You can't sent a room request to your property. Please change the property Id.",
        isSuccess: false,
      });
    }

    const room = await roomDB.getById({ id: roomId });
    if (!room) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], No Room Found`
      );
      return res.status(400).json({ msg: "No Room Found", isSuccess: false });
    }

    if (room.status === CONSTANTS.ROOM_STATUS.OCCUPIED) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], Room already occupied`
      );
      return res
        .status(400)
        .json({ msg: "Room already occupied", isSuccess: false });
    }

    // const occupancy = await occupancyDB.getByTenantId({
    //   tenantId,
    // });
    let occupancy = await occupancyDB.getByTenantIdAndClientId({
          tenantId,
          clientId: property?.clientId,
    });

    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const title = `Room selection request received.`;
    const description = `Room (${room.roomNum}) Selection Request from ${tenant?.name} for room ${room.roomNum} of ${property.name} property`;

    const requestId = await requestDB.create({
      clientId: property.clientId,
      occupancyId: occupancy.id,
      tenantId,
      propId: property.id,
      roomId,
      floor,
      title,
      description,
      type: CONSTANTS.REQUEST_TYPE.ROOM_SELECTION,
    });

    // Sukhbir - MultiOccupancy
    // await tenantDB.updateStatus({
    //   status: CONSTANTS.TENANT_STATUS.REQUESTED,
    //   id: tenantId,
    // });

    await occupancyDB.updateStatus({
      status: CONSTANTS.OCCUPANCY_STATUS.REQUESTED,
      id: occupancy.id,
    });

    await occupancyDB.addRoomId({
      roomId,
      floor,
      id: occupancy.id,
    });

    if (Number(room.flatId)) {
      await occupancyDB.addFlatId({
        flatId: room.flatId,
        id: occupancy.id,
      });
    }

    const msg = CONSTANTS.MSG.ROOM_REQUEST_SENT;

    let isNotiSent = false;

    if (client.regId) {
        isNotiSent = await sendNotification({
          title: title,
          message: description,
          regId: client.regId,
          userId: client.id,
          userType: CONSTANTS.USER_TYPE.CLIENT,
          notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.ROOM_REQUEST,
          clientId: client.id,
        });
    } else {
      await notificationDB.add({
        userId: client.id,
        userType: CONSTANTS.USER_TYPE.CLIENT,
        title,
        message: description,
        notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.ROOM_REQUEST,
      });
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], No regId found of client [${client.id}]`
      );
    }

    // const isSent = await sendSMS(
    //   client.mobile,
    //   msg,
    //   CONSTANTS.SMS_TEMPLATE_IDS.ROOM_REQUEST_SENT
    // );

    const token = jwt.sign(
      {
        id: tenant?.id,
        type: CONSTANTS.USER_TYPE.TENANT,
        isEvicted: false,
        clientId: client.id,
      },
      process.env.TOKEN_SECRET!,
      { expiresIn: process.env.TOKEN_EXPIRY }
    );

    let flatName = "";
    if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
      const { name } = await flatDB.getById({ id: room.flatId });
      flatName = name;
    } else {
      flatName =
        occupancy.floor === "G" ? "Ground Floor" : "Floor " + occupancy.floor;
    }
    const notiSettings = await settingsDB.getByClientIdAndPropId({
      clientId: occupancy.clientId,
      propId: occupancy.propId,
    });
    
    let footerText = notiSettings.footer || "Kipinn Team";
    if(Number(notiSettings?.whatsApp) === 1) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${occupancy?.clientId}], Request Id [${requestId}], WhatsApp Enabled`
      );
      await sendWhatsappNewRequest(
        client?.mobile || "",
        client?.name || "",
        tenant?.name || "",
        "Room",
        property?.name || "",
        `${flatName} (${room.roomNum})`,
        footerText,
        client?.id
      );
    } else {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${occupancy?.clientId}], Request Id [${requestId}], WhatsApp Not Enabled`
      );
    }

    logTenantRequestActivity(
      req.userType!,
      Number(req.id)!,
      0,
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.TENANT_ROOM_REQUEST,
      property.id,
      Number(tenantId),
      tenant.name,
      roomId,
      0
    );

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Property GId [${propGId}], Room Id [${roomId}], Floor [${floor}], Is Notification Sent [${isNotiSent}], Room request sent successfully`
    );

    return res.status(200).json({
      msg: "Room request sent successfully",
      requestId,
      token,
      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,
    });
  }
};

rooms.RoomRequestStatus = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "RoomRequestStatus";

  try {
    const { requestId } = req.params;
    const tenantId = req.id;
    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Request Id [${requestId}]`
    );

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

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

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

    if (!request) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Request Id [${requestId}], No Request Found`
      );

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

    let kycOptions = null;
    let msg = "";

    if (request.status === CONSTANTS.REQUEST_STATUS.PENDING) {
      msg = "Your owner haven't accepted your onboarding request";
    } else if (request.status === CONSTANTS.REQUEST_STATUS.REJECTED) {
      msg = "Your owner have declined your onboarding request";
    } else if (request.status === CONSTANTS.REQUEST_STATUS.APPROVED) {
      msg = "Your owner have accepted your onboarding request";

      const property = await propertyDB.getById({
        id: request?.propId,
      });

      kycOptions = {
        isPoliceVerificationEnabled: property?.isPoliceVerificationEnabled,
        isRentAgreementEnabled: property.isRentAgreementEnabled,
        isIdVerificationEnabled: property?.isIdVerificationEnabled,
        isPanVerificationEnabled: property?.isPanVerificationEnabled,
        isOnlinePaymentEnabled: property?.isOnlinePaymentEnabled,
        isBondAvailable: property?.isBondAvailable,
      };
    }

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

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Request Id [${requestId}], Room request details sent successfully`
    );

    return res.status(200).json({
      msg: "Room request details sent successfully",
      data: { ...request, msg, tenantStatus: updatedTenant.status, kycOptions },
      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,
    });
  }
};

rooms.ApproveRoomRequest = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "ApproveRoomRequest";

  try {
    let {
      requestId,
      monthlyRent,
      securityDeposit,
      agreementStartDate,
      rentalCycle,
      agreementPeriod,
      noticePeriod,
      lockInPeriod,
      moveInDate,
      allBedsOccupied,
      electricityReading,
      isonlinePayment = 0,
    } = req.body;

    let clientId = req.id;
    const userType = req.userType;
    let dueDescription = "";
    if (Number(rentalCycle) === 0) {
      const day = moment(moveInDate, "YYYY-MM-DD").format("DD");
      rentalCycle = day;
    }
    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Request Id [${requestId}], Monthly Rent [${monthlyRent}], Security Deposit [${securityDeposit}], Agreement Start Date [${agreementStartDate}], Rental Cycle [${rentalCycle}], Agreement Period [${agreementPeriod}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Move In Date [${moveInDate}], All Beds Occupied [${allBedsOccupied}], Electricity Reading [${electricityReading}], Staff Id [${req.id}], isonlinePayment [${isonlinePayment}], 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}], Request Id [${requestId}], Monthly Rent [${monthlyRent}], Security Deposit [${securityDeposit}], Agreement Start Date [${agreementStartDate}], Rental Cycle [${rentalCycle}], Agreement Period [${agreementPeriod}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Move In Date [${moveInDate}], All Beds Occupied [${allBedsOccupied}], Electricity Reading [${electricityReading}], Staff Id [${req.id}], isonlinePayment [${isonlinePayment}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Monthly Rent [${monthlyRent}], Security Deposit [${securityDeposit}], Agreement Start Date [${agreementStartDate}], Rental Cycle [${rentalCycle}], Agreement Period [${agreementPeriod}], Notice Period [${noticePeriod}], Lock In Period [${lockInPeriod}], Move In Date [${moveInDate}], All Beds Occupied [${allBedsOccupied}], Electricity Reading [${electricityReading}], isonlinePayment [${isonlinePayment}], Client Requested....`
      );
    }

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

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

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

    if (!request) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], No Request Found`
      );
      return res
        .status(400)
        .json({ msg: "No Request Found", isSuccess: false });
    }

    if (request.status !== CONSTANTS.REQUEST_STATUS.PENDING) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request Already Approved Or Rejected`
      );
      return res.status(400).json({
        msg: "Request already approved or rejected",
        isSuccess: false,
      });
    }

    const occupancy = await occupancyDB.getById({ id: request.occupancyId });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    if (occupancy.status !== CONSTANTS.OCCUPANCY_STATUS.REQUESTED) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Occupancy Already Approved Or Rejected`
      );
      return res.status(400).json({
        msg: "Occupancy already approved or rejected",
        isSuccess: false,
      });
    }

    //Pallav - created occupancy gId for new occupancy
    let ogId = await generateOccupancyGId();
    occupancyDB.updateGId({ gId: ogId, tenantId: occupancy.tenantId, clientId });

    let discountEndDate = null;
    let discountStartDate = null;

    if (occupancy.discount > 0 && occupancy.discountPeriod > 0) {
      log.info(
        `[${C}], [${F}], Rental Cycle [${rentalCycle}], Rental Type [1], Discount Direction [${occupancy.isDiscountFromFirstMonth}], Discount Period [${occupancy.discountPeriod}], Discount Available, Adding Start and End Date`
      );
      let nextRentCycle = moment().date(rentalCycle);
      if (
        Number(moment(moveInDate).date()) !== Number(rentalCycle)
      ) {
        nextRentCycle = moment(nextRentCycle).add(1, "month");
      }

      if (Number(occupancy.isDiscountFromFirstMonth) === 1) {
        discountEndDate = nextRentCycle
          .clone()
          .add(occupancy.discountPeriod, "months")
          .subtract(1, "day")
          .format("YYYY-MM-DD");
        discountStartDate = moment(nextRentCycle).format("YYYY-MM-DD");
      } else {
        discountEndDate = moment(agreementStartDate)
          .add(agreementPeriod, "months")
          .subtract(1, "day")
          .format("YYYY-MM-DD");
        discountStartDate = moment(agreementStartDate)
          .add((agreementPeriod - occupancy.discountPeriod), "months")
          .format("YYYY-MM-DD");
      }

      await occupancyDB.updateDiscountStartEndDate({
        tenantId: occupancy.tenantId,
        clientId,
        discountStartDate,
        discountEndDate,
      });

      log.info(
        `[${C}], [${F}], Start Date [${discountStartDate}], Discount End Date [${discountEndDate}]`
      );
    }

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

    if (property.status !== CONSTANTS.PROPERTY_STATUS.ACTIVE) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Property is not active`
      );
      return res.status(400).json({
        msg: "Please make this property active to add a tenant",
        isSuccess: false,
      });
    }

    const room = await roomDB.getById({ id: occupancy.roomId });
    if (!room) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], No Room Found`
      );
      return res.status(400).json({ msg: "No Room Found", isSuccess: false });
    }

    if (room.status === CONSTANTS.ROOM_STATUS.OCCUPIED) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Room is already occupied`
      );
      return res
        .status(400)
        .json({ msg: "Room is already occupied", isSuccess: false });
    }

    if (allBedsOccupied && room.status !== CONSTANTS.ROOM_STATUS.VACANT) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Room is not vacant to occupy all beds`
      );
      return res.status(400).json({
        msg: "Room is not vacant to occupy all beds",
        isSuccess: false,
      });
    }

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

    // Sukhbir - MultiOccupancy
    // if (tenant.status !== CONSTANTS.TENANT_STATUS.REQUESTED) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Tenant Status is not requested`
    //   );
    //   return res.status(400).json({
    //     msg: "Tenant is either occupied or not completed his/her profile.",
    //     isSuccess: false,
    //   });
    // }

    let occupancyStatus = CONSTANTS.OCCUPANCY_STATUS.OCCUPIED;
    let isMoveInNow = true;
    if (moveInDate > moment().format("YYYY-MM-DD")) {
      isMoveInNow = false;
      occupancyStatus = CONSTANTS.OCCUPANCY_STATUS.RESERVED;
    }

    let rentalMonths = 0;

    if (moment(moveInDate).date() < Number(rentalCycle)) {
      rentalMonths += 1;
    }

    if (allBedsOccupied) {
      const beds = await bedDB.getVacantBeds({
        roomId: room.id,
        status: CONSTANTS.BED_STATUS.VACANT,
      });
      if (!beds) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Room Id [${room.id}] No Vacant Bed Available`
        );
        return res.status(400).json({
          msg: "No Vacant Bed Available",
          isSuccess: false,
        });
      }

      let index = 0;
      for (const bed of beds) {
        if (index === 0) {
          await occupancyDB.requestApproved({
            id: occupancy.id,
            bedId: bed.id,
            rent: monthlyRent,
            security: securityDeposit,
            agreementStartDate,
            rentalCycle,
            rentalMonths,
            agreementPeriod,
            noticePeriod,
            lockInPeriod,
            moveInDate,
            electricityReading,
            fine: property.fine,
            fineType: property.fineType,
            gracePeriod: property.gracePeriod,
            status: occupancyStatus,
            isOnlinePaymentEnabled: isonlinePayment
          });
        } else {
          await occupancyDB.addFullOccupancy({
            clientId,
            tenantId: tenant.id,
            propId: property.id,
            roomId: room.id,
            bedId: bed.id,
            floor: room.floor,
            rent: monthlyRent,
            security: securityDeposit,
            agreementStartDate,
            rentalCycle,
            agreementPeriod,
            noticePeriod,
            lockInPeriod,
            moveInDate,
            electricityReading,
            fine: property.fine,
            fineType: property.fineType,
            gracePeriod: property.gracePeriod,
            status: occupancyStatus,
            isOnlinePaymentEnabled: isonlinePayment
          });
        }

        if (true === isMoveInNow) {
          await bedDB.updateStatus({
            id: bed.id,
            status: CONSTANTS.BED_STATUS.OCCUPIED,
          });
        } else {
          await bedDB.updateStatus({
            id: bed.id,
            status: CONSTANTS.BED_STATUS.VACANT_RESERVED,
          });
        }

        index++;
      }
    } else {
      const bed = await bedDB.getVacantBed({
        roomId: room.id,
        status: CONSTANTS.BED_STATUS.VACANT,
      });

      if (!bed) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Room Id [${room.id}] No Vacant Bed Available`
        );
        return res.status(400).json({
          msg: "No Vacant Bed Available",
          isSuccess: false,
        });
      }

      await occupancyDB.requestApproved({
        id: occupancy.id,
        bedId: bed.id,
        rent: monthlyRent,
        security: securityDeposit,
        agreementStartDate,
        rentalCycle,
        rentalMonths,
        agreementPeriod,
        noticePeriod,
        lockInPeriod,
        moveInDate,
        electricityReading,
        fine: property.fine,
        fineType: property.fineType,
        gracePeriod: property.gracePeriod,
        status: occupancyStatus,
        isOnlinePaymentEnabled: isonlinePayment
      });

      if (true === isMoveInNow) {
        await bedDB.updateStatus({
          id: bed.id,
          status: CONSTANTS.BED_STATUS.OCCUPIED,
        });
      } else {
        await bedDB.updateStatus({
          id: bed.id,
          status: CONSTANTS.BED_STATUS.VACANT_RESERVED,
        });
      }
    }

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

    // Sukhbir - MultiOccupancy
    // if (true === isMoveInNow) {
    //   await tenantDB.updateStatus({
    //     id: tenant.id,
    //     status: CONSTANTS.TENANT_STATUS.OCCUPIED,
    //   });
    // } else {
    //   await tenantDB.updateStatus({
    //     id: tenant.id,
    //     status: CONSTANTS.TENANT_STATUS.RESERVED,
    //   });
    // }

    const referenceId = await generateLedgerReferenceId({ clientId });
    if (Number(securityDeposit)) {
      await duesDB.add({
        tenantId: tenant.id,
        amount: securityDeposit,
        occupancyId: occupancy.id,
        roomId: room.id,
        propId: property.id,
        clientId,
        dueDate: moment(moveInDate).format("YYYY-MM-DD HH:mm:ss"),
        type: CONSTANTS.DUES_TYPES.SECURITY,
        balance: securityDeposit,
        ledgerReferenceId: referenceId,
      });
      dueDescription = getDueDescription(CONSTANTS.DUES_TYPES.SECURITY);
      await ledgerDB.add({
        tenantId: tenant.id,
        roomId: room.id,
        propId: property.id,
        clientId,
        amount: securityDeposit,
        balance: securityDeposit,
        referenceId: referenceId,
        transactionId: null,
        type: CONSTANTS.DUES_TYPES.SECURITY,
        rentStartDate: null,
        rentEndDate: null,
        dueDate: moment(moveInDate).format("YYYY-MM-DD HH:mm:ss"),
        description: dueDescription,
      });
    }


    let discountFlag = 1;

    if (Number(monthlyRent)) {
      const rents = calculateMonthlyRent(moveInDate, rentalCycle, monthlyRent);
      dueDescription = getDueDescription(CONSTANTS.DUES_TYPES.RENT);
      if (Array.isArray(rents) && rents.length > 0) {
        for (let rent of rents) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenant.id}], Prop Id [${property.id}], Start Date [${rent.startDate}], End Date [${rent.endDate}], Rent [${rent.rent}]`
          );
          const referenceId = await generateLedgerReferenceId({ clientId });

          let rentAfterDiscount = rent.rent;

          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${occupancy.tenantId}], Dicount Start Date [${discountStartDate}], Discount End Date [${discountEndDate}], Rent Start Date [${rent.startDate}] Disocunt Added`
          );

          if (
            Number(occupancy.discount) &&
            Number(occupancy.discount) > 0 &&
            moment(discountStartDate).isSameOrBefore(rent.startDate) &&
            discountFlag <= Number(occupancy.discountPeriod)
          ) {
            if (occupancy.discountType === CONSTANTS.DISCOUNT_TYPE.FLAT) {
              rentAfterDiscount = Number(rent.rent) - Number(occupancy.discount);
              discountFlag += 1;
            } else if (occupancy.discountType === CONSTANTS.DISCOUNT_TYPE.PERCENTAGE) {
              rentAfterDiscount = Number(rent.rent) - Number((rent.rent * occupancy.discount) / 100);
              discountFlag += 1;
            }

            log.info(
              `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${occupancy.tenantId}], Discount Type [${occupancy.discountType}], Discount [${occupancy.discount}], Rent Amount [${rent.rent}], Rent After Discount [${rentAfterDiscount}], Disocunt Added`
            );
          }

          await duesDB.addWithStartEndDateX({
            tenantId: tenant.id,
            amount: rentAfterDiscount,
            occupancyId: occupancy.id,
            roomId: room.id,
            propId: property.id,
            clientId,
            rentStartDate: rent.startDate,
            rentEndDate: rent.endDate,
            dueDate: moment(rent.startDate).format("YYYY-MM-DD HH:mm:ss"),
            type: CONSTANTS.DUES_TYPES.RENT,
            balance: rentAfterDiscount,
            ledgerReferenceId: referenceId,
            title: "Rent",
            description: "Due added while onboarding",
            discount: rent.rent - rentAfterDiscount,
          });
          await ledgerDB.add({
            tenantId: tenant.id,
            roomId: room.id,
            propId: property.id,
            clientId,
            amount: rentAfterDiscount,
            balance: rentAfterDiscount,
            referenceId: referenceId,
            transactionId: null,
            type: CONSTANTS.DUES_TYPES.RENT,
            rentStartDate: rent.startDate,
            rentEndDate: rent.endDate,
            dueDate: moment(rent.startDate).format("YYYY-MM-DD HH:mm:ss"),
            //changed below description moment(---) at 17/12/2024 20:03:00
            description: rent.rent - rentAfterDiscount > 0
              ? `${dueDescription} for ${moment(rent.startDate).format("MMM YYYY")}, Discount of ₹${rent.rent - rentAfterDiscount} given for this due`
              : `${dueDescription} for ${moment(rent.startDate).format("MMM YYYY")}`,
            discount: rent.rent - rentAfterDiscount,
            title: "Rent",
          });
        }
      }
    }

    const extraCharges = await extraChargeDB.getAllByClientIdAndPropId({
      clientId,
      propId: property.id,
    });

    if (extraCharges) {
      for (let extraCharge of extraCharges) {
        let type = 0;
        const referenceId = await generateLedgerReferenceId({ clientId });
        type = convertTypes({
          from: "extraChargeType",
          to: "dueType",
          type: extraCharge.type,
        });

        if (type === 0) continue;

        if (Number(type) === CONSTANTS.DUES_TYPES.MOVE_OUT_CHARGES) {
          continue;
        }

        dueDescription = getDueDescription(extraCharge.type);
        await duesDB.add({
          tenantId: occupancy.tenantId,
          amount: extraCharge.amount,
          occupancyId: occupancy.id,
          roomId: occupancy.roomId,
          propId: occupancy.propId,
          clientId,
          dueDate: moment(moveInDate).format("YYYY-MM-DD HH:mm:ss"),
          type,
          balance: extraCharge.amount,
          ledgerReferenceId: referenceId,
        });
        await ledgerDB.add({
          tenantId: occupancy.tenantId,
          roomId: occupancy.roomId,
          propId: occupancy.propId,
          clientId,
          amount: extraCharge.amount,
          balance: extraCharge.amount,
          referenceId: referenceId,
          transactionId: null,
          type: type,
          rentStartDate: null,
          rentEndDate: null,
          dueDate: moment(moveInDate).format("YYYY-MM-DD HH:mm:ss"),
          description: `${dueDescription} for ${moment(moveInDate).format(
            "MMM"
          )}`,
        });
      }
    }

    let roomStatus = null;

    if (allBedsOccupied) {
      roomStatus = CONSTANTS.ROOM_STATUS.OCCUPIED;
    } else {
      const bed = await bedDB.getVacantBed({
        roomId: room.id,
        status: CONSTANTS.BED_STATUS.VACANT,
      });
      if (bed) roomStatus = CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED;
      else roomStatus = CONSTANTS.ROOM_STATUS.OCCUPIED;
    }

    await roomDB.updateStatus({
      id: room.id,
      status: roomStatus,
    });

    //other requestss
    const requests = await requestDB.getPendingReqByRoomId({
      roomId: room.id,
      status: CONSTANTS.REQUEST_STATUS.PENDING,
    });

    if (requests) {
      for (const request of requests) {
        if(requests?.type === CONSTANTS.REQUEST_TYPE.ROOM_SELECTION) {
          await requestDB.updateStatus({
            id: request?.id,
            status: CONSTANTS.REQUEST_STATUS.REJECTED,
          });
        }
      }
    }

    const msg = CONSTANTS.MSG.ROOM_REQUEST_APPROVED.replace(
      "{#var#}",
      tenant.name
    );
    // const isSent = await sendSMS(
    //   tenant.mobile,
    //   msg,
    //   CONSTANTS.SMS_TEMPLATE_IDS.ROOM_REQUEST_APPROVED
    // );

    let isNotiSent = false;
    if (tenant.regId) {
      isNotiSent = await sendNotification({
        title: "Room selection request approved!",
        message:
          "Your room selection request has been approved by the Owner/Manager. Tap to view details",
        regId: tenant.regId,
        userId: tenant.id,
        userType: CONSTANTS.USER_TYPE.TENANT,
        notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.ROOM_REQUEST,
        clientId: client.id,
      });
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], No RegId found for tenant [${tenant.id}]`
      );
    }

    let gId = await generateTenantGId();

    await tenantDB.updateGId({ gId, id: tenant.id });

    await generateTenantTId(
      tenant.id,
    );

    const notiSettings = await settingsDB.getByClientIdAndPropId({
    clientId: request?.clientId,
    propId: request?.propId,
  });
      
  let footerText = notiSettings.footer || "Kipinn Team";
  //const property = await propertyDB.getById({ id: request?.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})`;
  let requestName = getRequestName(CONSTANTS.REQUEST_TYPE.ROOM_SELECTION);
  if(Number(notiSettings?.whatsApp) === 1) {
      log.info(
        `[${C}], [${F}], Client Id [${request?.clientId}], Request Id [${requestId}], WhatsApp Enabled`
      );
      await sendWhatsappRequestUpdates(
        tenant?.mobile,
        tenant?.name,
        requestName,
        occupancyName,
        `accepted`,
        footerText,
        request?.clientId
      );
  } else {
    log.info(
        `[${C}], [${F}], Client Id [${request?.clientId}], Request Id [${requestId}], WhatsApp Not Enabled`
      );
  }

    logTenantRequestActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.TENANT_ROOM_REQUEST_ACCEPT,
      property.id,
      Number(tenant.id),
      tenant.name,
      room.id,
      0
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Is Notification Sent [${isNotiSent}] Room Request Approved Successfully`
    );

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

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

rooms.RejectRoomRequest = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "RejectRoomRequest";

  try {
    const { requestId, reason=null } = req.body;

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Request Id [${requestId}], 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}], Request Id [${requestId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Client Requested....`
      );
    }

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

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

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

    if (!request) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], No Request Found`
      );
      return res
        .status(400)
        .json({ msg: "No Request Found", isSuccess: false });
    }

    if (request.status !== CONSTANTS.REQUEST_STATUS.PENDING) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Request Already Approved Or Rejected`
      );
      return res.status(400).json({
        msg: "Request already approved or rejected",
        isSuccess: false,
      });
    }

    const occupancy = await occupancyDB.getById({ id: request.occupancyId });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    if (occupancy.status !== CONSTANTS.OCCUPANCY_STATUS.REQUESTED) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Occupancy Already Approved Or Rejected`
      );
      return res.status(400).json({
        msg: "Occupancy already approved or rejected",
        isSuccess: false,
      });
    }

    const tenant = await tenantDB.getById({ id: request.tenantId });
    let requestReason = "Your request has been rejected";
    if(!reason) {
      await requestDB.updateStatus({
        id: request?.id,
        status: CONSTANTS.REQUEST_STATUS.REJECTED,
      });
    } else {
      requestReason = reason;
      await requestDB.updateStatusWithReason({
        id: request?.id,
        reason,
        status: CONSTANTS.REQUEST_STATUS.REJECTED,
      });
    }
    const notiSettings = await settingsDB.getByClientIdAndPropId({
        clientId,
        propId: occupancy.propId,
      });
          
    let footerText = notiSettings.footer || "Kipinn Team";
    const property = await propertyDB.getById({ id: occupancy.propId });
    if(Number(notiSettings?.whatsApp) === 1) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], WhatsApp Enabled`
      );
      let requestName = getRequestName(CONSTANTS.REQUEST_TYPE.ROOM_SELECTION);
      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.updateOccupancyId({
      id: request?.id,
      occupancyId: null,
    });

    if (request?.occupancyId) {
      await occupancyDB.removeById({
        id: request.occupancyId,
      });
    }

    if (request?.tenantId) {
      await tenantDB.updateStatus({
        id: request.tenantId,
        status: CONSTANTS.TENANT_STATUS.VACANT,
      });
    }

    const msg = CONSTANTS.MSG.ROOM_REQUEST_REJECTED.replace(
      "{#var#}",
      tenant.name
    );

    let isNotiSent = false;
    if (tenant.regId) {
      isNotiSent = await sendNotification({
        title: "Room selection request rejected!",
        message:
          "Your room selection request has been rejected by the Owner/Manager. Tap to view details",
        regId: tenant.regId,
        userId: tenant.id,
        userType: CONSTANTS.USER_TYPE.TENANT,
        notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.ROOM_REQUEST,
        clientId: client.id,
      });
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], No RegId found for tenant [${tenant.id}]`
      );
    }

    // const isSent = await sendSMS(
    //   tenant.mobile,
    //   msg,
    //   CONSTANTS.SMS_TEMPLATE_IDS.ROOM_REQUEST_REJECTED
    // );

    logTenantRequestActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.TENANT_ROOM_REQUEST_REJECT,
      occupancy.propId,
      Number(tenant.id),
      tenant.name,
      occupancy.roomId,
      0
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Request Id [${requestId}], Is Notification Sent [${isNotiSent}] Room Request Rejected Successfully`
    );

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

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

rooms.CancelRoomRequest = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "CancelRoomRequest";

  try {
    const tenantId = req.id;
    const { requestId } = req.body;
    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Request Id [${requestId}]`
    );

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

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

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

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

    if (request.status !== CONSTANTS.REQUEST_STATUS.PENDING) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Request Id [${requestId}], Request Already Approved Or Rejected`
      );
      return res.status(400).json({
        msg: "Request already approved or rejected",
        isSuccess: false,
      });
    }

    const occupancy = await occupancyDB.getById({ id: request.occupancyId });

    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Request Id [${requestId}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    if (occupancy.status !== CONSTANTS.OCCUPANCY_STATUS.REQUESTED) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Request Id [${requestId}], Occupancy Already Approved Or Rejected`
      );
      return res.status(400).json({
        msg: "Occupancy already approved or rejected",
        isSuccess: false,
      });
    }

    await requestDB.updateStatus({
      id: request?.id,
      status: CONSTANTS.REQUEST_STATUS.CANCELLED,
    });

    await requestDB.updateOccupancyId({
      id: request?.id,
      occupancyId: null,
    });

    if (request?.occupancyId) {
      await occupancyDB.removeById({
        id: request.occupancyId,
      });
    }

    if (request.tenantId) {
      await tenantDB.updateStatus({
        id: request.tenantId,
        status: CONSTANTS.TENANT_STATUS.VACANT,
      });
    }

    logTenantRequestActivity(
      req.userType!,
      Number(req.id)!,
      Number(req.parentClientId),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.TENANT_ROOM_REQUEST_CANCEL,
      occupancy.propId,
      Number(tenant.id),
      tenant.name,
      occupancy.roomId,
      0
    );

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Request Id [${requestId}], Room Request Cancelled Successfully`
    );

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

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

rooms.CreateRoomOption = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "CreateRoomOption";

  try {
    const { name, rent, security = null, type, amenities, propId, totalBedCount } = req.body;

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Name [${name}], Rent [${rent}], Security [${security}], Type [${type}] Amenities [${amenities}], Property Id [${propId}], Total Bed Count [${totalBedCount}], 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}], Name [${name}], Rent [${rent}], Security [${security}], Type [${type}] Amenities [${amenities}], Property Id [${propId}], Total Bed Count [${totalBedCount}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Rent [${rent}], Security [${security}], Type [${type}] Amenities [${amenities}], Property Id [${propId}], Total Bed Count [${totalBedCount}], Client Requested....`
      );
    }

    if (totalBedCount > 12) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Rent [${rent}], Type [${type}] Amenities [${amenities}], Property Id [${propId}], Total Bed Count [${totalBedCount}], Bed count cannot be more than 12`
      );
      return res
        .status(400)
        .json({ msg: "Bed count cannot be more than 12", isSuccess: false });
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Rent [${rent}], Type [${type}] Amenities [${amenities}], Property Id [${propId}], Total Bed Count [${totalBedCount}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const property = await propertyDB.getById({ id: propId });
    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Name [${name}], Rent [${rent}], Type [${type}] Amenities [${amenities}], Property Id [${propId}], Total Bed Count [${totalBedCount}], No Property Found`
      );
      return res.status(400).json({
        msg: "Invalid Property Id",
        isSuccess: false,
      });
    }

    const isSameNameExists = await roomOptionDB.getByName({
      name,
      propId,
      type,
    });

    if (isSameNameExists) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Name [${name}], Rent [${rent}], Type [${type}] Amenities [${amenities}], Property Id [${propId}], Total Bed Count [${totalBedCount}], Same Name Exists`
      );
      return res.status(400).json({
        msg: "Option with same name is already exists",
        isSuccess: false,
      });
    }

    const roomOptionId = await roomOptionDB.create({
      propId,
      rent,
      security,
      type,
      name,
      amenities,
      totalBedCount,
    });

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Name [${name}], Rent [${rent}], Type [${type}] Amenities [${amenities}], Property Id [${propId}], Total Bed Count [${totalBedCount}], Room option created successfully `
    );
    return res.status(200).json({
      msg: "Room option created successfully",
      roomOptionId,
      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,
    });
  }
};

rooms.EditRoomOption = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "EditRoomOption";

  try {
    const { name, rent, security = null, amenities, roomOptionId } = req.body;

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Name [${name}], Rent [${rent}], Security [${security}], Amenities [${amenities}], Room Option Id [${roomOptionId}], 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}], Name [${name}], Rent [${rent}], Security [${security}], Amenities [${amenities}], Room Option Id [${roomOptionId}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Rent [${rent}], Security [${security}], Amenities [${amenities}], Room Option Id [${roomOptionId}], Client Requested....`
      );
    }

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

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

    const roomOption = await roomOptionDB.getById({ id: roomOptionId });
    if (!roomOption) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Rent [${rent}], Amenities [${amenities}], Room Option Id [${roomOptionId}], No Room Option Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }
    await roomOptionDB.update({
      rent,
      security,
      name,
      amenities,
      id: roomOptionId,
    });

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Name [${name}], Rent [${rent}], Amenities [${amenities}], Room Option Id [${roomOptionId}], Room option updated successfully `
    );
    return res.status(200).json({
      msg: "Room option 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,
    });
  }
};

rooms.RoomOptionList = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "RoomOptionList";

  try {
    const { propId, type } = req.params;

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Property Id [${propId}], Type [${type}], 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}], Property Id [${propId}], Type [${type}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Type [${type}], Client Requested....`
      );
    }

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

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

    const property = await propertyDB.getById({ id: propId });
    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Type [${type}], No Property Found`
      );
      return res.status(400).json({
        msg: "Invalid Property Id",
        isSuccess: false,
      });
    }

    const roomOptions = await roomOptionDB.getByPropIdAndType({
      propId,
      type,
    });

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Type [${type}], Room option list sent successfully `
    );

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

rooms.RoomOptionCount = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "RoomOptionCount";

  try {
    const { propId } = req.params;

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Property Id [${propId}], 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}], Property Id [${propId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Client Requested....`
      );
    }

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

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

    const property = await propertyDB.getById({ id: propId });
    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], No Property Found`
      );
      return res.status(400).json({
        msg: "Invalid Property Id",
        isSuccess: false,
      });
    }

    const counts = await roomOptionDB.getCountByPropId({ propId });

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Room option count sent successfully `
    );

    return res.status(200).json({
      msg: "Room option count sent successfully",
      data: counts,
      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,
    });
  }
};

rooms.AddRooms = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "AddRooms";

  try {
    const { rooms, propId } = req.body;
    let clientId = req.id;
    const userType = req.userType;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], Rooms Length [${rooms.length}], 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}], Prop Id [${propId}], Rooms Length [${rooms.length}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Rooms Length [${rooms.length}], Client Requested....`
      );
    }

    if (rooms.length === 0) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Rooms Length [${rooms.length}], Rooms Empty Array`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.VALIDATION_ERROR,
        isSuccess: false,
      });
    }

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

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

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

    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], No Property Found`
      );

      return res.status(400).json({
        msg: "Invalid Property Id",
        isSuccess: false,
      });
    }

    for (const index in rooms) {
      try {
        let { roomCount: count, floorNumber: floor } = rooms[index];

        const { totalRooms } = await roomDB.getCountsByPropIdAndFloor({
          propId,
          floor,
        });

        if (Number(totalRooms) >= CONSTANTS.MAX_ROOM_LIMIT) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Total Rooms [${totalRooms}], Already ${CONSTANTS.MAX_ROOM_LIMIT} rooms exists`
          );
          continue;
        } else {
          if (count > CONSTANTS.MAX_ROOM_LIMIT) {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], Floor [${floor}], Room Count [${count}], Room Count Exceeds ${CONSTANTS.MAX_ROOM_LIMIT}`
            );
            continue;
          } else {
            const roomCountCanBeAdded =
              CONSTANTS.MAX_ROOM_LIMIT - Number(totalRooms);
            if (count > roomCountCanBeAdded) {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], Floor [${floor}], Room Count [${count}], Room count exceeds remaining room limit, that's why update to Count [${roomCountCanBeAdded}]`
              );
              count = roomCountCanBeAdded;
            }
          }
        }

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Floor [${floor}], Room Count [${count}]`
        );

        let room = await roomDB.getLatestRoom({ propId, floor });
        let lastRoomNum: string =
          (room?.roomNum && room?.roomNum.toString()) || "";

        const arrFromCount = Array(count).fill(0);

        let curRoomCount = Number(totalRooms);

        for (const index2 in arrFromCount) {
          try {

            const roomNum = generateRoomNumX(lastRoomNum, floor, curRoomCount+1);
            const roomId = await roomDB.create({
              propId,
              floor,
              roomNum,
            });

            lastRoomNum = roomNum;
            curRoomCount += 1;

            log.info(
              `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Floor [${floor}], Room Count [${count}], Room Num [${roomNum}], Room Added Successfully`
            );
          } catch (error: any) {
            log.info(`[${C}], [${F}], Error2: ${error?.message || error}`);
          }
        }
      } catch (error: any) {
        log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
      }
    }

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

rooms.AddRoomsForFlat = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "AddRoomsForFlat";

  try {
    const { rooms, propId } = req.body;
    let clientId = req.id;
    const userType = req.userType;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], Rooms Length [${rooms.length}], 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}], Prop Id [${propId}], Rooms Length [${rooms.length}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Rooms Length [${rooms.length}], Client Requested....`
      );
    }

    if (rooms.length === 0) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Rooms Length [${rooms.length}], Rooms Empty Array`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.VALIDATION_ERROR,
        isSuccess: false,
      });
    }

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

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

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

    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], No Property Found`
      );

      return res.status(400).json({
        msg: "Invalid Property Id",
        isSuccess: false,
      });
    }

    for (const index in rooms) {
      try {
        let { roomCount: count, flatId } = rooms[index];

        const { totalRooms } = await roomDB.getCountsByPropIdAndFlatId({
          propId,
          flatId,
        });

        if (Number(totalRooms) >= CONSTANTS.MAX_ROOM_LIMIT) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Total Rooms [${totalRooms}], Already ${CONSTANTS.MAX_ROOM_LIMIT} rooms exists`
          );
          continue;
        } else {
          if (count > CONSTANTS.MAX_ROOM_LIMIT) {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], Room Count [${count}], Room Count Exceeds ${CONSTANTS.MAX_ROOM_LIMIT}`
            );
            continue;
          } else {
            const roomCountCanBeAdded =
              CONSTANTS.MAX_ROOM_LIMIT - Number(totalRooms);
            if (count > roomCountCanBeAdded) {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], Room Count [${count}], Room count exceeds remaining room limit, that's why update to Count [${roomCountCanBeAdded}]`
              );
              count = roomCountCanBeAdded;
            }
          }
        }

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], Room Count [${count}]`
        );

        const flat = await flatDB.getById({ id: flatId });
        let room = await roomDB.getLatestRoomForFlat({ propId, flatId });
        let lastRoomNum: string =
          (room?.roomNum && room?.roomNum.toString()) || "";

        const arrFromCount = Array(count).fill(0);

        for (const index2 in arrFromCount) {
          try {
            if (flat.name.length > 5) {
              log.info(
                `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Flat Id [${flatId}], Room Count [${count}], Flat Name [${flat.name}] Flat name length greater than 5 characters`
              );

              return res.status(400).json({
                msg: "Flat name length greater than 5 characters",
                isSuccess: false,
              });
            }
            const roomNum = generateRoomNum(lastRoomNum, String(flat.name));
            const roomId = await roomDB.AddForFlat({
              propId,
              flatId,
              roomNum,
            });

            lastRoomNum = roomNum;

            log.info(
              `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Flat Id [${flatId}], Room Count [${count}], Room Num [${roomNum}], Room Added Successfully`
            );
          } catch (error: any) {
            log.info(`[${C}], [${F}], Error2: ${error?.message || error}`);
          }
        }
      } catch (error: any) {
        log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
      }
    }

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

rooms.AddBeds = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "AddBeds";

  try {
    const { rooms, roomOptionId } = req.body;

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Room Option Id [${roomOptionId}], Rooms Length [${rooms.length}], 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}], Room Option Id [${roomOptionId}], Rooms Length [${rooms.length}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Option Id [${roomOptionId}], Rooms Length [${rooms.length}], Client Requested....`
      );
    }

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

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

    const roomOption = await roomOptionDB.getById({ id: roomOptionId });

    if (!roomOption) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Room Option Id [${roomOptionId}], No Room Option Found`
      );
      return res.status(400).json({
        msg: "Invalid Room Option Id",
        isSuccess: false,
      });
    }

    if(rooms && rooms.length < 1) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Rooms [${rooms.length}], No rooms found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.ERROR_MESSAGE,
        isSuccess: false,
      });
    }
    
    let isBedRemovedAlso = false;

    for (const roomId of rooms) {
      const room = await roomDB.getById({ id: roomId });
      if (!room) {
        log.info(
          `[${C}], [${F}], Client Id [${client?.id}], Room Option Id [${roomOptionId}], Room Id [${roomId}], No Room Found`
        );
        continue;
      }

      const bedCount = await bedDB.getCountsByRoomId({
        id: roomId,
      });

      if (room.roomOptionId === roomOptionId && bedCount.total === roomOption.totalBedCount) {
        log.info(
          `[${C}], [${F}], Client Id [${client?.id}], Room Option Id [${roomOptionId}], Room Id [${roomId}], Same Room Option Exits`
        );
        continue;
      }

      // Remove Extra beds , in case user wants to change Room option type from single to double or triple
      await bedDB.removeAllExtraBedByRoomId({ roomId });

      let bedCountToBeAdded = 0;
      let oldBeds = null;

      if (room?.roomOptionId) {
        const beds = await bedDB.getByRoomId({ roomId });
        if (beds) {
          oldBeds = beds;
          log.info(
            `[${C}], [${F}], Client Id [${client?.id}], Room Option Id [${roomOptionId}], Room Id [${roomId}], ${beds.length} Beds already exists`
          );
        }
      }

      // ---------- ADD BEDS -----------
      if (!oldBeds || (oldBeds && roomOption.totalBedCount > oldBeds.length)) {
        if (!oldBeds) bedCountToBeAdded = roomOption.totalBedCount;
        else {
          if (oldBeds.length >= CONSTANTS.MAX_BED_LIMIT) {
            log.info(
              `[${C}], [${F}], Client Id [${client?.id}], Room Option Id [${roomOptionId}], Room Id [${roomId}], Maximum no. of beds already exists`
            );
            continue;
          } else {
            bedCountToBeAdded = roomOption.totalBedCount - oldBeds.length;
          }
        }

        log.info(
          `[${C}], [${F}], Client Id [${client?.id}], Room Option Id [${roomOptionId}], Room Id [${roomId}], No. of beds to be added [${bedCountToBeAdded}]`
        );
        const bedsEmptyArr = Array.from(Array(bedCountToBeAdded));

        for (const _ of bedsEmptyArr) {
          const bedId = await bedDB.create({
            roomId,
            isExtraBed: 0,
          });

          log.info(
            `[${C}], [${F}], Client Id [${client?.id}], Room Option Id [${roomOptionId}], Room Id [${roomId}], Bed Id [${bedId}], Bed Added Successfully`
          );
        }
      }

      // ---------- REMOVE BEDS -----------
      else {
        const removeCount = oldBeds.length - roomOption.totalBedCount;
        const vacantBeds = oldBeds.filter(
          (bed: bedTypes) => bed.status === CONSTANTS.BED_STATUS.VACANT
        );
        if (vacantBeds.length >= removeCount) {
          await bedDB.removeVacantBeds({
            roomId,
            count: removeCount,
            status: CONSTANTS.BED_STATUS.VACANT,
          });
          log.info(
            `[${C}], [${F}], Client Id [${client?.id}], Room Option Id [${roomOptionId}], Room Id [${roomId}], Remove Count [${removeCount}], Vacant Beds [${vacantBeds.length}], Beds Removed Successfully`
          );
          isBedRemovedAlso = true;
        } else {
          log.info(
            `[${C}], [${F}], Client Id [${client?.id}], Room Option Id [${roomOptionId}], Room Id [${roomId}], Remove Count [${removeCount}], Vacant Beds [${vacantBeds.length}], There are not enough vacant beds to be removed.`
          );
          continue;
        }
      }

      await roomDB.updateRoomOption({
        id: roomId,
        roomOptionId,
      });
    }

    return res.status(200).json({
      msg: `Beds has been ${isBedRemovedAlso ? "adjusted" : "added"
        } 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,
    });
  }
};

rooms.ClientRoomList = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "ClientRoomList";

  try {
    const { propId } = req.params;
    let clientId = req.id;
    const userType = req.userType;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], 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}], Prop Id [${propId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Client Requested....`
      );
    }

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

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

    const property = await propertyDB.getById({ id: propId });
    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], No Property Found`
      );
      return res.status(400).json({
        msg: "Invalid Property Id",
        isSuccess: false,
      });
    }

    const floors = Array.from(Array(property.floorCount)).map((_, index) => {
      if (Number(property.isGroundIncluded)) {
        return {
          floor: index === 0 ? "G" : index,
          rooms: [],
        };
      } else {
        return {
          floor: index + 1,
          rooms: [],
        };
      }
    });

    const rooms = await roomDB.getRoomsByPropId({
      propId: property?.id,
    });

    if (!rooms) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], No Room Available`
      );
    }

    const allBeds = await bedDB.getDetailsByPropId({ propId });
    const bedMap = new Map<number, any[]>();
    if (allBeds) {
      for (const bed of allBeds) {
        if (!bedMap.has(bed.roomId)) bedMap.set(bed.roomId, []);
        bedMap.get(bed.roomId)?.push(bed);
      }
    }

    const tenantMap = new Map<number, any>();
    const tenantIds = new Set<number>();

    const allOccupancies = await occupancyDB.getByPropId({ propId });
    const occupancyByBed = new Map<number, any>();
    const occupancyByRoom = new Map<number, any[]>();
    if (allOccupancies) {
      for (const occ of allOccupancies) {
        if (!occupancyByRoom.has(occ.roomId)) occupancyByRoom.set(occ.roomId, []);
        occupancyByRoom.get(occ.roomId)?.push(occ);
        // occupancyByBed.set(occ.bedId, occ);
        if (!occupancyByBed.has(occ.bedId)) occupancyByBed.set(occ.bedId, []);
        occupancyByBed.get(occ.bedId)?.push(occ);

        tenantIds.add(occ.tenantId);
      }
    }

    const tenants = await tenantDB.getInfoByIds({ids: [...tenantIds]}); 
    if (tenants) {
      for (let tenant of tenants) {
        tenantMap.set(tenant.id, tenant);
      }
    }

    type RoomCount = {
      roomId: number;
      total: number;
      vacant: number;
    };
    const allRoomsCount = await bedDB.getCountsByRoomIdForProp({ propId });
    const roomCountMap = new Map<number, RoomCount[]>();
    if (allRoomsCount) {
      for (const room of allRoomsCount) {
        if (!roomCountMap.has(room.roomId)) roomCountMap.set(room.roomId, []);
        roomCountMap.get(room.roomId)?.push(room);
      }
    }

    const allDues = await duesDB.getTotalDuesByPropIdGroupByTenant({ propId });
    const duesMap = new Map<number, number>();
    if (allDues) {
      for (const d of allDues) {
        duesMap.set(d.tenantId, d.totalDues || 0);
      }
    }

    if (rooms) {
      for (const floorObj of floors) {
        const floorWiseRooms = rooms.filter(
          (room: roomsTypes) =>
            room.floor.toString() === floorObj.floor.toString()
        );
        for (const room of floorWiseRooms) {
          // const beds = await bedDB.getDetailsByRoomId({
          //   roomId: room.id,
          // });
          const beds = bedMap.get(room.id) || false;
          if (beds) {
            for (let bed of beds) {
              let occupancyStatus = CONSTANTS.OCCUPANCY_STATUS.OCCUPIED;

              if (bed.status === CONSTANTS.BED_STATUS.MOVING_OUT) {
                occupancyStatus = CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT;
              } else if (bed.status === CONSTANTS.BED_STATUS.VACANT_RESERVED) {
                occupancyStatus = CONSTANTS.OCCUPANCY_STATUS.RESERVED;
              } else if (bed.status === CONSTANTS.BED_STATUS.RESERVED) {
                occupancyStatus = CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT;
              }
              bed.isDuePending = 0;
              // const occupancy = await occupancyDB.getByBedId({
              //   bedId: bed.id,
              //   status: occupancyStatus,
              // });
              // const occupancy = occupancyByBed.get(bed.id);
              const occupancies = occupancyByBed.get(bed.id);
              let occupancy: any = false;
              if (occupancies && occupancies.length > 0) {
                occupancy = occupancies[0];
              }
              if (occupancy) {
                // const { totalDues } = await duesDB.getTotalDuesByTenantId({
                //   tenantId: occupancy.tenantId,
                //   propId: propId,
                // });
                // bed.isDuePending = Number(totalDues) || 0;
                // bed.isDuePending = duesMap.get(occupancy.tenantId) || 0;
                const tenant = await tenantDB.getById({ id: occupancy.tenantId });
                bed.tenantName = tenant?.name || "";
                bed.tenantGender = tenant?.gender || 0;
              }
              if (occupancies) {
                for (let occupancy of occupancies) {
                  bed.isDuePending += Number(duesMap.get(occupancy.tenantId)) || 0;
                  const tenant = tenantMap.get(occupancy.tenantId);
                    occupancy.tenantName = tenant?.name || "";
                    occupancy.tenantMobile = tenant?.mobile || "";
                    occupancy.tenantGender = tenant?.gender || "";
                }
              }

              bed.occupancies = occupancies;

              bed.canAddTenant = true;
              if (occupancies) {
                const tenants = await occupancyDB.getAllByBedId({
                  bedId: bed.id,
                });
                let vacantSlots  = await getVacantSlots(tenants);
    
                if (vacantSlots.length === 1 && vacantSlots[0].endDate === null) {
                  bed.canAddTenant = true;
                } else if (vacantSlots.length === 0) {
                  bed.canAddTenant = false;
                }
              }
            }
          }
          room.beds = beds;

          // const occupancies = await occupancyDB.getByRoomId({
          //   roomId: room.id,
          // });
          const occupancies = occupancyByRoom.get(room.id) || false;

          if (occupancies) {
            //for (occupancy)
            for (let occupancy of occupancies) {
              const { totalDues } = await duesDB.getTotalDuesByTenantId({
                tenantId: occupancy.tenantId,
                propId: propId,
              });
              room.beds.isDuePending = Number(totalDues) || 0;
            }
          }

          // const bedCount = await bedDB.getCountsByRoomId({
          //   id: room.id,
          // });
          // const totalBeds = bedCount.totalBeds;
          const totalBeds = roomCountMap.get(room.id)?.[0]?.total || 0;

          if (!occupancies || totalBeds > occupancies.length || totalBeds === 1)
            room.isFullyOccupied = false;
          else {
            room.isFullyOccupied = occupancies.every(
              (occupancy: occupanciesTypes) =>
                occupancy.tenantId === occupancies[0].tenantId
            );
          }
          room.isFlatFullyOccupied = false;
        }
        floorObj.rooms = floorWiseRooms;
      }
    }

    const bedCount = await bedDB.getCountsByPropId({ propId: property?.id });
    const { dueTenantCount } = await duesDB.getDistinctTenantCountByPropId({
      propId: property?.id,
    });
    const totalRooms = await roomDB.getCountsByPropId({ propId });
    const totalTenants = await occupancyDB.getTotalCountByClientIdAndPropId({
      clientId,
      propId,
    });
    // const bedStats = await bedDB.getCountsByPropId({ propId });
    const vacantRooms = await roomDB.getRoomCountByPropIdAndStatus({
      propId,
      status: CONSTANTS.ROOM_STATUS.VACANT,
    });
    const semiVacantRooms = await roomDB.getRoomCountByPropIdAndStatus({
      propId,
      status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
    });
    const movingOut =
      await occupancyDB.getMovingOutTenantsCountByClientIdAndPropId({
        clientId,
        propId,
      });

    const pendingDues = await duesDB.getTotalDuesByPropId({
      propId,
    });

    const curMonthPendingDues = await duesDB.getTotalDuesByPropIdDateRange({
      propId,
      startDate: moment().startOf("month").format("YYYY-MM-DD"),
      endDate: moment().endOf("month").format("YYYY-MM-DD"),
    });

    const stats = {
      totalRooms: totalRooms?.totalRooms || [],
      totalTenants: totalTenants?.totalOccupanciesCount || 0,
      totalBeds: bedCount?.total || 0,
      vacantBeds: bedCount?.vacant || 0,
      occupiedBeds: bedCount?.occupied || 0,
      vacantRooms: vacantRooms?.totalRooms || 0,
      semiVacantRooms: semiVacantRooms?.totalRooms || 0,
      movingOut: movingOut || 0,
      dueTenantCount: dueTenantCount || 0,
      pendingDues: pendingDues.totalDues || 0,
      curMonthPendingDues: curMonthPendingDues.totalDues || 0,
    };

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Room list sent successfully `
    );

    return res.status(200).json({
      msg: "Room list sent successfully",
      isSuccess: true,
      data: {
        floors,
        property: {
          name: property.name,
          address: property.address,
          streetAddress: property.streetAddress,
          type: property.type,
          status: property.status,
        },
        beds: bedCount || [],
      },
      stats,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

rooms.ClientFlatRoomList = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "ClientFlatRoomList";

  try {
    const { propId } = req.params;
    let clientId = req.id;
    const userType = req.userType;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], 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}], Prop Id [${propId}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Client Requested....`
      );
    }

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

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

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

    const flats = await flatDB.getByPropId({ propId });

    const flatsIds = flats
      .map((flat: any) => flat.id)
      .join(",");

    if (!flats) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], No Flat Available`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (!rooms) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], No Room Available`
      );
    }

    type RoomCount = {
      roomId: number;
      total: number;
      vacant: number;
    };

    const allRooms = await roomDB.getRoomsByPropIdForFlats({ propId, flatId: flatsIds });
    const allBeds = await bedDB.getDetailsByPropId({ propId });
    const allOccupancies = await occupancyDB.getByPropId({ propId });
    const allDues = await duesDB.getTotalDuesByPropIdGroupByTenant({ propId });
    const allFlatDues = await duesDB.getTotalDuesByPropIdGroupByFlat({ propId });
    const allRoomsCount = await bedDB.getCountsByRoomIdForProp({ propId });

    const roomMap = new Map<number, any[]>();
    if (allRooms) {
      for (const room of allRooms) {
        if (!roomMap.has(room.flatId)) roomMap.set(room.flatId, []);
        roomMap.get(room.flatId)?.push(room);
      }
    }

    const roomCountMap = new Map<number, RoomCount[]>();
    if (allRoomsCount) {
      for (const room of allRoomsCount) {
        if (!roomCountMap.has(room.roomId)) roomCountMap.set(room.roomId, []);
        roomCountMap.get(room.roomId)?.push(room);
      }
    }

    const bedMap = new Map<number, any[]>();
    if (allBeds) {
      for (const bed of allBeds) {
        if (!bedMap.has(bed.roomId)) bedMap.set(bed.roomId, []);
        bedMap.get(bed.roomId)?.push(bed);
      }
    }

    const tenantMap = new Map<number, any>();
    const tenantIds = new Set<number>();

    const occupancyByBed = new Map<number, any>();
    const occupancyByRoom = new Map<number, any[]>();
    if (allOccupancies) {
      for (const occ of allOccupancies) {
        if (!occupancyByRoom.has(occ.roomId)) occupancyByRoom.set(occ.roomId, []);
        occupancyByRoom.get(occ.roomId)?.push(occ);

        // if (occ.status === 'OCCUPIED' || occ.status === 'MOVING_OUT') {
        //   occupancyByBed.set(occ.bedId, occ);
        // }
        // occupancyByBed.set(occ.bedId, occ);
        if (!occupancyByBed.has(occ.bedId)) occupancyByBed.set(occ.bedId, []);
        occupancyByBed.get(occ.bedId)?.push(occ);

        tenantIds.add(occ.tenantId);
      }
    }

    const tenants = await tenantDB.getInfoByIds({ids: [...tenantIds]}); 
    if (tenants) {
      for (let tenant of tenants) {
        tenantMap.set(tenant.id, tenant);
      }
    }

    const duesMap = new Map<number, number>();
    if (allDues) {
      for (const d of allDues) {
        duesMap.set(d.tenantId, d.totalDues || 0);
      }
    }

    const flatDuesMap = new Map<number, number>();
    if (allFlatDues) {
      for (const d of allFlatDues) {
        flatDuesMap.set(d.flatId, d.totalDues || 0);
      }
    }

    if (rooms) {
      for (const flat of flats) {
        // const rooms = await roomDB.getRoomsByPropIdAndFlatId({
        //   propId: property?.id,
        //   flatId: flat.id,
        // });
        const rooms = roomMap.get(flat.id) || false;

        if (!rooms) {
          log.info(
            `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Flat Id [${flat.id}], No Rooms Available `
          );

          flat.rooms = [];
          flat.isFlatFullyOccupied = false;
          continue;
        }

        const isFlatFullyVacant = await flatDB.isFlatFullyVacant({
          id: flat.id,
        });

        let isFlatFullOccupied = await flatDB.isFlatFullyOccupied({
          id: flat.id,
        });

        // log.info(
        //   `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Flat Id [${flat.id}], isFlatFullyVacant [${isFlatFullyVacant}], isFlatFullOccupied [${isFlatFullOccupied}]`
        // );
        if (true === isFlatFullOccupied) {
          const flatOccupancies = await occupancyDB.getByFlatId({
            flatId: flat.id,
          });
          if (flatOccupancies) {
            isFlatFullOccupied = flatOccupancies.every(
              (occupancy: occupanciesTypes) =>
                occupancy.tenantId === flatOccupancies[0].tenantId
            );
          }
        }

        for (const room of rooms) {
          // const bedsOld = await bedDB.getDetailsByRoomId({
          //   roomId: room.id,
          // });
          const beds = bedMap.get(room.id) || false;

          // if(Number(clientId) === 93) log.info(`New Beds [${JSON.stringify(beds)}], Old Beds [${bedsOld}]`);

          if (beds) {
            for (let bed of beds) {
              let occupancyStatus = CONSTANTS.OCCUPANCY_STATUS.OCCUPIED;

              if (bed.status === CONSTANTS.BED_STATUS.MOVING_OUT) {
                occupancyStatus = CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT;
              } else if (bed.status === CONSTANTS.BED_STATUS.VACANT_RESERVED) {
                occupancyStatus = CONSTANTS.OCCUPANCY_STATUS.RESERVED;
              } else if (bed.status === CONSTANTS.BED_STATUS.RESERVED) {
                occupancyStatus = CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT;
              }
              bed.isDuePending = 0;
              // const occupancy = await occupancyDB.getByBedId({
              //   bedId: bed.id,
              //   status: occupancyStatus,
              // });
              const occupancies = occupancyByBed.get(bed.id);
              let occupancy: any = false;
              if (occupancies && occupancies.length > 0) {
                occupancy = occupancies[0];
              }
              if (occupancy) {
                // const { totalDues } = await duesDB.getTotalDuesByTenantId({
                //   tenantId: occupancy.tenantId,
                //   propId: propId,
                // });
                // bed.isDuePending = Number(totalDues) || 0;
                // bed.isDuePending = duesMap.get(occupancy.tenantId) || 0;
                const tenant = await tenantDB.getById({ id: occupancy.tenantId });
                bed.tenantName = tenant?.name || "";
                bed.tenantGender = tenant?.gender || 0;
              }

              bed.canAddTenant = true;

              if (occupancies) {
                for (let occupancy of occupancies) {
                  bed.isDuePending += Number(duesMap.get(occupancy.tenantId)) || 0;
                  const tenant = tenantMap.get(occupancy.tenantId);
                    occupancy.tenantName = tenant?.name || "";
                    occupancy.tenantMobile = tenant?.mobile || "";
                    occupancy.tenantGender = tenant?.gender || "";
                }
              }

              bed.occupancies = occupancies;

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

                let vacantSlots  = await getVacantSlots(tenants);
    
                if (vacantSlots.length === 1 && vacantSlots[0].endDate === null) {
                  bed.canAddTenant = true;
                } else if (vacantSlots.length === 0) {
                  bed.canAddTenant = false;
                }
              }
            }
          }
          room.beds = beds;

          // const occupancies = await occupancyDB.getByRoomId({
          //   roomId: room.id,
          // });
          const occupancies = occupancyByRoom.get(room.id) || false;

          // const bedCount = await bedDB.getCountsByRoomId({
          //   id: room.id,
          // });
          // const totalBeds = bedCount.totalBeds;
          const totalBeds = roomCountMap.get(room.id)?.[0]?.total || 0;

          if (!occupancies || totalBeds > occupancies.length || totalBeds === 1) {
            room.isFullyOccupied = false;
            // isFlatFullyOccupied = false;
          } else {
            room.isFullyOccupied = occupancies.every(
              (occupancy: occupanciesTypes) =>
                occupancy.tenantId === occupancies[0].tenantId
            );

            // if (!room.isFullyOccupied) {
            //   isFlatFullyOccupied = false;
            // }
          }
          room.isFlatVacant = isFlatFullyVacant;
          room.isFlatFullyOccupied = isFlatFullOccupied;
          room.flatName = flat.name;
          room.flatStatus = flat.status;
        }
        flat.rooms = rooms;
        // flat.isFlatFullyOccupied = isFlatFullyOccupied;

        // const flatDueTotal = await duesDB.getDueTotalByFlatId({
        //   clientId: clientId,
        //   flatId: flat.id,
        // });
        // flat.pendingDues = flatDueTotal.total || 0;
        flat.pendingDues = flatDuesMap.get(flat.id) || 0;

      }
    }

    const bedCount = await bedDB.getCountsByPropId({ propId: property?.id });
    const { dueTenantCount } = await duesDB.getDistinctTenantCountByPropId({
      propId: property?.id,
    });
    const totalRooms = await roomDB.getCountsByPropId({ propId });
    const totalTenants = await occupancyDB.getTotalCountByClientIdAndPropId({
      clientId,
      propId,
    });
    // const bedStats = await bedDB.getCountsByPropId({ propId });
    const vacantRooms = await roomDB.getRoomCountByPropIdAndStatus({
      propId,
      status: CONSTANTS.ROOM_STATUS.VACANT,
    });
    const semiVacantRooms = await roomDB.getRoomCountByPropIdAndStatus({
      propId,
      status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
    });
    const movingOut =
      await occupancyDB.getMovingOutTenantsCountByClientIdAndPropId({
        clientId,
        propId,
      });

    const stats = {
      totalRooms: totalRooms?.totalRooms || [],
      totalTenants: totalTenants?.totalOccupanciesCount || 0,
      totalBeds: bedCount?.total || 0,
      vacantBeds: bedCount?.vacant || 0,
      occupiedBeds: bedCount?.occupied || 0,
      vacantRooms: vacantRooms?.totalRooms || 0,
      semiVacantRooms: semiVacantRooms?.totalRooms || 0,
      movingOut: movingOut || 0,
      dueTenantCount: dueTenantCount || 0,
    };

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Room list sent successfully `
    );
    return res.status(200).json({
      msg: "Room list sent successfully",
      isSuccess: true,
      data: {
        flats,
        flatCount: Array.isArray(flats) ? flats.length : 0,
        property: {
          name: property.name,
          address: property.address,
          streetAddress: property.streetAddress,
          type: property.type,
          status: property.status,
        },
        beds: bedCount || [],
      },
      stats,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

rooms.EditRoomNum = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "EditRoomNum";

  try {
    const { roomId, newRoomNum, newExtendedName = null } = req.body;

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Room Id [${roomId}], New Room Num [${newRoomNum}], New Extended Room Name [${newExtendedName}], 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}], Room Id [${roomId}], New Room Num [${newRoomNum}], New Extended Room Name [${newExtendedName}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Num [${newRoomNum}], New Extended Room Name [${newExtendedName}], Client Requested....`
      );
    }

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

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

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

    if (!room) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Num [${newRoomNum}], New Extended Room Name [${newExtendedName}], No Room Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const isSameNameExists = await roomDB.getByRoomNumAndPropId({
      roomNum: newRoomNum,
      propId: room.propId,
      flatId: room.flatId,
    });

    if (isSameNameExists && isSameNameExists.id !== roomId) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Num [${newRoomNum}], Room Already Exists With Same roomNum`
      );
      return res.status(400).json({
        msg: "This room number is already exists",
        isSuccess: false,
      });
    }

    const isSameExtendedNameExists = await roomDB.getByExtendedNameAndPropId({
      extendedName: newExtendedName,
      propId: room.propId,
      flatId: room.flatId,
    });

    if (isSameExtendedNameExists && isSameExtendedNameExists.id !== roomId) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Extended Name [${newExtendedName}], Room Already Exists With Same Extended Name`
      );
      return res.status(400).json({
        msg: "This room name is already exists",
        isSuccess: false,
      });
    }

    await roomDB.updateRoomNum({
      id: roomId,
      roomNum: newRoomNum,
      extendedName: newExtendedName ? newExtendedName : room.extendedName,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Num [${newRoomNum}], Room number edited successfully`
    );

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

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

rooms.MakeActive = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "MakeActive";

  try {
    let { roomId } = req.body;

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Room Id [${roomId}], 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}], Room Id [${roomId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], Client Requested....`
      );
    }

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

    const rooms = await roomDB.getById({ id: roomId });
    if (!rooms) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Room Id [${roomId}], No Rooms Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    if (rooms.status != CONSTANTS.ROOM_STATUS.INACTIVE) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Room Id [${roomId}], Room is not inactive`
      );
      return res.status(400).json({
        msg: "This Room is already active",
        isSuccess: false,
      });
    }

    await roomDB.updateStatus({
      id: roomId,
      status: CONSTANTS.ROOM_STATUS.VACANT,
    });

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Room Id [${roomId}], Room activated successfully`
    );

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

rooms.MakeDeactive = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "MakeDeactive";

  try {
    let { roomId } = req.body;

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Room Id [${roomId}], 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}], Room Id [${roomId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], Client Requested....`
      );
    }

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

    const rooms = await roomDB.getById({ id: roomId });
    if (!rooms) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Room Id [${roomId}], No Rooms Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    if (rooms.status != CONSTANTS.ROOM_STATUS.VACANT) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Room Id [${roomId}], Room is not vacant`
      );
      return res.status(400).json({
        msg: "You can deactivate vacant rooms only",
        isSuccess: false,
      });
    }

    await roomDB.updateStatus({
      id: roomId,
      status: CONSTANTS.ROOM_STATUS.INACTIVE,
    });

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Room Id [${roomId}], Room deactivated successfully`
    );

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

rooms.SwitchRoom = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "SwitchRoom";

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

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Room Id [${roomId}], New Room Id [${newRoomId}], 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}], Room Id [${roomId}], New Room Id [${newRoomId}],  Tenant Id [${tenantId}], Rent [${rent}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}],  Tenant Id [${tenantId}], Rent [${rent}], Client Requested....`
      );
    }

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

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

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

    if (!room) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}],  Tenant Id [${tenantId}], Rent [${rent}], Old Room Not Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }
    const newRoom = await roomDB.getById({ id: newRoomId });

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

    if (newRoom.status === CONSTANTS.ROOM_STATUS.OCCUPIED) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}],  Tenant Id [${tenantId}], Rent [${rent}], New Room is not vacant`
      );
      return res.status(400).json({
        msg: "This room is not vacant",
        isSuccess: false,
      });
    }

    if (newRoom.status === CONSTANTS.ROOM_STATUS.INACTIVE) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}], Tenant Id [${tenantId}], Rent [${rent}], New Room is not active`
      );
      return res.status(400).json({
        msg: "This room is not active",
        isSuccess: false,
      });
    }

    const occupancy = await occupancyDB.getByTenantIdForSwitchRoom({
      tenantId,
      clientId,
    });

    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}], Tenant Id [${tenantId}], Rent [${rent}], Bed Id [${occupancy[0].bedId}], Occupancy Not Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    await duesDB.updateOccupancyByTenant({
      tenantId,
      clientId,
      occupancyId: occupancy[0].id,
      propId: occupancy[0].propId,
      roomId: newRoomId,
    });
    // await requestDB.updateOccupancyByTenant({
    //   tenantId,
    //   occupancyId: occupancy[0].id,
    // });

    for (let i = 1; i < occupancy.length; i++) {
      await occupancyDB.deleteFullOccupiedExtraEntry({
        tenantId,
        clientId,
      });

      if (occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
        await moveOutDB.deleteFullOccupiedExtraEntry({
          tenantId,
          clientId,
        });
      }
    }

    const bed = await bedDB.getVacantBed({
      roomId: newRoomId,
      status: CONSTANTS.BED_STATUS.VACANT,
    });

    if (!bed) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}], Tenant Id [${tenantId}], Rent [${rent}], No Vacant Bed Found`
      );
      return res.status(400).json({
        msg: "This room is not vacant",
        isSuccess: false,
      });
    }

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

    if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
      await occupancyDB.switchRoomAndFlat({
        id: occupancy[0].id,
        roomId: newRoomId,
        flatId: newRoom.flatId,
        bedId: bed.id,
        floor: newRoom.floor,
        rent: rent,
      });
      if (occupancy[0].status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
        await moveOutDB.switchRoomAndFlat({
          clientId: occupancy[0].clientId,
          tenantId: occupancy[0].tenantId,
          roomId: newRoomId,
          flatId: newRoom.flatId,
          bedId: bed.id,
          floor: newRoom.floor,
          rent: rent,
        });
      }
    } else {
      await occupancyDB.switchRoom({
        id: occupancy[0].id,
        roomId: newRoomId,
        bedId: bed.id,
        floor: newRoom.floor,
        rent: rent,
      });
      if (occupancy[0].status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
        await moveOutDB.switchRoomAndFlat({
          clientId: occupancy[0].clientId,
          tenantId: occupancy[0].tenantId,
          roomId: newRoomId,
          flatId: null,
          bedId: bed.id,
          floor: newRoom.floor,
          rent: rent,
        });
      }
    }

    await switchRoomRecordDB.add({
      clientId,
      tenantId,
      propId: occupancy[0].propId,
      roomId: occupancy[0].roomId,
      bedId: occupancy[0].bedId,
      floor: occupancy[0].floor,
      rent: occupancy[0].rent,
      newRoomId,
      newBedId: bed.id,
      newFloor: newRoom.floor,
      newRent: rent,
    });

    if (occupancy[0].status === CONSTANTS.OCCUPANCY_STATUS.RESERVED) {
      //update new room & bed status
      await bedDB.updateStatus({
        id: bed.id,
        status: CONSTANTS.BED_STATUS.VACANT_RESERVED,
      });
    } else if (occupancy[0].status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
      await bedDB.updateStatus({
        id: bed.id,
        status: CONSTANTS.BED_STATUS.MOVING_OUT,
      });
    } else {
      await bedDB.updateStatus({
        id: bed.id,
        status: CONSTANTS.BED_STATUS.OCCUPIED,
      });
    }

    const vacantBeds = await bedDB.getVacantBeds({
      roomId: newRoomId,
      status: CONSTANTS.BED_STATUS.VACANT,
    });

    if (!vacantBeds) {
      await roomDB.updateStatus({
        id: newRoom.id,
        status: CONSTANTS.ROOM_STATUS.OCCUPIED,
      });
    } else {
      await roomDB.updateStatus({
        id: newRoom.id,
        status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
      });
    }
    // const oldBed = await bedDB.getById({ id: occupancy.bedId });

    // if (oldBed?.status === CONSTANTS.BED_STATUS.RESERVED) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Bed Status [${oldBed?.status}], Setting Status Moving Out`
    //   );
    //   //update old room & bed status
    //   await bedDB.updateStatus({
    //     id: occupancy.bedId,
    //     status: CONSTANTS.BED_STATUS.MOVING_OUT,
    //   });
    // } else {
    //   //update old room & bed status
    //   await bedDB.updateStatus({
    //     id: occupancy.bedId,
    //     status: CONSTANTS.BED_STATUS.VACANT,
    //   });
    // }

    for (let occ of occupancy) {
      const oldBed = await bedDB.getById({ id: occ.bedId });

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

      if (tenants && tenants.length > 1) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Bed Status [${oldBed?.status}], Setting Status Reserved`
        );
        //update old room & bed status
        await bedDB.updateStatus({
          id: occ.bedId,
          status: CONSTANTS.BED_STATUS.RESERVED,
        });
      } else if (tenants && tenants.length === 1) {
        if (tenants[0].status === CONSTANTS.OCCUPANCY_STATUS.RESERVED) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Bed Status [${oldBed?.status}], Setting Status Reserved`
          );
          //update old room & bed status
          await bedDB.updateStatus({
            id: occ.bedId,
            status: CONSTANTS.BED_STATUS.VACANT_RESERVED,
          });
        } else if (tenants[0].status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Bed Status [${oldBed?.status}], Setting Status Reserved`
          );
          //update old room & bed status
          await bedDB.updateStatus({
            id: occ.bedId,
            status: CONSTANTS.BED_STATUS.MOVING_OUT,
          });
        }
      } else if (oldBed?.status === CONSTANTS.BED_STATUS.RESERVED) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Bed Status [${oldBed?.status}], Setting Status Moving Out`
        );
        //update old room & bed status
        if (occupancy[0].status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
          await bedDB.updateStatus({
            id: occ.bedId,
            status: CONSTANTS.BED_STATUS.VACANT_RESERVED,
          });
        } else {
          await bedDB.updateStatus({
            id: occ.bedId,
            status: CONSTANTS.BED_STATUS.MOVING_OUT,
          });
        }
      } else {
        //update old room & bed status
        await bedDB.updateStatus({
          id: occ.bedId,
          status: CONSTANTS.BED_STATUS.VACANT,
        });
      }
    }

    for (let occ of occupancy) {
      const vacantBedsForOldRoom = await bedDB.getVacantBeds({
        roomId: occ.roomId,
        status: CONSTANTS.BED_STATUS.VACANT,
      });

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

      const bedCount = await bedDB.getCountsByRoomId({
        id: room.id,
      });
      const totalBeds = bedCount.totalBeds;

      if (vacantBedsForOldRoom.length === totalBeds) {
        await roomDB.updateStatus({
          id: occ.roomId,
          status: CONSTANTS.ROOM_STATUS.VACANT,
        });
      } else {
        await roomDB.updateStatus({
          id: occ.roomId,
          status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
        });
      }
    }

    await transactionDB.updateRoomByTenantIdAndRoomId({
      tenantId,
      roomId: occupancy[0].roomId,
      newRoomId,
    });
    await duesDB.updateRoomByTenantIdAndRoomId({
      tenantId,
      roomId: occupancy[0].roomId,
      newRoomId,
    });
    await ledgerDB.updateRoomByTenantIdAndRoomId({
      tenantId,
      roomId: occupancy[0].roomId,
      newRoomId,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}], Tenant Id [${tenantId}], Rent [${rent}], Room Switched Successfully`
    );

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

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

rooms.SwitchRoomFullOccupied = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "SwitchRoomFullOccupied";

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

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Room Id [${roomId}], New Room Id [${newRoomId}], 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}], Room Id [${roomId}], New Room Id [${newRoomId}],  Tenant Id [${tenantId}], Rent [${rent}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}],  Tenant Id [${tenantId}], Rent [${rent}], Client Requested....`
      );
    }

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

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

    const room = await roomDB.getById({ id: roomId });
    if (!room) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}],  Tenant Id [${tenantId}], Rent [${rent}], Old Room Not Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const newRoom = await roomDB.getById({ id: newRoomId });
    if (!newRoom) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}],  Tenant Id [${tenantId}], Rent [${rent}], New Room Not Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    if (newRoom.status === CONSTANTS.ROOM_STATUS.OCCUPIED) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}],  Tenant Id [${tenantId}], Rent [${rent}], New Room is not vacant`
      );
      return res.status(400).json({
        msg: "This room is not vacant",
        isSuccess: false,
      });
    }

    if (newRoom.status === CONSTANTS.ROOM_STATUS.INACTIVE) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}], Tenant Id [${tenantId}], Rent [${rent}], New Room is not active`
      );
      return res.status(400).json({
        msg: "This room is not active",
        isSuccess: false,
      });
    }

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

    await occupancyDB.deleteFullOccupiedExtraEntry({
      tenantId,
      bedCount: Number(occupancy.length - 1),
    });

    const bed = await bedDB.getVacantBed({
      roomId: newRoomId,
      status: CONSTANTS.BED_STATUS.VACANT,
    });

    if (!bed) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], New Room Id [${newRoomId}], Tenant Id [${tenantId}], Rent [${rent}], No Vacant Bed Found`
      );
      return res.status(400).json({
        msg: "This room is not vacant",
        isSuccess: false,
      });
    }

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

    if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
      await occupancyDB.switchRoomAndFlat({
        id: occupancy[0].id,
        roomId: newRoomId,
        flatId: newRoom.flatId,
        bedId: bed.id,
        floor: newRoom.floor,
        rent: rent,
      });
    } else {
      await occupancyDB.switchRoom({
        id: occupancy[0].id,
        roomId: newRoomId,
        bedId: bed.id,
        floor: newRoom.floor,
        rent: rent,
      });
    }

    await switchRoomRecordDB.add({
      clientId,
      tenantId,
      propId: room.propId,
      roomId: occupancy[0].roomId,
      bedId: occupancy[0].bedId,
      floor: occupancy[0].floor,
      rent: occupancy[0].rent,
      newRoomId,
      newBedId: bed.id,
      newFloor: newRoom.floor,
      newRent: rent,
    });

    if (occupancy[0].status === CONSTANTS.OCCUPANCY_STATUS.RESERVED) {
      //update new room & bed status
      await bedDB.updateStatus({
        id: bed.id,
        status: CONSTANTS.BED_STATUS.VACANT_RESERVED,
      });
    } else {
      await bedDB.updateStatus({
        id: bed.id,
        status: CONSTANTS.BED_STATUS.OCCUPIED,
      });
    }

    const vacantBeds = await bedDB.getVacantBeds({
      roomId: newRoomId,
      status: CONSTANTS.BED_STATUS.VACANT,
    });

    if (!vacantBeds) {
      await roomDB.updateStatus({
        id: newRoom.id,
        status: CONSTANTS.ROOM_STATUS.OCCUPIED,
      });
    } else {
      await roomDB.updateStatus({
        id: newRoom.id,
        status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
      });
    }

    for (let occ of occupancy) {
      const oldBed = await bedDB.getById({ id: occ.bedId });

      if (oldBed?.status === CONSTANTS.BED_STATUS.RESERVED) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Bed Status [${oldBed?.status}], Setting Status Moving Out`
        );
        //update old room & bed status
        await bedDB.updateStatus({
          id: occ.bedId,
          status: CONSTANTS.BED_STATUS.MOVING_OUT,
        });
      } else {
        //update old room & bed status
        await bedDB.updateStatus({
          id: occ.bedId,
          status: CONSTANTS.BED_STATUS.VACANT,
        });
      }
    }

    const vacantBedsForOldRoom = await bedDB.getVacantBeds({
      roomId: room.id,
      status: CONSTANTS.BED_STATUS.VACANT,
    });

    const bedCount = await bedDB.getCountsByRoomId({
      id: room.id,
    });
    const totalBeds = bedCount.totalBeds;

    if (vacantBedsForOldRoom.length === totalBeds) {
      await roomDB.updateStatus({
        id: room.id,
        status: CONSTANTS.ROOM_STATUS.VACANT,
      });
    } else if (vacantBedsForOldRoom.length === 0) {
      await roomDB.updateStatus({
        id: room.id,
        status: CONSTANTS.ROOM_STATUS.OCCUPIED,
      });
    } else {
      await roomDB.updateStatus({
        id: room.id,
        status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
      });
    }
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

rooms.AddExtraBeds = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "AddExtraBeds";

  try {
    const { roomId, bedCount } = req.body;

    log.info(`[${C}], [${F}], Room Id [${roomId}], Bed Count [${bedCount}]`);

    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}]`
        }, Platform [${req.platform
        }], Room Id [${roomId}], Bed Count [${bedCount}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Room Id [${roomId}], Bed Count [${bedCount}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (
        staff.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
        staff.role !== CONSTANTS.STAFF_ROLES.WARDEN &&
        staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE
      ) {
        log.info(
          `[${C}], [${F}], Room Id [${roomId}], Bed Count [${bedCount}], Staff Id [${req.id}], Staff Not Admin`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], Bed Count [${bedCount}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin/Warden Requesting....`
      );
    }

    const room = await roomDB.getById({ id: roomId });
    if (!room) {
      log.info(
        `[${C}], [${F}], Room Id [${roomId}], Bed Count [${bedCount}], Room Not Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    for (let i = 1; i <= bedCount; i++) {
      const bedId = await bedDB.create({
        roomId,
        isExtraBed: 1,
      });
    }

    if (room.status === CONSTANTS.ROOM_STATUS.OCCUPIED) {
      await roomDB.updateStatus({
        id: roomId,
        status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
      });
    }
    await logPropertyActivity(
      req.userType!,
      Number(req.id),
      Number(req.parentClientId!),
      req.platform!,
      CONSTANTS.ACTIVITY_TYPES.ADD_BED,
      room?.propId,
      room?.id,
      Number(bedCount),
    );

    log.info(
      `[${C}], [${F}], Room Id [${roomId}], Bed Count [${bedCount}], Successfully Added Extra Beds`
    );
    return res.status(200).json({
      msg: "Extra bed has been added 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,
    });
  }
};

rooms.RemoveExtraBeds = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "RemoveExtraBeds";

  try {
    const { bedId } = req.body;

    log.info(`[${C}], [${F}], Bed Id [${bedId}]`);

    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}]`
        }, Platform [${req.platform}], Bed Id [${bedId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Bed Id [${bedId}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (
        staff.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
        staff.role !== CONSTANTS.STAFF_ROLES.WARDEN &&
        staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE
      ) {
        log.info(
          `[${C}], [${F}], Bed Id [${bedId}], Staff Id [${req.id}], Staff Not Admin`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

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

    const bed = await bedDB.getById({ id: bedId });
    if (!bed) {
      log.info(`[${C}], [${F}], Bed Id [${bedId}], Bed Not Found`);
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    // if (bed.isExtraBed !== 1) {
    //   log.info(`[${C}], [${F}], Bed Id [${bedId}], Not an Extra Bed`);
    //   return res
    //     .status(400)
    //     .json({ msg: "Bed is not an extra bed", isSuccess: false });
    // }

    if (bed.status !== CONSTANTS.BED_STATUS.VACANT) {
      log.info(
        `[${C}], [${F}], Bed Id [${bedId}], Bed Not Vacant, Can't Delete`
      );
      return res
        .status(400)
        .json({ msg: "Bed is not vacant", isSuccess: false });
    }

    const room = await roomDB.getById({ id: bed.roomId });
    if (!room) {
      log.info(`[${C}], [${F}], Bed Id [${bedId}], Room Not Found`);
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await occupancyDB.deletePendingByBedId({
      bedId: bedId,
    });

    await bedDB.delete({ id: bedId });

    if (room.status === CONSTANTS.ROOM_STATUS.OCCUPIED) {
      await roomDB.updateStatus({
        id: room.id,
        status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
      });
    } else if (room.status === CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED) {
      const beds = await bedDB.getByRoomId({ roomId: room.id });
      const vacantBeds = beds.filter(
        (bed: any) => bed.status === CONSTANTS.BED_STATUS.VACANT
      );
      if (beds.length === 0 || beds.length === vacantBeds.length) {
        await roomDB.updateStatus({
          id: room.id,
          status: CONSTANTS.ROOM_STATUS.VACANT,
        });
      } else if (vacantBeds.length === 0) {
        await roomDB.updateStatus({
          id: room.id,
          status: CONSTANTS.ROOM_STATUS.OCCUPIED,
        });
      }
    }
    await logPropertyActivity(
      req.userType!,
      Number(req.id),
      Number(req.parentClientId!),
      req.platform!,
      CONSTANTS.ACTIVITY_TYPES.REMOVE_BED,
      room?.propId,
      room?.id,
      0,
    );
    
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Bed Id [${bedId}], Successfully Deleted Extra Beds`
    );
    return res.status(200).json({
      msg: `${bed.isExtraBed !== 1 ? "Bed" : "Extra bed"} has been 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,
    });
  }
};

rooms.EditBunkPosition = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "EditBunkPosition";

  try {
    const { bedId, bunkPosition } = req.body;

    log.info(`[${C}], [${F}], Bed Id [${bedId}], Bunk Position [${bunkPosition}]`);

    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}]`
        }, Platform [${req.platform}], Bed Id [${bedId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Bed Id [${bedId}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (
        staff.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
        staff.role !== CONSTANTS.STAFF_ROLES.WARDEN &&
        staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE
      ) {
        log.info(
          `[${C}], [${F}], Bed Id [${bedId}], Staff Id [${req.id}], Staff Not Admin`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Bed Id [${bedId}], Staff Id [${req.id}], Staff Role [${staff.role}], Platform [${req.platform}], Admin/Warden Requesting....`
      );
    }

    const bed = await bedDB.getById({ id: bedId });
    if (!bed) {
      log.info(`[${C}], [${F}], Bed Id [${bedId}], Bed Not Found`);
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (bed.status !== CONSTANTS.BED_STATUS.VACANT) {
      log.info(
        `[${C}], [${F}], Bed Id [${bedId}], Bed Not Vacant, Can't Update`
      );
      return res
        .status(400)
        .json({ msg: "Bed is not vacant", isSuccess: false });
    }

    await bedDB.updateBunkPosition({
      id: bedId,
      bunkPosition: bunkPosition,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Bed Id [${bedId}], Old Position [${bed.bunkPosition}], New Bunk Position [${bunkPosition}]`
    );

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

rooms.DeleteRoomPackage = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "DeleteRoomPackage";

  try {
    const { id } = req.body;
    log.info(`[${C}], [${F}], Package Id [${id}]`);

    const userType = req.userType;

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Platform [${req.platform}], Package Id [${id}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Package Id [${id}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (
        staff.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
        staff.role !== CONSTANTS.STAFF_ROLES.WARDEN &&
        staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE
      ) {
        log.info(
          `[${C}], [${F}], Package Id [${id}], Staff Id [${req.id}], Staff Not Admin`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

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

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

    const isUsed = await roomDB.getByRoomOptionId({ roomOptionId: id });
    if (isUsed) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Package Id [${id}], Package is being used, cannot delete`
      );
      return res.status(400).json({
        msg: "This package is used in rooms, cannot delete",
        isSuccess: false,
      });
    }

    await roomOptionDB.delete({ id });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Package[${id}] has been deleted successfully `
    );

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


rooms.UpdateRoomOcccupancy = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "UpdateRoomOcccupancy";

  try {
    let { tenantId, bedId } = req.body;
    let clientId = req.id;
    const userType = req.userType;

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

      clientId = staff.clientId;

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

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

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

    const occupancies = await occupancyDB.getDetailByTenantIdAndClientIdOrderByBedId({tenantId, clientId});
    if (!occupancies) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Bed Id [${bedId}], No Occupancy Found`);
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    let totalOccupancies = occupancies.length;
    let roomId = 0;
    if(totalOccupancies && totalOccupancies > 1 ){
        let isFirstOccupancy = true;
        for (let occupancy of occupancies) {
          if (isFirstOccupancy) {
             if(!bedId) {
              bedId = occupancy.bedId;
              roomId = occupancy.roomId;
            }   
            await roomDB.updateStatus({id: occupancy?.roomId, status : CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED});
            await occupancyDB.updateBedId({
              id: occupancy.id,
              bedId: bedId,
            });
            if (occupancy.bedId !== bedId) {
              await bedDB.updateStatus({id: occupancy?.bedId, status: CONSTANTS.BED_STATUS.VACANT});
            }

            await duesDB.updateOccupancyIdByClientIdAndTenantId({clientId, tenantId, occupancyId: occupancy?.id});
            isFirstOccupancy = false;
            continue;
          }

          if (occupancy.bedId !== bedId) {
            await bedDB.updateStatus({id: occupancy?.bedId, status: CONSTANTS.BED_STATUS.VACANT});
          }
          
          await occupancyDB.deleteById({id : occupancy?.id});
          
          if(occupancy?.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
            await moveOutDB.deleteByRoomIdBedIdAndTenantId({tenantId, roomId: occupancy?.roomId, bedId: occupancy?.bedId, clientId,});
          }

          // if(occupancy.roomId != roomId) {
          //   await roomDB.updateStatus({id: occupancy?.roomId, status : CONSTANTS.ROOM_STATUS.VACANT});
          // }
                
        }
        await logActivity(
              Number(req.userType),
              Number(req.id),
              Number(req.parentClientId!),
              Number(req.platform),
              Number(tenantId),
              CONSTANTS.ACTIVITY_TYPES.TENANT_OCCUPANCY_CHANGE,
              totalOccupancies,
              null,
              null,
              null,
              null
            );
        log.info(`[${C}], [${F}], Client Id [${clientId}], Room Id [${tenantId}], Tenant Moved To Single Bed Successfully`);
        return res.status(200).json({
          msg: "Tenant has been successfully moved to a single bed.",
          isSuccess: true,
        });
    }
    return res.status(200).json({
      msg: "Tenant already is in single bed",
      isSuccess: false,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

rooms.OccupyFullRoom = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "OccupyFullRoom";

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

      clientId = staff.clientId;

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

    const room = await roomDB.getById({ id: roomId });
    if (!room) {
      log.info(`[${C}], [${F}], Room Id [${roomId}], No Room Found`);
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const beds = await bedDB.getByRoomId({
      roomId: roomId,
    });

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

    if (beds.length === 0) {
      log.info(`[${C}], [${F}], Room Id [${roomId}], No Beds Found`);
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    for (let bed of beds) {
      let occupancy = await occupancyDB.getByBedIdWithoutStatus({bedId : bed.id});
      occupancy = occupancy[0];

      if (occupancy && (Number(occupancy.tenantId) !== Number(tenantId))) {
        log.info(`[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], Bed Id [${bed.id}], Tenant Id [${tenantId}], Another Tenant Occupying Bed`);
        return res.status(400).json({ 
          msg: "Room is already occupied by another tenant", 
          isSuccess: false 
        });
      }
    }

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

    for (let bed of beds) {
      if (Number(bed.id) === Number(occupancy.bedId)) continue;
      if(bed.status === CONSTANTS.BED_STATUS.VACANT) {
        let occupancyId = await occupancyDB.duplicateOccupancy({
          clientId,
          tenantId,
          propId: room.propId,
          roomId,
          bedId: bed.id,
          floor: room.floor,
          rent: occupancy.rent,
          rentalCycle: occupancy.rentalCycle,
          rentalType: occupancy.rentalType,
          rentalMonths: occupancy.rentalMonths,
          noticePeriod: occupancy.noticePeriod,
          lockInPeriod: occupancy.lockInPeriod,
          agreementPeriod: occupancy.agreementPeriod,
          security: occupancy.security,
          agreementStartDate: occupancy.agreementStartDate,
          moveInDate: occupancy.moveInDate,
          moveOutDate: occupancy.moveOutDate,
          status: occupancy.status,
          electricityReading: occupancy.electricityReading,
          fine: occupancy.fine,
          fineType: occupancy.fineType,
          gracePeriod: occupancy.gracePeriod,
          isFineEnabled: occupancy.isFineEnabled,
          isOnlinePaymentEnabled: occupancy.isOnlinePaymentEnabled,
          flatId: occupancy.flatId,
          rentalBond: occupancy.rentalBond,
          stayType: occupancy.stayType,
          notes: occupancy.notes,
          moveOutReason: occupancy.moveOutReason,
          isPoliceVerified: occupancy.isPoliceVerified,
          isRentAgreementSigned: occupancy.isRentAgreementSigned,
          kycStatus: occupancy.kycStatus,
          discountType: occupancy.discountType,
          discount: occupancy.discount,
          discountPeriod: occupancy.discountPeriod,
          discountStartDate: occupancy.discountStartDate,
          discountEndDate: occupancy.discountEndDate,
          isDiscountFromFirstMonth: occupancy.isDiscountFromFirstMonth,
          verificationId: occupancy.verificationId,
          referenceId: occupancy.referenceId,
          gId: occupancy.gId,
          lastRemindedOn: occupancy.lastRemindedOn,
          isFoodOpted: occupancy.isFoodOpted,
          bookingAmt: occupancy.bookingAmt,
          bookedBy: occupancy.bookedBy,
          tallyStatus: occupancy.tallyStatus,
          tallyLedgerName: occupancy.tallyLedgerName,
          isGstEnabled: occupancy.isGstEnabled,
        });
        await bedDB.updateStatus({ id: bed.id, status: CONSTANTS.BED_STATUS.OCCUPIED });
      }
    }

    await roomDB.updateStatus({ id: roomId, status: CONSTANTS.ROOM_STATUS.OCCUPIED });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Room Id [${roomId}], Room Occupied Successfully`);
    return res.status(200).json({
      msg: "Room has been successfully occupied",
      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,
    });
  }
};

rooms.ListBedHistory = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "ListBedHistory";

  try {
    const { bedId } = req.params;
    const userType = req.userType;
    let clientId = req.id;

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

      clientId = staff.clientId;

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

    const occupancies = await occupancyDB.getCompleteHistoryByBedId({ bedId });

    log.info(`[${C}], [${F}], Bed Id [${bedId}], Bed History Sent Successfully`);

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

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

rooms.HoldBed = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "HoldBed";

  try {
    const { bedId, status = CONSTANTS.BED_STATUS.ON_HOLD } = req.body;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Bed Id [${bedId}], Status [${status}]`);

    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}]`
        }, Platform [${req.platform}], Bed Id [${bedId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Bed Id [${bedId}], 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}], Bed Id [${bedId}], Staff Id [${req.id}], Staff Role [${staff.role}], Platform [${req.platform}], Staff Requesting....`
      );
    }

    const bed = await bedDB.getById({ id: bedId });
    if (!bed) {
      log.info(
        `[${C}], [${F}], Bed Id [${bedId}], No Bed Found`
      );
      return res.status(400).json({ 
        msg: "No bed record found", 
        isSuccess: false 
      });
    }

    const room = await roomDB.getById({ id: bed?.roomId });
    if (!room) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Bed Id [${bedId}], Room Id [${bed?.roomId}], No Room Found`
      );
      return res.status(400).json({ 
        msg: "No room record found", 
        isSuccess: false 
      });
    }
    
    if (Number(status) === CONSTANTS.BED_STATUS.ON_HOLD) {
      if (Number(bed.status) !== CONSTANTS.BED_STATUS.VACANT) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Bed Id [${bedId}], Bed Status [${bed?.status}], Bed is not vacant, Can Hold Only Vacant Beds`
        );
        return res.status(400).json({ 
          msg: "Bed is currently not vacant", 
          isSuccess: false 
        });
      }
    } else if (Number(status) === CONSTANTS.BED_STATUS.VACANT) {
      if (Number(bed.status) !== CONSTANTS.BED_STATUS.ON_HOLD) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Bed Id [${bedId}], Bed Status [${bed?.status}], Bed is not on hold, Can Make Bed Vacant Only When Bed Is On Hold`
        );
        return res.status(400).json({ 
          msg: "Bed is currently not on hold", 
          isSuccess: false 
        });
      }
    }

    await bedDB.updateStatus({
      id: bedId,
      status: status,
    });

    if (Number(status) === CONSTANTS.BED_STATUS.VACANT) {
      const bedCount = await bedDB.getCountsByRoomId({
        id: room.id,
      });
      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,
        });
      }
      
    } else if (Number(status) === CONSTANTS.BED_STATUS.ON_HOLD) {
      const vacantBed = await bedDB.getVacantBeds({
        roomId: room.id,
        status: CONSTANTS.BED_STATUS.VACANT,
      });
      if (!vacantBed) {
        await roomDB.updateStatus({
          id: room.id,
          status: CONSTANTS.ROOM_STATUS.OCCUPIED,
        });
      } else {
        await roomDB.updateStatus({
          id: room.id,
          status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
        });
      }
      
    }

    log.info(`[${C}], [${F}], Client Id [${clientId}], Bed Id [${bedId}], ${status === CONSTANTS.BED_STATUS.ON_HOLD ? "Bed Put On Hold Successfully" : "Bed Made Vacant Successfully"}`);

    return res.status(200).json({
      msg: status === CONSTANTS.BED_STATUS.ON_HOLD ? "Bed has been put on hold successfully" : "Bed has been made vacant 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,
    });
  }
};

rooms.List = async (req: CustomRequest, res: Response) => {
  const C = "Room Controller";
  const F = "List";

  try {
    let { propId } = req.query;
    const userType = req.userType;

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

    let propFilter = [];


    if (propId && typeof propId === 'string') {
      propFilter = propId.split(',').map(v => v.trim()).filter(Boolean);
    } else {
      propFilter = [propId];
    }

    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}]`
        }, Platform [${req.platform}], ${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}], Platform [${req.platform}], Staff Requesting....`
      );
    }

    let rooms = await roomDB.getByClientIdAndFilter({
      clientId,
      propIds: propFilter && propFilter.length > 0 ? propFilter : null,
    });

    //log.info(`Rooms [${JSON.stringify(rooms)}]`);

    log.info(`[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Room List Sent Successfully`);

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