import { Request, Response } from "express";
import clientDB from "../models/client.model";
import log from "../config/log";
import CONSTANTS from "../config/constants";
import propertyDB from "../models/property.model";
import CustomRequest from "../types/requestType";
import tenantDB from "../models/tenant.model";
import staffDB from "../models/staff.model";
import propertiesTypes from "../schemas/property.schema";
import complaintDB from "../models/complaint.model";
import { sendWhatsappStaffKycDone, sendWhatsappStaffWelcome } from "../utils/sendWhatsappWithConfig";
import staffLedgerDB from "../models/staffLedger.model";
import staffBalanceDB from "../models/staffBalance.model";
import moment from "moment";
import { complaintStaffCountsForYearMonth } from "../utils/client/complaintsByUserType";
import { getMatchedStaffLedgerType } from "../utils/client/getMatchedTransactionFor";
import staffAttendanceDB from "../models/staffAttendance.model";
import fsPromises from "fs/promises";
import fs, { stat } from "fs";
import axios from "axios";
import serviceProviderDB from "../models/serviceProvider.model";
import {
  addAadhaarNumberToCashfree,
  addAadhaarNumberToCashfreeX,
  uploadAadhaarToCashfree,
  uploadAadhaarToCashfreeStaff,
  verifyOTPFromCashfreeX,
  createDigiLockerCashfreeLinkViaClient,
  createDigiLockerVerifyRequestViaClient,
} from "../utils/client/id_verification/cashfree";
import staffDocumentDB from "../models/staffDocument.model";
import {
  addAadhaarNumberToSignzy,
  verifyOTPFromSignzy,
} from "../utils/client/id_verification/signzy";
import ipAddressDB from "../models/ipAddress.model";
import leadDB from "../models/lead.model";
import { isUserPartner, staffSalaryModulePermission } from "../utils/isUserPartner";
import recurringExpenseDB from "../models/recurringExpense.model";
import expenseDB from "../models/expense.model";
import { createBusinessCard, roleDescription } from "../utils/client/createBusinessCard";
import getStaffLinkedProperties from "../utils/client/getStaffLinkedProperties";
import { any } from "zod";
import getDocumentTitle, { getStaffDocumentTitle } from "../utils/getDocumentTitle";
import leadActivityLogsDB from "../models/leadActivityLogs.model";
import { setExpenseJournalTallyStatus, setExpensePaymentTallyStatus } from "../utils/setTallyStatus";
import { logStaffAttendanceManual } from "../utils/logActivity";
import occupancyDB from "../models/occupancy.model";
import moveOutDB from "../models/moveOut.model";
import { isPrivilegedStaff } from "../utils/isPrivilegedStaff";

const staffs: any = {};

staffs.Create = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "Create";

  try {
    let { name, role, mobile, properties, salary, gender, commission = null, commissionType = null, isOnPayroll = 1, isSuperAdmin=0, } = 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}], Role [${role}], Mobile [${mobile}], Properties Length [${properties?.length}], Salary [${salary}], Gender [${gender}], 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}], Role [${role}], Mobile [${mobile}], Properties Length [${properties?.length}], Salary [${salary}], Gender [${gender}], Commission [${commission}], Commission Type [${commissionType}], Staff Id [${req.id}], isOnPayroll [${isOnPayroll}], Is Super Admin [${isSuperAdmin}], Staff Requested....`
      );

      // if (
      //   staff.role === CONSTANTS.STAFF_ROLES.ADMIN &&
      //   role === CONSTANTS.STAFF_ROLES.ADMIN
      // ) {
      //   log.info(
      //     `[${C}], [${F}], Name [${name}], Role [${role}], Mobile [${mobile}], Properties Length [${properties.length}], Salary [${salary}], Gender [${gender}], Staff Id [${req.id}], Admin is not allowed to add admin`
      //   );
      //   return res
      //     .status(400)
      //     .json({ msg: "You are not allowed to add admin", isSuccess: false });
      // }
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Properties Length [${properties?.length}], Salary [${salary}], Gender [${gender}], Commission [${commission}], Commission Type [${commissionType}], isOnPayroll [${isOnPayroll}], Is Super Admin [${isSuperAdmin}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Salary [${salary}], Gender [${gender}], No Client Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    if (mobile === client.mobile) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Salary [${salary}], Gender [${gender}], Client tries to add yourself as staff`
      );

      return res.status(400).json({
        msg: "You can't add yourself as staff",
        isSuccess: false,
      });
    }

    let isExistsWithSameMobile = false;
    const isStaffExists = await staffDB.getByMobileAndClientId({
      mobile,
      clientId,
    });

    if (isStaffExists) {
      if (isStaffExists?.status === CONSTANTS.STAFF_STATUS.ACTIVE)
        isExistsWithSameMobile = true;
    }

    if (!isExistsWithSameMobile) {
      const isSameClientMobile = await clientDB.getByMobile({ mobile });
      if (isSameClientMobile) isExistsWithSameMobile = true;
    }

    if (!isExistsWithSameMobile) {
      const isSameTenantMobile = await tenantDB.getByMobile({ mobile });
      if (isSameTenantMobile) isExistsWithSameMobile = true;
    }

    if (isExistsWithSameMobile) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Salary [${salary}], Gender [${gender}], Mobile number is already linked with another account`
      );

      return res.status(400).json({
        msg: "Mobile number is already linked with another account",
        isSuccess: false,
      });
    }

    const staffId = await staffDB.create({
      name,
      role,
      mobile,
      clientId,
      gender,
      salary: salary || 0,
      commission: commission || null,
      commissionType: Number(commissionType) ?? null,
      isOnPayroll: isOnPayroll,
      isSuperAdmin: isSuperAdmin || 0,
    });

    if (role === CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        //permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COLLECTIONS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_DUES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_EXPENSES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_EXTRACHARGES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REPORTS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_STAFF_SALARY) | (1 << CONSTANTS.PERMISSIONS.MANAGE_EDIT_DUES) | (1 << CONSTANTS.PERMISSIONS.MANAGE_DELETE_DUES) 
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COLLECTIONS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_DUES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_EXPENSES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REPORTS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_EDIT_DUES) | (1 << CONSTANTS.PERMISSIONS.MANAGE_DELETE_DUES) | (1 << CONSTANTS.PERMISSIONS.MANAGE_FOOD_INVENTORY) | (1 << CONSTANTS.PERMISSIONS.MANANGE_LANDLORD) | (1 << CONSTANTS.PERMISSIONS.MANAGE_VENDOR) 
      });
      const propertyList = await propertyDB.getAllByClientId({
        clientId: clientId,
      });
      //Pallav As properties were linking moultiple times
      // for (let property of properties) {
      //   await staffDB.link({
      //     clientId,
      //     propId: property.id,
      //     staffId
      //   });
      // }
      properties = propertyList.map((item: any) => item.id);

    } else if (role === CONSTANTS.STAFF_ROLES.SALES_HEAD) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_REPORTS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_STAFF_SALARY) | (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES) | (1 << CONSTANTS.PERMISSIONS.MARK_STAFF_ATTENDANCE) | (1 << CONSTANTS.PERMISSIONS.MANAGE_STAFF_PERMISSION) | (1 << CONSTANTS.PERMISSIONS.MANANGE_STAFF) | (1 << CONSTANTS.PERMISSIONS.MANANGE_LEADS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_TENANTS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_DOCUMENTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REQUESTS)  
      });

    } else if (role === CONSTANTS.STAFF_ROLES.SALESPERSON) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_LEADS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_TENANTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REQUESTS)  
      });

    } else if (role === CONSTANTS.STAFF_ROLES.PARTNER) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COLLECTIONS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_DUES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_EXPENSES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_EXTRACHARGES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REPORTS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_STAFF_SALARY)
      });

    } else if (role === CONSTANTS.STAFF_ROLES.HELPDESK) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COMPLAINTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES)
      });
    } else if (role === CONSTANTS.STAFF_ROLES.ADMIN && isSuperAdmin === 1 ) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: parseInt(CONSTANTS.DEFAULT_PERMISSION_TO_SET_ALL, 2)
      });
      
    } else if (role === CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COLLECTIONS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_DUES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_EXPENSES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_TRANS_RESTORE) | (1 << CONSTANTS.PERMISSIONS.MANANGE_EXTRACHARGES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_LEADS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_STAFF) | (1 << CONSTANTS.PERMISSIONS.MANANGE_TENANTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REPORTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_INVENTORY) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REQUESTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_SENDNOTICES)
      });
    } else if (role === CONSTANTS.STAFF_ROLES.FOOD_VENDOR) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANAGE_FOOD),
      });
    } else if (role === CONSTANTS.STAFF_ROLES.KITCHEN_MANAGER) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COMPLAINTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES) | (1 << CONSTANTS.PERMISSIONS.MANAGE_FOOD) | (1 << CONSTANTS.PERMISSIONS.MANAGE_FOOD_INVENTORY) | (1 << CONSTANTS.PERMISSIONS.MANAGE_VENDOR) | (1 << CONSTANTS.PERMISSIONS.MANANGE_SENDNOTICES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REPORTS)
      });
    } else if (role === CONSTANTS.STAFF_ROLES.MAINTENANCE_SUPERVISOR) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COMPLAINTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_PROPERTY_ASSETS)
      });
    } else if (role === CONSTANTS.STAFF_ROLES.IT_ADMIN) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COMPLAINTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_INVENTORY) | (1 << CONSTANTS.PERMISSIONS.MANANGE_PROPERTY_ASSETS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_STAFF) | (1 << CONSTANTS.PERMISSIONS.MANANGE_SENDNOTICES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REPORTS) 
      });
    } else if (role === CONSTANTS.STAFF_ROLES.BUS_INCHARGE) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES) | (1 << CONSTANTS.PERMISSIONS.MANAGE_BUS_SERVICE)
      });
    } else if (role === CONSTANTS.STAFF_ROLES.WARDEN) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COMPLAINTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES) | (1 << CONSTANTS.PERMISSIONS.MANAGE_LAUNDRY) | (1 << CONSTANTS.PERMISSIONS.MANAGE_FOOD) | (1 << CONSTANTS.PERMISSIONS.MANANGE_TENANTS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_MARK_PAID) | (1 << CONSTANTS.PERMISSIONS.MANANGE_DUES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_INVENTORY) | (1 << CONSTANTS.PERMISSIONS.MANANGE_SENDNOTICES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REQUESTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_LEADS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_BUS_SERVICE) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REPORTS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_HOUSEKEEPING) | (1 << CONSTANTS.PERMISSIONS.MANAGE_OTHER_STAFF_HOUSEKEEPING)
      });
    
    } else if (role === CONSTANTS.STAFF_ROLES.ADMIN) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COMPLAINTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES) | (1 << CONSTANTS.PERMISSIONS.MANAGE_LAUNDRY) | (1 << CONSTANTS.PERMISSIONS.MANAGE_FOOD) | (1 << CONSTANTS.PERMISSIONS.MANANGE_TENANTS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_MARK_PAID) | (1 << CONSTANTS.PERMISSIONS.MANANGE_DUES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_INVENTORY) | (1 << CONSTANTS.PERMISSIONS.MANANGE_SENDNOTICES) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REQUESTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_PROPERTY_ASSETS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_LEADS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_BUS_SERVICE) | (1 << CONSTANTS.PERMISSIONS.MANANGE_REPORTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_COLLECTIONS) | (1 << CONSTANTS.PERMISSIONS.MANAGE_HOUSEKEEPING) | (1 << CONSTANTS.PERMISSIONS.MANAGE_OTHER_STAFF_HOUSEKEEPING)
      });
    } else if (role === CONSTANTS.STAFF_ROLES.HOUSEKEEPING) {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_COMPLAINTS) | (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES) | (1 << CONSTANTS.PERMISSIONS.MANAGE_LAUNDRY) | (1 << CONSTANTS.PERMISSIONS.MANAGE_HOUSEKEEPING) 
      });
    } else {
      await staffDB.updateStaffPermissions({
        id: staffId,
        permissions: (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES),
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Staff Id [${staffId}], Salary [${salary}], Gender [${gender}], Staff has been added successfully`
    );

    if (isSuperAdmin === 1) {
      const propertyList = await propertyDB.getAllByClientId({
        clientId: clientId,
      });
      properties = propertyList.map((item: any) => item.id);
    }

    let propName = "";
    for (const propertyId of properties) {
      try {
        propName = propName == "" ? "" : propName + ", ";
        const property = await propertyDB.getById({ id: propertyId });
        if (!property) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Property Id [${propertyId}], Salary [${salary}], Gender [${gender}], No Property Found`
          );
          continue;
        }
        propName = propName + property.name;
        await staffDB.link({ staffId, propId: propertyId, clientId });
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Property Id [${propertyId}], Salary [${salary}], Gender [${gender}], Staff linked successfully`
        );
      } catch (error: any) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Property Id [${propertyId}], Salary [${salary}], Gender [${gender}], Error [${error?.message || error
          }]`
        );
      }
    }

    if (Number(salary) > 0) {
      let paidDate = moment().date(1).format("YYYY-MM-DD");
      let dueDate = moment().add(1, "month").date(1).format("YYYY-MM-DD");
      let expenseId = null;

      if (moment().date() !== 1) {
        paidDate = moment().add(1, "month").date(1).format("YYYY-MM-DD");
      } else {
        // expenseId = await expenseDB.create({
        //   type: 14,
        //   amount: salary,
        //   clientId,
        //   paidDate: paidDate,
        //   paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        //   paidBy: clientId,
        //   paidTo: staffId,
        //   paidToUserType: CONSTANTS.USER_TYPE.STAFF,
        //   description: "Staff salary",
        //   paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
        //   repetitionType: 2,
        //   noOfMonths: 1,
        //   dueDate: dueDate,
        //   isPaid: 0,
        // });
      }

      // const recurringExpenseId = await recurringExpenseDB.create({
      //   type: 14,
      //   amount: salary,
      //   clientId,
      //   propId: null,
      //   paidDate: paidDate,
      //   paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
      //   paidBy: clientId,
      //   paidTo: staffId,
      //   paidToUserType: CONSTANTS.USER_TYPE.STAFF,
      //   description: "Staff salary",
      //   paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
      //   noOfMonths: 1,
      //   dueDate: dueDate,
      //   expenseCycle: 1,
      // });

      const recurringExpenseId = await recurringExpenseDB.createNew({
        type: 14, //Salary
        amount: salary,
        clientId,
        propId: null,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: staffId,
        paidToUserType: CONSTANTS.USER_TYPE.STAFF,
        description: `Staff salary`,
        paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
        noOfMonths: 1,
        dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
        recurrenceDate: moment(paidDate).add(1, 'months').format("YYYY-MM-DD"),
        expenceCycleType: 2,
        expenseCycle: 1,
        referenceId: null,
      });
      if (moment().date() === 1) {
        await expenseDB.updateRecurringExpenseId({
          id: expenseId,
          recurringExpenseId: recurringExpenseId
        });
      }
    }
    // Create business card
    //const client = await clientDB.getById({ id: clientId });
    let designation = await roleDescription(role);
    let businessName = client?.businessName || "";
    let website = client?.website || "";
    let businessEmail = client?.businessEmail || "";
    let businessAddress = client?.businessAddress || "";
    if (businessName != "" && website != "" && businessEmail != "" && businessAddress != "") {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Business Email [${businessEmail}], Business Address [${businessAddress}], Website [${website}], Business Name [${businessName}], Staff Id [${staffId}], Generating Business Card`
      );
      const businessCard = await createBusinessCard(
        Number(clientId),
        staffId,
        name,
        designation,
        businessName,
        mobile,
        businessEmail,
        website,
        businessAddress,
        true
      );
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Business Card Created [${businessCard.url}], Staff Id [${staffId}], Business card generated successfully`
      );
      const isExists = await staffDocumentDB.getByStaffIdAndType({
        staffId: staffId,
        clientId: clientId,
        type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
      });
      let docId = null;
      if (isExists) {
        docId = isExists.id;
        await staffDocumentDB.updateDoc({
          staffId,
          clientId: clientId,
          type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
          value: businessCard.url,
          id: isExists.id,
        });
      } else {
        docId = await staffDocumentDB.add({
          staffId: staffId,
          clientId: clientId,
          type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
          value: businessCard.url,
        });
      }
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${mobile}], Business Email [${businessEmail}], Business Address [${businessAddress}], Website [${website}], Business Name [${businessName}], Staff Id [${staffId}], Business card generation skipped as business details are not complete`
      );
    }
    let footerText = "The Kipinn Team";
    sendWhatsappStaffWelcome(mobile, name, client.name, propName, footerText, Number(clientId));

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

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

  try {
    let clientId = req.id;
    const userType = req.userType;
    let { filter, s, roleFilters, propFilters, statusFilter = null } = req.query;
    let staffs = [];
    let propList = [];
    let isPartner = false;

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

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

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

      isPartner = staff.role === CONSTANTS.STAFF_ROLES.PARTNER;
      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Filter [${filter}], Search [${s}], Role Filters [${roleFilters}], Property Filters [${propFilters}], Status Filter [${statusFilter}], Staff Requested....`
      );
      if (s && s !== "undefined" && s !== "null" && s !== " ") {
        staffs = await staffDB.getSearchResult({
          clientId,
          status: filter,
          id: staff.id,
          searchVal: s,
        });
      } else {
        // staffs = await staffDB.getAllExcludingRequester({
        //   clientId,
        //   id: staff.id,
        //   status: filter,
        // });
        staffs = await staffDB.getAllByFilterExcludingRequester({
          clientId,
          id: staff.id,
          status: filter,
          roleFilters: roleFilters ? roleFilters : null,
          propFilters: propFilters ? propFilters : null,
        });
      }
      if (staffs && (staff.role === CONSTANTS.STAFF_ROLES.ADMIN || staff.role === CONSTANTS.STAFF_ROLES.BACK_OFFICE)) {
        staffs = staffs.filter((staff: any) =>
          // staff.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
          staff.role !== CONSTANTS.STAFF_ROLES.PARTNER
        );
      }
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Filter [${filter}], Search [${s}], Role Filters [${roleFilters}], Property Filters [${propFilters}], Status Filter [${statusFilter}], 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,
        });
      }
      if (s && s !== "undefined" && s !== "null" && s !== " ") {
        staffs = await staffDB.getSearchResult({
          clientId,
          status: filter,
          id: 0,
          searchVal: s,
        });
      } else {
        // staffs = await staffDB.getAllByClientId({ clientId, status: filter });
        staffs = await staffDB.getAllByClientIdAndFilters({
          clientId,
          status: statusFilter,
          roleFilters: roleFilters ? roleFilters : null,
          propFilters: propFilters ? propFilters : null,
        });
      }
    }

    propList = await propertyDB.getAllActiveByClientId({
      clientId: clientId,
    });

    if (staffs && staffs.length > 0) {
      for (let staff of staffs) {
        const selfi = await staffDocumentDB.getByStaffIdAndType({
          staffId: staff.id,
          clientId: staff.clientId,
          type: CONSTANTS.DOCUMENT_TYPES.SELFI,
        });

        if (selfi) {
          staff.selfi = selfi.value;
        } else {
          staff.selfi = null;
        }

        const summaryData = await staffBalanceDB.getStaffSummaryData({
          staffId: staff.id,
        });
        staff.cash = summaryData?.inhand || 0;

        const leadCount = await leadDB.getSummaryByClientIdForStaff({
          clientId: staff.clientId,
          staffId: staff.id,
        });
        staff.leads = leadCount?.totalLeads || 0;

        const assignedComplaintCount = await complaintDB.getComplaintCountForStaff({
          assignedTo: staff.id,
          status: CONSTANTS.COMPLAINT_STATUS.ASSIGNED,
        });

        const resolvedComplaintCount = await complaintDB.getComplaintCountForStaff({
          assignedTo: staff.id,
          status: CONSTANTS.COMPLAINT_STATUS.RESOLVED,
        });

        staff.complaints = Number(assignedComplaintCount) + Number(resolvedComplaintCount);

        //staff.linkedProps = await getStaffLinkedProperties (staff.id, staff.clientId, Number(userType), isPartner);
        if (!staffSalaryPermission) {
          staff.salary = 0;
        }
      }
    }

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

    return res.status(200).json({
      msg: "Staff list sent successfully",
      data: staffs.length === 0 ? false : staffs,
      propList: propList || [],
      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,
    });
  }
};

staffs.ToggleStaff = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "toggleStaff";

  try {
    const { toggleValue, propId, staffId } = 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}], Client Id [${clientId}], Toggle Value [${toggleValue}], Property Id [${propId}], Staff Id [${staffId}], Requested by Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

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

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

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

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

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

      const isAlreadyLinked = await staffDB.isAlreadyLinked({
        clientId,
        propId,
        staffId,
      });

      if (isAlreadyLinked) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], Staff Id [${staffId}], Staff already linked`
        );
        return res
          .status(400)
          .json({ msg: "Staff already linked", isSuccess: false });
      }

      await staffDB.link({ clientId, propId, staffId });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], Staff Id [${staffId}], Staff linked Successfully`
      );
    } else {
      const isPendingComplaints = await complaintDB.getAssignedByStaffId({
        clientId,
        propId,
        assignedTo: staffId,
      });

      if (isPendingComplaints) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], Staff Id [${staffId}], Staff has some unresolved complaints for this property.`
        );
        return res.status(400).json({
          msg: "Staff has some unresolved complaints for this property.",
          isSuccess: false,
        });
      }

      await staffDB.unlink({ clientId, propId, staffId });
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], Staff Id [${staffId}], Staff unlinked Successfully`
      );
    }

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

staffs.ToggleStaffMultiple = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "ToggleStaffMultiple";

  try {
    let { toggleValue, propIds, staffId, properties } = req.body;

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

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

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

      clientId = staff.clientId;

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

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

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

    // Unlink Properties
    for (const propertyId of properties) {
      try {
        const isPendingComplaints = await complaintDB.getAssignedByStaffId({
          clientId,
          propId: propertyId,
          assignedTo: staffId,
        });

        if (isPendingComplaints) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Property Id [${propertyId}],  Toggle Value [${toggleValue}], Property Id [${propertyId}], Staff Id [${staffId}], Staff has some unresolved complaints for this property.`
          );
          return res.status(400).json({
            msg: "Staff has some unresolved complaints for this property.",
            isSuccess: false,
          });
        }

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

    let propName = '';
    for (const propertyId of properties) {
      try {
        propName = propName == "" ? "" : propName + ", ";
        const property = await propertyDB.getById({ id: propertyId });
        if (!property) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}],  Property Id [${propertyId}], No Property Found`
          );
          continue;
        }
        propName = propName + property.name;
        await staffDB.link({ staffId, propId: propertyId, clientId });
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Property Id [${propertyId}], Staff linked successfully`
        );
      } catch (error: any) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Property Id [${propertyId}], Error [${error?.message || error
          }]`
        );
      }
    }

    // const isPendingComplaints = await complaintDB.getAssignedByStaffId({
    //         clientId,
    //         propId,
    //         assignedTo: staffId,
    //       });

    //       if (isPendingComplaints) {
    //         log.info(
    //           `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], Staff Id [${staffId}], Staff has some unresolved complaints for this property.`
    //         );
    //         return res.status(400).json({
    //           msg: "Staff has some unresolved complaints for this property.",
    //           isSuccess: false,
    //         });
    //       }

    // if (propIds && propIds.length > 0) {
    //   for (let propId of propIds) {

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

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

    //       const isAlreadyLinked = await staffDB.isAlreadyLinked({
    //         clientId,
    //         propId,
    //         staffId,
    //       });

    //       if (isAlreadyLinked) {
    //         log.info(
    //           `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], Staff Id [${staffId}], Staff already linked`
    //         );
    //         return res
    //           .status(400)
    //           .json({ msg: "Staff already linked", isSuccess: false });
    //       }

    //       await staffDB.link({ clientId, propId, staffId });

    //       log.info(
    //         `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], Staff Id [${staffId}], Staff linked Successfully`
    //       );
    //     } else {
    //       const isPendingComplaints = await complaintDB.getAssignedByStaffId({
    //         clientId,
    //         propId,
    //         assignedTo: staffId,
    //       });

    //       if (isPendingComplaints) {
    //         log.info(
    //           `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], Staff Id [${staffId}], Staff has some unresolved complaints for this property.`
    //         );
    //         return res.status(400).json({
    //           msg: "Staff has some unresolved complaints for this property.",
    //           isSuccess: false,
    //         });
    //       }

    //       await staffDB.unlink({ clientId, propId, staffId });
    //       log.info(
    //         `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}],  Toggle Value [${toggleValue}], Property Id [${propId}], Staff Id [${staffId}], Staff unlinked Successfully`
    //       );
    //     }
    //   }
    // }


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

staffs.LinkedProperties = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "LinkedProperties";

  try {
    const { staffId } = req.params;

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

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

      isPartner = staff.role === CONSTANTS.STAFF_ROLES.PARTNER;

      clientId = staff.clientId;

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

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

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

    let iterableProperties = null;

    let linkedProperties = await staffDB.getLinkedProperties({
      staffId,
      clientId,
    });
    const allProperties = await propertyDB.getActivePropIdsByClientId({
      clientId,
      status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
    });

    iterableProperties = allProperties || [];

    if (userType === CONSTANTS.USER_TYPE.STAFF && !isPartner) {
      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId: req.id,
      });

      iterableProperties = staffLinkedProps || [];
    }

    const mergedProps = [
      ...(iterableProperties || []),
      ...(linkedProperties || []),
    ];

    const uniqueProps = Array.from(
      new Map(mergedProps.map((prop: propertiesTypes) => [prop.id, prop])).values()
    );

    const combinedPropList = uniqueProps.map(
      (property: propertiesTypes) => {
        let isExists = false;
        if (linkedProperties) {
          isExists = linkedProperties.find(
            (prop: propertiesTypes) => prop.id === property.id
          );
        }
        return {
          id: property.id,
          name: property.name,
          isLinked: isExists ? 1 : 0,
        };
      }
    );

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

    return res.status(200).json({
      msg: `Linked Properties sent successfully`,
      data: combinedPropList,
      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,
    });
  }
};

staffs.Edit = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "Edit";

  try {
    const { staffId, name, role, salary, gender, commission, commissionType, isOnPayroll = 0, isSuperAdmin = 0, } = req.body;

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

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Staff Id [${staffId}], Name [${name}], Role [${role}], Salary [${salary}], Gender [${gender}], Commission [${commission}], Commission Type [${commissionType}], isOnPayroll [${isOnPayroll}], isSuperAdmin [${isSuperAdmin}], Requested by Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Name [${name}], Role [${role}], Salary [${salary}], Gender [${gender}], Commission [${commission}], Commission Type [${commissionType}], Requested by Staff Id [${req.id}], isOnPayroll [${isOnPayroll}], isSuperAdmin [${isSuperAdmin}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Name [${name}], Role [${role}], Salary [${salary}], Gender [${gender}], Commission [${commission}], Commission Type [${commissionType}], isOnPayroll [${isOnPayroll}], isSuperAdmin [${isSuperAdmin}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Name [${name}], Role [${role}], Salary [${salary}], Gender [${gender}], No Client Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

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

    if (!staff) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Name [${name}], Role [${role}], Salary [${salary}], Gender [${gender}], No Staff Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    if (
      staff.role === CONSTANTS.STAFF_ROLES.PARTNER &&
      userType === CONSTANTS.USER_TYPE.STAFF
    ) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Name [${name}], Role [${role}], Salary [${salary}], Gender [${gender}], Staff is Partner, Only Client can edit`
      );
      return res.status(400).json({
        msg: `Staff is Partner, Only Client can edit`,
        isSuccess: false,
      });
    }

    await staffDB.edit({
      id: staffId,
      clientId,
      name,
      role,
      salary: !staffSalaryPermission ? staff.salary : Number(salary),
      gender,
      commission: Number(commission) ? Number(commission) : staff.commission,
      commissionType: Number(commissionType) === 0 || Number(commissionType) === 1 ? Number(commissionType) : staff.commissionType,
      isOnPayroll: isOnPayroll,
      isSuperAdmin: (isSuperAdmin === 0 || isSuperAdmin === 1) ? Number(isSuperAdmin) : staff.isSuperAdmin,
    });

    if (Number(isSuperAdmin) === 1) {
      const propertyList = await propertyDB.getAllByClientId({
        clientId: clientId,
      });

      let properties = propertyList.map((item: any) => item.id);
      
      if (properties && properties.length > 0) {
        for (let propertyId of properties) {
          let alreadyExist = await staffDB.isAlreadyLinked({staffId, propId: propertyId, clientId});
          if(!alreadyExist) {
            await staffDB.link({ staffId, propId: propertyId, clientId });
          }
        }
      }
    }


    if (Number(staff.salary) === 0 && Number(salary) !== 0 && staffSalaryPermission) {
      let paidDate = moment().date(1).format("YYYY-MM-DD");
      // let dueDate = moment().add(1, "month").date(1).format("YYYY-MM-DD");
      let dueDate = moment().endOf("month").format("YYYY-MM-DD");
      let expenseId = null;

      if (moment().date() !== 1) {
        paidDate = moment().add(1, "month").date(1).format("YYYY-MM-DD");
      } else {

        // const isSalaryAdded = await expenseDB.isStaffSalaryAddedForToday ({
        //   paidTo: staffId,
        // });

        // if (!isSalaryAdded) {
        //   expenseId = await expenseDB.create({
        //     type: 14,
        //     amount: salary,
        //     clientId,
        //     paidDate: paidDate,
        //     paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        //     paidBy: clientId,
        //     paidTo: staffId,
        //     paidToUserType: CONSTANTS.USER_TYPE.STAFF,
        //     description: "Staff salary",
        //     paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
        //     repetitionType: 2,
        //     noOfMonths: 1,
        //     dueDate: dueDate,
        //     isPaid: 0,
        //   });
        // }
      }

      const recurringExpenseId = await recurringExpenseDB.create({
        type: 14,
        amount: salary,
        clientId,
        propId: null,
        paidDate: paidDate,
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: staffId,
        paidToUserType: CONSTANTS.USER_TYPE.STAFF,
        description: "Staff salary",
        paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
        noOfMonths: 1,
        dueDate: dueDate,
        expenseCycle: 1,
      });
      // if (moment().date() === 1) {
      //   await expenseDB.updateRecurringExpenseId({
      //     id: expenseId,
      //     recurringExpenseId: recurringExpenseId
      //   });
      // }
    } else if (Number(staff.salary) !== 0 && staffSalaryPermission) {
      if (Number(salary) === 0) {
        await recurringExpenseDB.deleteSalaryExpense({
          staffId: Number(staffId),
          clientId: Number(clientId),
        });
      // } else if (Number(salary) !== Number(staff.salary)) {
      } else {

        const salrayRecurringExists = await recurringExpenseDB.getStaffSalaryExpense({
          staffId: Number(staffId),
          clientId: Number(clientId),
        });

        if (!salrayRecurringExists) {
          const recurringExpenseId = await recurringExpenseDB.create({
            type: 14,
            amount: salary,
            clientId,
            propId: null,
            paidDate: moment().date(1).format("YYYY-MM-DD"),
            paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
            paidBy: clientId,
            paidTo: staffId,
            paidToUserType: CONSTANTS.USER_TYPE.STAFF,
            description: "Staff salary",
            paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
            noOfMonths: 1,
            dueDate: moment().add(1, "month").date(1).format("YYYY-MM-DD"),
            expenseCycle: 1,
          });
        } else {
          await recurringExpenseDB.updateSalaryExpense({
            staffId: Number(staffId),
            clientId: Number(clientId),
            amount: Number(salary),
          });
        }

      }
    }


    let designation = await roleDescription(role);
    let businessName = client?.businessName || "";
    let website = client?.website || "";
    let businessEmail = client?.businessEmail || "";
    let businessAddress = client?.businessAddress || "";
    if (businessName != "" && website != "" && businessEmail != "" && businessAddress != "") {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${staff.mobile}], Business Email [${businessEmail}], Business Address [${businessAddress}], Website [${website}], Business Name [${businessName}], Staff Id [${staffId}], Generating Business Card`
      );
      const businessCard = await createBusinessCard(
        Number(clientId),
        staffId,
        name,
        designation,
        businessName,
        staff.mobile,
        businessEmail,
        website,
        businessAddress,
        true
      );
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${staff.mobile}], Business Card Created [${businessCard.url}], Staff Id [${staffId}], Business card generated successfully`
      );
      const isExists = await staffDocumentDB.getByStaffIdAndType({
        staffId: staffId,
        clientId: clientId,
        type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
      });
      let docId = null;
      if (isExists) {
        docId = isExists.id;
        await staffDocumentDB.updateDoc({
          staffId,
          clientId: clientId,
          type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
          value: businessCard.url,
          id: isExists.id,
        });
      } else {
        docId = await staffDocumentDB.add({
          staffId: staffId,
          clientId: clientId,
          type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
          value: businessCard.url,
        });
      }
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Role [${role}], Mobile [${staff.mobile}], Business Email [${businessEmail}], Business Address [${businessAddress}], Website [${website}], Business Name [${businessName}], Staff Id [${staffId}], Business card generation skipped as business details are not complete`
      );
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Name [${name}], Role [${role}], Salary [${salary}], Gender [${gender}], Staff has been updated successfully`
    );

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

staffs.ToggleStatus = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "ToggleStatus";

  try {
    const { staffId } = req.body;

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

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

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

      clientId = staff.clientId;

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

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Only Admin and Back Office Allowed`);
        return res.status(400).json({
          msg: "You are not allowed to change the status of staff.",
          isSuccess: false,
        });
      }

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

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

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

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

    if (!staff) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], No Staff Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }
    // let isActiveWithOtherClient = false;
    // if (staff.status === CONSTANTS.STAFF_STATUS.INACTIVE) {
    //   isActiveWithOtherClient = await staffDB.validateIfActiveWithOtherClient({ mobile: staffMobile, clientId });
    // }

    let isDuplicate = false;

    const duplicateStaffCheck = await staffDB.getByMobileAndClientIdAndStatus({
      clientId: clientId,
      mobile: staffMobile,
      status: CONSTANTS.STAFF_STATUS.ACTIVE
    });

    log.info(`Duplicate Staff Check [${JSON.stringify(duplicateStaffCheck)}]`)

    if (duplicateStaffCheck && duplicateStaffCheck?.length > 0 && staff.status === CONSTANTS.STAFF_STATUS.INACTIVE) {
      isDuplicate = true;
    }

    if (false == isDuplicate) {
      await staffDB.updateStatus({
        id: staffId,
        clientId,
        status:
          staff.status === CONSTANTS.STAFF_STATUS.ACTIVE
            ? CONSTANTS.STAFF_STATUS.INACTIVE
            : CONSTANTS.STAFF_STATUS.ACTIVE,
      });
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Staff already active with other client`
      );
      return res.status(400).json({
        msg: "Staff with same mobile number is already active",
        isSuccess: true,
      });
    }

    if (staff.status === CONSTANTS.STAFF_STATUS.ACTIVE) {
      await recurringExpenseDB.deleteSalaryExpense({
        staffId: Number(staffId),
        clientId: Number(clientId),
      });
    }
    //else if (staff.status === CONSTANTS.STAFF_STATUS.INACTIVE && Number(staff.salary) > 0) {
    //   let paidDate = moment().date(1).format("YYYY-MM-DD");
    //   let dueDate = moment().add(1, "month").date(1).format("YYYY-MM-DD");
    //   let expenseId = null;

    //   if (moment().date() !== 1) {
    //     paidDate = moment().add(1, "month").date(1).format("YYYY-MM-DD");
    //   } else {
    //     const isSalaryAdded = await expenseDB.isStaffSalaryAddedForToday ({
    //       paidTo: staffId,
    //     });

    //     if (!isSalaryAdded) {
    //       expenseId = await expenseDB.create({
    //         type: 14,
    //         amount: staff.salary,
    //         clientId,
    //         paidDate: paidDate,
    //         paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
    //         paidBy: clientId,
    //         paidTo: staffId,
    //         paidToUserType: CONSTANTS.USER_TYPE.STAFF,
    //         description: "Staff salary",
    //         paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
    //         repetitionType: 2,
    //         noOfMonths: 1,
    //         dueDate: dueDate,
    //         isPaid: 0,
    //       });
    //     }
    //   }

    //   const recurringExpenseId = await recurringExpenseDB.create({
    //     type: 14,
    //     amount:staff.salary,
    //     clientId,
    //     propId: null,
    //     paidDate: paidDate,
    //     paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
    //     paidBy: clientId,
    //     paidTo: staffId,
    //     paidToUserType: CONSTANTS.USER_TYPE.STAFF,
    //     description: "Staff salary",
    //     paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
    //     noOfMonths: 1,
    //     dueDate: dueDate,
    //     expenseCycle: 1,
    //   });
    //   if (moment().date() === 1) {
    //     await expenseDB.updateRecurringExpenseId({
    //       id: expenseId,
    //       recurringExpenseId: recurringExpenseId
    //     });
    //   }
    // }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Staff has been updated successfully`
    );

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

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

  try {
    const staffId = req.id;
    const { regId } = req.body;

    log.info(`[${C}], [${F}], Staff Id [${staffId}], Reg Id [${regId}]`);

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

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

    await staffDB.updateRegId({ id: staffId, regId });

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

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

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

staffs.Summary = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "Summary";

  try {
    let clientId = req.id;
    const userType = req.userType;
    const { staffId } = req.params;
    let { y, m } = req.query;
    let year = y;
    let month = m;
    if (!year || !month || "undefined" == month) {
      year = moment().format("YYYY");
      month = moment().format("MM");
    }
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Year [${year}], Month [${month}]`
    );

    let complaintCounts = {
      new: 0,
      assigned: 0,
      closed: 0,
    };

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

    let leadSummary = [];

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

      clientId = staff.clientId;

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

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

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

    const selfi = await staffDocumentDB.getByStaffIdAndType({
      staffId: staff.id,
      clientId: staff.clientId,
      type: CONSTANTS.DOCUMENT_TYPES.SELFI,
    });

    if (selfi) {
      staff.selfi = selfi.value;
    } else {
      staff.selfi = null;
    }

    //const recentTrans = await staffLedgerDB.getRecentTransactions({ staffId });
    const summaryData = await staffBalanceDB.getStaffSummaryData({
      staffId,
    });

    complaintCounts = await complaintStaffCountsForYearMonth(
      Number(clientId),
      Number(staffId),
      year,
      month
    );

    // if (staff.role === CONSTANTS.STAFF_ROLES.SALESPERSON) {
    //   const staffLinkedProps = await propertyDB.getPropsByStaffId({
    //     staffId: staff.id,
    //   });

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

    //     leadSummary = await leadDB.getSummaryByClientIdForStaff({ clientId, propertiesIds, staffId });
    //   }
    // }

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

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

      leadSummary = await leadDB.getSummaryByClientIdForStaff({ clientId, propertiesIds, staffId });
    }
    let leadSummaryX = await leadActivityLogsDB.getLeadStatsForStaffByYearMonth({
      staffId,
      month: month,
      year: year,
    });

    //log.info(` Lead Summary New [${JSON.stringify(leadSummaryX)}]`);

    let summary = {
      collection: summaryData.collection || 0,
      inhand: summaryData.inhand || 0,
      expenses: summaryData.expenses || 0,
      reibursements: summaryData.reimbursements || 0,
      contacted: Number(leadSummaryX.contacted) || 0,
      visitDone: Number(leadSummaryX.visitDone) || 0,
      booked: Number(leadSummaryX.booked) || 0,
      movedIn:Number(leadSummaryX.movedIn) || 0,
    };

    let bookingCount = await occupancyDB.getTenantBookedCountByClientIdAndStaffId ({ clientId, bookedBy: staffId, month, year});
    summary.booked = Number(summary.booked) + Number(bookingCount)
    // const staffDocs = await staffDocumentDB.getByStaffId({
    //   staffId: staffId,
    //   clientId: clientId,
    // });

    const isExists = await staffDocumentDB.getByStaffIdAndType({
      staffId: staffId,
      clientId: staff.clientId,
      type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
    });
    if (isExists) {
      staff.businessCard = isExists.value;
    } else {
      staff.businessCard = null;
    }

    const docs = await staffDocumentDB.getByStaffId({ staffId, clientId});
    if (docs && docs.length > 0) {
      for (let doc of docs) {
        const title = await getStaffDocumentTitle(doc.type);
        doc.title = title;
      }
    }
    
    if (!staffSalaryPermission) {
      staff.salary = 0;
    }

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Collection [${summary.collection}], Inhand [${summary.inhand}], Expenses [${summary.expenses}], Staff Summary Sent`
    );

    return res.status(200).json({
      msg: "Staff Summary Sent",
      isSuccess: true,
      data: {
        summary: summary,
        complaints: complaintCounts,
        leadSummary: leadSummary || [],
        // staffDocs: staffDocs || [],
        ...staff,
        //recentTrans: recentTrans || [],
        canDoStaffEkyc: userType === CONSTANTS.USER_TYPE.CLIENT ? client.canDoStaffEkyc : 0,
        docs: docs || [],
      },
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

// TenantPaymentToOwner | ReturnTenantPaymentToOwner | ReturnCollectedAmountToOwner
// This function will be called when Staff is giving cash to owner collected from tenant.
staffs.PayToClient = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "PayToClient";
  try {
    let { staffId, amount, collectionDate, mode, paymentId } = req.body;

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

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

      givenToName = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Amount [${amount}], Collection Date [${collectionDate}], Mode [${mode}], Payment Id [${paymentId}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Amount [${amount}], Collection Date [${collectionDate}], Mode [${mode}], Payment Id [${paymentId}], Client Requested....`
      );
    }

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

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

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

    await staffLedgerDB.add({
      staffId: staffId,
      amount: amount,
      type: CONSTANTS.STAFF_LEDGER_TYPES.PAY_OUT,
      mode: mode,
      // description: `Client Payment from Staff ${staff.name} to Owner ${client.name
      //   } on ${moment(collectionDate).format("YYYY-MM-DD HH:mm:ss")}`,
      description: `Payment of ₹${amount} collected by ${userType === CONSTANTS.USER_TYPE.CLIENT ? "Owner" : "Staff"} ${givenToName} from Staff ${staff.name} on ${moment(collectionDate).format("MMM DD, YYYY")}`,
      transactionId: paymentId || 0,
      paidDate: moment(collectionDate).format("YYYY-MM-DD"),
    });

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

    if (!staffExistsInBalance) {
      await staffBalanceDB.add({
        staffId: staffId,
        totalCollected: 0,
        totalGivenToOwner: amount,
        totalExpense: 0,
      });
    } else {
      await staffBalanceDB.updateTotalGivenToOwner({
        staffId: staffId,
        amount: amount,
      });
    }
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Amount [${amount}], Collection Date [${collectionDate}], Mode [${mode}], Payment Id [${paymentId}], Amount has been paid to owner successfully....`
    );

    return res.status(200).json({
      msg: "Amount recorded successfully",
      paymentId: paymentId || "",
      amount,
      mode,
      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,
    });
  }
};

staffs.ListStaffLedger = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "ListStaffLedger";

  try {
    const { pageNum, s } = req.query;
    const { staffId } = req.params;
    let clientId = req.id;
    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Page Number [${pageNum}], Search Val [${s}] User Type [${userType}]`
    );

    let staffLedger = [];
    let limit = 10;

    if (req.platform === CONSTANTS.TENANT_DEVICE_TYPE.WEB) {
      limit = 10e9;
    }

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

    if (!staffSalaryPermission) {
      log.info(`[${C}], [${F}], User Id [${req.id}], UserType [${userType}], Unauthorized Access`);

      return res.status(400).json({
        message: "Unauthorized access",
        isSuccess: false,
      });
    }

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Search Val [${s}], Staff Id [${staffId}], Staff Requested.....`
      );

      clientId = staff.clientId;
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Search Val [${s}], Staff Id [${staffId}], Client Requested.....`
      );
    }

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

    if (s && s !== "undefined" && s !== "null" && s !== " ") {
      const { staffLedgerType } = getMatchedStaffLedgerType(s.toString());
      staffLedger = await staffLedgerDB.getSearchResultByStaffId({
        staffId,
        pageNum: Number(pageNum),
        limit,
        searchVal: s,
        staffLedgerType,
      });
    } else {
      staffLedger = await staffLedgerDB.getByStaffId({
        staffId,
        pageNum: Number(pageNum),
        limit,
      });
    }

    const { totalCollection, totalExpense, totalGivenToClient, totalReimbursed } =
      await staffLedgerDB.getTotalByStaffId({
        staffId,
      });

    // const staffLedgerBalance = await staffLedgerDB.getTotalBalanceByStaffId({
    //   staffId,
    // });

    const totalBalance = (Math.abs(totalExpense) + Math.abs(totalGivenToClient)) - (Math.abs(totalCollection) + Math.abs(totalReimbursed));
    // const totalBalance = (Math.abs(totalCollection) - Math.abs(totalGivenToClient)) + Number(staffLedgerBalance);

    //log.info(`Total Collection [${totalCollection}], Total Expense [${totalExpense}], Amount Given To Client [${totalGivenToClient}], Total Reimbursed [${totalReimbursed}], Total Balance [${totalBalance}], Staff Ledger Balance [${staffLedgerBalance}]`);

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Search Val [${s}], Staff Id [${staffId}], Total Collection [${totalCollection}], Total Expense [${totalExpense}], Amount Given To Client [${totalGivenToClient}], Staff Ledger sent successfully.`
    );

    return res.status(200).json({
      msg: "Staff Ledger sent successfully.",
      data: {
        staffLedger: staffLedger || [],
        totalCollection: totalCollection || 0,
        totalExpense: totalExpense || 0,
        totalGivenToClient: totalGivenToClient || 0,
        totalReimbursed: totalReimbursed || 0,
        totalBalance: totalBalance || 0,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

staffs.ListStaffSalaryLedger = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "ListStaffSalaryLedger";

  try {
    const { pageNum, s } = req.query;
    const { staffId } = req.params;
    let clientId = req.id;
    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Page Number [${pageNum}], Search Val [${s}] User Type [${userType}]`
    );

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

    if (!staffSalaryPermission) {
      log.info(`[${C}], [${F}], User Id [${req.id}], UserType [${userType}], Unauthorized Access`);

      return res.status(400).json({
        message: "Unauthorized access",
        isSuccess: false,
      });
    }

    let staffLedger = [];
    const limit = 10;

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Search Val [${s}], Staff Id [${staffId}], Staff Requested.....`
      );

      clientId = staff.clientId;
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Search Val [${s}], Staff Id [${staffId}], Client Requested.....`
      );
    }

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

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

    if (s && s !== "undefined" && s !== "null" && s !== " ") {
      const { staffLedgerType } = getMatchedStaffLedgerType(s.toString());
      staffLedger = await staffLedgerDB.getSearchResultByStaffId({
        staffId,
        pageNum: Number(pageNum),
        limit,
        searchVal: s,
        staffLedgerType,
      });
    } else {
      staffLedger = await staffLedgerDB.getByStaffIdSalary({
        staffId,
        pageNum: Number(pageNum),
        limit,
      });
    }

    const totalPaid = await staffLedgerDB.getTotalSalaryPaid({
      staffId,
    });

    const curMonthSalaryPaid = await staffLedgerDB.getCurrentMonthSalaryPaid({
      staffId,
    });

    const curMonthReimbursmentPaid = await staffLedgerDB.getCurrentMonthReimbursmentPaid({
      staffId,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Search Val [${s}], Staff Id [${staffId}], Staff Salray Ledger sent successfully.`
    );

    return res.status(200).json({
      msg: "Staff Ledger sent successfully.",
      data: {
        staffLedger: staffLedger || [],
        totalPaid: totalPaid || 0,
        curMonthSalaryPaid: curMonthSalaryPaid || 0,
        curMonthReimbursmentPaid: curMonthReimbursmentPaid || 0,
        staffSalary: staff.salary || 0,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

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

  try {
    let clientId = req.id;
    const userType = req.userType;
    let { filter, filterVal, s, t, date } = req.query;
    let staffs = [];

    if (!date) date = moment().format("YYYY-MM-DD");

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

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Filter [${filter}], Filter Val [${filterVal}], Search Val [${s}], Search Type [${t}], Date [${date}], Staff Requested.....`
      );
      if (filter === "role") {
        staffs = await staffDB.getByClientIdAndRole({
          clientId: clientId,
          role: Number(filterVal),
        });
      } else if (filter === "status") {
        staffs = await staffDB.getAllByClientId({
          clientId: clientId,
          status: Number(filterVal),
        });
      } else if (filter === "name") {
        staffs = await staffDB.getSearchResultByName({
          clientId: clientId,
          name: filterVal,
        });
      } else if (s && s != "" && s !== "undefined") {
        staffs = await staffDB.getByClientIdAndSearchVal({
          clientId,
          searchVal: s,
          searchType: t,
        })
      } else if (filter === "SP") {//Salary Pending
        staffs = await staffDB.getCurMonthPendingSalary({
          clientId,
        });
      } else {
        staffs = await staffDB.getAllByClientIdExcludingRequester({
          clientId,
          id: staff.id,
        });
      }
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Filter [${filter}], Filter Val [${filterVal}],  Search Val [${s}], Search Type [${t}], Date [${date}], 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,
        });
      }
      if (filter === "role") {
        staffs = await staffDB.getByClientIdAndRole({
          clientId: clientId,
          role: Number(filterVal),
        });
      } else if (filter === "status") {
        staffs = await staffDB.getAllByClientId({
          clientId: clientId,
          status: Number(filterVal),
        });
      } else if (filter === "name") {
        staffs = await staffDB.getSearchResultByName({
          clientId: clientId,
          name: filterVal,
        });
      } else if (filter === "attendanceStatus") {
        staffs = await staffDB.getByDateAndAttendance({
          clientId: clientId,
          date: date,
          attendance: Number(filterVal),
        });
      } else if (s && s != "" && s !== "undefined") {
        staffs = await staffDB.getByClientIdAndSearchVal({
          clientId,
          searchVal: s,
          searchType: t,
        })
      } else if (filter === "SP") {//Salary Pending
        staffs = await staffDB.getCurMonthPendingSalary({
          clientId,
        });
      } else {
        staffs = await staffDB.getByClientId({ clientId });
      }
    }

    const staffSummary = await staffDB.getSummaryForClient({clientId});

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

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

    let present = 0;
    let absent = 0;
    let attendanceFlag = 0;
    let staffSalaryBudget = 0;

    if (staffs.length > 0) {
      staffs = staffs.map((staff: { id: number; salary: any; status: any }) => {
        let attendanceRecord = attendanceMap[staff.id];

        let staffAttendance = attendanceRecord
          ? attendanceRecord.attendance
          : 0;

        if (Number(staff.status) === CONSTANTS.STAFF_STATUS.ACTIVE) {
          if (staffAttendance === 1) {
            present += 1;
          } else {
            absent += 1;
          }
        }

        staffSalaryBudget += Number(staff.salary) || 0;

        if (!staffSalaryPermission) {
          staffSalaryBudget = 0;
          staff.salary = 0;
        }
        return {
          ...staff,
          userType:  CONSTANTS.USER_TYPE.STAFF,
          attendance: staffAttendance,
          propId: attendanceRecord ? attendanceRecord.propId : null,
          propName: attendanceRecord ? attendanceRecord.propName : null,
          attendanceDate: date,
          attendanceTime: attendanceRecord
            ? attendanceRecord.attendanceTime
            : "",
        };
      });

      attendanceFlag = staffs.some(
        (staff: any) =>
          (staff.permissions &
            (1 << CONSTANTS.PERMISSIONS.MANANGE_ATTENDANCES)) !==
          0
      )
        ? 1
        : 0;
    }

    const salaryPaid = await expenseDB.getTotalByClientIdAndDateRangeAndType({
      clientId,
      startDate: moment().startOf("month").format("YYYY-MM-DD"),
      endDate: moment().endOf("month").format("YYYY-MM-DD"),
      type: 14 //salary
    })

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

    return res.status(200).json({
      msg: "Staff list sent successfully",
      data: staffs || [],
      attendanceFlag,
      // staffAttendances: mergedStaffAttendance,
      summary: {
        total: staffs?.length || 0,
        active: staffSummary?.active || 0,
        inactive: staffSummary?.inactive || 0,
        present: present,
        absent: absent,
        salaryBudget: Number(staffSalaryBudget) || 0,
        pendingSalary: Number(staffSalaryBudget) - Number(salaryPaid) > 0 ? Number(staffSalaryBudget) - Number(salaryPaid) : 0,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

staffs.MarkAttendance = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "MarkAttendance";

  try {
    const {
      propId = null,
      attendance,
      checkIn = moment().format("YYYY-MM-DD HH:mm:ss"),
      checkOut = null,
      longitude = null,
      latitude = null,
    } = req.body;

    const staffId = req.id;

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Prop Id [${propId}], Attendance [${attendance}], Check In [${checkIn}], Check Out [${checkOut}], Longitude [${longitude}], Latitude [${latitude}]`
    );

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

    if (checkOut) {
      await staffAttendanceDB.checkOut({
        clientId: staff.clientId,
        propId: propId,
        staffId: staffId,
        checkOut: moment(checkOut).format("YYYY-MM-DD HH:mm:ss"),
        checkOutLongitude: longitude,
        checkOutLatitude: latitude,
      });
    } else {
      const isCheckOutPending = await staffAttendanceDB.isCheckOutPending({
        clientId: staff.clientId,
        staffId,
        date: moment(checkIn).format("YYYY-MM-DD"),
      });
      if (isCheckOutPending) {
        log.info(
          `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${staffId}], Prop Id [${propId}], Attendance [${attendance}], Check In [${checkIn}], Check Out [${checkOut}], Check Out Pending`
        );

        return res.status(400).json({
          msg: "Please check out form previous property beofre checking into another.",
          isSuccess: true,
        });
      }
      await staffAttendanceDB.add({
        clientId: staff.clientId,
        staffId: staffId,
        propId: propId,
        attendance: attendance,
        checkIn: moment(checkIn).format("YYYY-MM-DD HH:mm:ss"),
        checkInLongitude: longitude,
        checkInLatitude: latitude,
      });
    }


    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Property Id [${propId}], Attendance [${attendance}], Check In [${checkIn}], Check Out [${checkOut}], ${checkOut ? "Checked Out" : "Check In"} successfully`
    );
    return res.status(200).json({
      msg: `${checkOut ? "Checked Out" : "Check In"} 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,
    });
  }
};

staffs.MarkAttendanceManually = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "MarkAttendanceManually";

  try {
    let {
      staffId,
      attendance,
      checkIn = moment().format("YYYY-MM-DD HH:mm:ss"),
      checkOut = null,
    } = req.body;

    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Attendance [${attendance}], Check In [${checkIn}], Check Out [${checkOut}]`
    );
    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

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

      if (!(staff.permissions & CONSTANTS.PERMISSIONS.MARK_STAFF_ATTENDANCE)) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Permissions [${staff.permissions}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

    // const isTodayAttendanceMarked = await staffAttendanceDB.getByClientIdAndStaffIdAndDate({
    //   clientId: clientId,
    //   staffId,
    //   date: moment(checkIn).format("YYYY-MM-DD")
    // });
    // if (isTodayAttendanceMarked) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Attendance [${attendance}], Check In [${checkIn}], Attendance Already Marked For Staff`
    //   );

    //   return res.status(400).json({
    //     msg: "Todays attendance is already marked for this staff.",
    //     isSuccess: true,
    //   });
    // }
    // await staffAttendanceDB.add({
    //   clientId: clientId,
    //   staffId: staffId,
    //   propId: null,
    //   attendance: attendance,
    //   checkIn: moment(checkIn).format("YYYY-MM-DD HH:mm:ss"),
    //   checkInLongitude: null,
    //   checkInLatitude: null,
    // });

    if (checkOut) {
      // checkOut = `${checkOut} ${moment().format("HH:mm:ss")}`;
      await staffAttendanceDB.checkOutManually({
        clientId: clientId,
        staffId: staffId,
        checkOut: moment(checkOut).format("YYYY-MM-DD HH:mm:ss"),
        date: moment(checkOut).format("YYYY-MM-DD"),
        checkOutLongitude: null,
        checkOutLatitude: null,
      });
      log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Attendance [${attendance}], Check In [${checkIn}], Check Out [${checkOut}], Check Out Success`
        );
    } else {
      // if(checkIn) {
      //   checkIn = `${checkIn} ${moment().format("HH:mm:ss")}`;
      // }
      const isCheckOutPending = await staffAttendanceDB.isCheckOutPending({
        clientId: clientId,
        staffId,
        date: moment(checkIn).format("YYYY-MM-DD"),
      });
      if (isCheckOutPending) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Attendance [${attendance}], Check In [${checkIn}], Check Out [${checkOut}], Check Out Pending`
        );

        return res.status(400).json({
          msg: "Please check out form previous property beofre checking into another.",
          isSuccess: true,
        });
      }
      await staffAttendanceDB.add({
        clientId: clientId,
        staffId: staffId,
        propId: null,
        attendance: attendance,
        checkIn: moment(checkIn).format("YYYY-MM-DD HH:mm:ss"),
        checkInLongitude: null,
        checkInLatitude: null,
      });
    }

    let activity = CONSTANTS.ACTIVITY_TYPES.STAFF_CHECKIN_MANUAL;
    if (checkOut) activity = CONSTANTS.ACTIVITY_TYPES.STAFF_CHECKOUT_MANUAL;
    if (Number(attendance) === 0) activity = CONSTANTS.ACTIVITY_TYPES.STAFF_MARK_ABSENT_MANUAL;

    await logStaffAttendanceManual(
      Number(req.userType),
      Number(req.id),
      Number(req.parentClientId),
      Number(req.platform),
      activity,
      Number(staffId),
      moment(checkIn).format("YYYY-MM-DD"),
      Number(attendance),
    )


    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Attendance [${attendance}], Attendance Marked Successfully`
    );
    return res.status(200).json({
      msg: `Attendance marked 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,
    });
  }
};

staffs.ListAttendance = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "ListAttendance";

  try {
    const staffId = req.id;

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], List Attendance Requested....`
    );

    let present = 0;
    let absent = 0;

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

    // let dateArray = [];
    // let month = moment().month() + 1;
    // let year = moment().year();
    // let today = moment().format("YYYY-MM-DD");
    // let startDate = moment(`${year}-${month}-01`).format("YYYY-MM-DD");

    // while (startDate <= today) {
    //   dateArray.push(today);
    //   today = moment(today).subtract(1, "days").format("YYYY-MM-DD");
    // }

    // const attendanceMap = staffAttendances.reduce((acc: any, cur: any) => {
    //   acc[cur.attendanceDate] = cur;
    //   return acc;
    // }, {});

    // staffAttendances = dateArray.map((date) => ({
    //   sno: dateArray.indexOf(date) + 1,
    //   id: attendanceMap[date]?.id || null,
    //   staffId: staff.id,
    //   staffName: staff.name,
    //   clientId: staff.clientId,
    //   propId: attendanceMap[date]?.propId || null,
    //   attendance: attendanceMap[date]?.attendance || 0,
    //   attendanceDate: date,
    //   attendanceTime: attendanceMap[date]
    //     ? attendanceMap[date]?.attendanceTime
    //     : "",
    //   checkIn: attendanceMap[date]?.checkIn || null,
    //   checkOut: attendanceMap[date]?.checkOut || null,
    // }));

    const today = moment().format("YYYY-MM-DD");
    const startDate = moment().startOf("month").format("YYYY-MM-DD");

    let staffAttendances = await staffAttendanceDB.getByStaffIdForMonth({
      staffId,
      date: today,
    });

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

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

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

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

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

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

    let desiredLocations = [];
    const staffLinkedProps = await staffDB.getLinkedProperties({
      staffId: staff.id,
      clientId: staff.clientId,
    });

    let allLocationNull = 1;

    if (staffLinkedProps) {
      for (let prop of staffLinkedProps) {
        const property = await propertyDB.getById({ id: prop.id });
        if (property) {
          if (property.latitude && property.longitude) {
            allLocationNull = 0;
          }
          desiredLocations.push({
            latitude: property.latitude,
            longitude: property.longitude,
            propId: property.id,
            propName: property.name,
          });
        }
      }
    }

    if (staff.reportingLongitude && staff.reportingLatitude) {
      allLocationNull = 0;
      desiredLocations.push({
        latitude: staff.reportingLatitude,
        longitude: staff.reportingLongitude,
        propId: 0,
        propName: "Office",
      });
    }

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Attendance list sent successfully`
    );
    return res.status(200).json({
      msg: "Attendance list sent successfully",
      // data: staffAttendances || [],
      data: formattedAttendance || [],
      desiredLocations,
      allLocationNull,
      present,
      absent,
      nullLocationMsg:
        "Unable to mark attendace due to absence of property locations. Please contact the owner to resolve this.",
      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,
    });
  }
};

staffs.UploadAadhaar = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "UploadAadhaar";

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

  try {
    const { staffId } = req.body;

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

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Files [${JSON.stringify(files)}]`
    );

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

      await removeTmpImages();

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

    const folderName = `staff_${staff.id}`;
    const folderPath = `uploads/documents/client_${staff.clientId}/${folderName}`;
    const urlBase = folderPath.replace(
      "uploads/",
      `${process.env.UPLOAD_PATH}/`
    );

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

    const provider = await serviceProviderDB.getActiveByType({
      type: CONSTANTS.SERVICE_PROVIDERS_TYPE.AADHAAR_UPLOAD,
    });
    if (!provider) {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], , No service provider is active`
      );

      await removeTmpImages();

      return res.status(400).json({
        msg: "Service is currently unavailable, please try again later.",
        isSuccess: false,
      });
    }

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

    let DOB = null;
    let GENDER = 0;
    let ADDRESS = "";
    let NAME = "";
    let UID = null;

    if (provider.name === CONSTANTS.SERVICE_PROVIDERS.CASHFREE) {
      const { msg, isSuccess, isServerError, yob, genderVal, address, name, uid } =
        await uploadAadhaarToCashfreeStaff({
          staffId: Number(staffId),
          oldFrontPath: String(oldFrontPath),
          oldBackPath: String(oldBackPath),
          folderPath,
          urlBase,
        });

      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], MSG [${msg}], Is Success [${isSuccess}], Is Server Error [${isServerError}], Yob [${yob}], Gender Val [${genderVal}], Address [${address}], Name [${name}], UID [${uid}]`
      );

      if (!isSuccess) {
        await removeTmpImages();

        if (!isServerError) {
          return res.status(400).json({
            msg: msg,
            isSuccess: false,
          });
        } else {
          return res.status(500).json({
            msg: CONSTANTS.MSG.ERROR_MESSAGE,
            isSuccess: false,
          });
        }
      }
      DOB = yob;
      GENDER = Number(genderVal) || 0;
      ADDRESS = address;
      NAME = name;
      UID = uid;
    }

    // await staffDB.updatePersonalInfo({
    //   id: staffId,
    //   gender: GENDER,
    //   address: ADDRESS,
    //   name: NAME,
    //   aadharNumber: UID,
    // });

    await staffDB.updatePersonalInfoX({
      id: staffId,
      gender: GENDER,
      fatherName: "",
      address: ADDRESS || "",
      name: NAME || "",
      aadharNumber: UID || "",
      dob: (DOB && moment(DOB, "DD-MM-YYYY").format("YYYY-MM-DD")) || null
    });

    await staffDB.updateIsVerified({
      id: staffId,
      isVerified: 1,
    });

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

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

    await sendWhatsappStaffKycDone(
      client.mobile,
      updatedStaff.name,
      updatedStaff.fatherName || "",
      updatedStaff.address || "",
      updatedStaff.aadharNumber || "",
      Number(staff?.clientId),
    );

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

    return res.status(200).json({
      msg: "Id doc(s) uploaded sucessfully",
      tenant: updatedStaff,
      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,
    });
  }
};

staffs.UploadIdNumber = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "UploadIdNumber";

  try {
    const { IdNumber, staffId } = req.body;

    const maskedIdNumber = "XXXXXXXX" + IdNumber.slice(-4);

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], ID Number [${maskedIdNumber}]`
    );

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

    const isExists = await staffDocumentDB.getByStaffIdAndType({
      staffId: staffId,
      clientId: staff.clientId,
      type: CONSTANTS.DOCUMENT_TYPES.AADHAAR,
    });

    if (isExists && Number(staff.isVerified) === 1) {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], ID Number [${maskedIdNumber}], ID Number Already Exists And Verified`
      );
      return res.status(400).json({
        msg: "ID Number Already Verified",
        isDown: false,
        isSuccess: false,
      });
    }

    const provider = await serviceProviderDB.getActiveByType({
      type: CONSTANTS.SERVICE_PROVIDERS_TYPE.AADHAAR_NUMBER,
    });

    if (!provider) {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], ID Number [${maskedIdNumber}], No service provider is active`
      );

      return res.status(400).json({
        msg: "Service is currently unavailable, please try again later.",
        isSuccess: false,
      });
    }

    let providerFunc: any = null;

    if (provider.name === CONSTANTS.SERVICE_PROVIDERS.SIGNZY)
      providerFunc = addAadhaarNumberToSignzy;
    else if (provider.name === CONSTANTS.SERVICE_PROVIDERS.CASHFREE)
      providerFunc = addAadhaarNumberToCashfreeX;
    else {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], ID Number [${maskedIdNumber}], SP Name [${provider.name}] Invalid service provider`
      );

      return res.status(400).json({
        msg: "Service is currently unavailable, please try again later.",
        isSuccess: false,
      });
    }

    const { isSuccess, isServerError, msg, isDown, requestId } =
      await providerFunc({
        userId: Number(staffId),
        userType: CONSTANTS.USER_TYPE.STAFF,
        IdNumber,
        maskedIdNumber,
        isExists,
      });

    if (!isSuccess) {
      if (!isServerError) {
        return res.status(400).json({
          msg: msg,
          isSuccess: false,
          isDown,
        });
      } else {
        return res.status(500).json({
          msg: CONSTANTS.MSG.ERROR_MESSAGE,
          isSuccess: false,
          isDown,
        });
      }
    }

    let docId = null;
    if (isExists) {
      docId = isExists.id;
      await staffDocumentDB.updateDoc({
        staffId,
        clientId: staff.clientId,
        type: CONSTANTS.DOCUMENT_TYPES.AADHAAR,
        value: maskedIdNumber,
        id: isExists.id,
      });
    } else {
      docId = await staffDocumentDB.add({
        staffId: staffId,
        clientId: staff.clientId,
        type: CONSTANTS.DOCUMENT_TYPES.AADHAAR,
        value: maskedIdNumber,
      });
    }

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], ID Number [${maskedIdNumber}], ID Number added successfully`
    );

    return res.status(200).json({
      msg: "OTP sent to registered mobile number",
      requestId,
      docId,
      maskedIdNumber,
      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,
    });
  }
};

staffs.VerifyIdNumber = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "VerifyIdNumber";

  try {
    const { requestId, otp, docId, staffId, IdNumber } = req.body;

    let IP = req.headers["x-forwarded-for"] || req.socket.remoteAddress;

    //log.info(`IP [${IP}]`);

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}],  Request Id [${requestId}], OTP [${otp}], Doc Id [${docId}], Id Number [${IdNumber}]`
    );

    const staff = await staffDB.getById({ id: staffId });
    if (!staff) {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}],  Request Id [${requestId}], OTP [${otp}], Doc Id [${docId}], No Tenant Found`
      );
      res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
        isDown: false,
      });
    }

    const hitCount = await ipAddressDB.getCountByHour({ ip: IP });

    if (hitCount > 3) {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Request Id [${requestId}], OTP [${otp}], Doc Id [${docId}], Hit more than thrice in an hour`
      );

      return res.status(400).json({
        msg: "Service is currently unavailable. Please try to upload aadhaar photo or try agian later.",
        isSuccess: false,
      });
    }

    await ipAddressDB.add({
      userId: req.id,
      userType: req.userType,
      ip: IP,
      target: CONSTANTS.IP_ADRESSES_TARGET.AADHAAR_OTP_VERIFICATION,
    });

    const provider = await serviceProviderDB.getActiveByType({
      type: CONSTANTS.SERVICE_PROVIDERS_TYPE.AADHAAR_NUMBER,
    });

    if (!provider) {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Request Id [${requestId}], OTP [${otp}], Doc Id [${docId}], No service provider is active`
      );

      return res.status(400).json({
        msg: "Service is currently unavailable, please try again later.",
        isSuccess: false,
      });
    }

    let providerFunc: any = null;

    if (provider.name === CONSTANTS.SERVICE_PROVIDERS.SIGNZY)
      providerFunc = verifyOTPFromSignzy;
    else if (provider.name === CONSTANTS.SERVICE_PROVIDERS.CASHFREE)
      providerFunc = verifyOTPFromCashfreeX;
    else {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Request Id [${requestId}], OTP [${otp}], Doc Id [${docId}], SP Name [${provider.name}] Invalid service provider`
      );

      return res.status(400).json({
        msg: "Service is currently unavailable, please try again later.",
        isSuccess: false,
      });
    }

    const { isServerError, isSuccess, msg, isDown, isTimeout, photo, result } =
      await providerFunc({
        userId: Number(staffId),
        userType: CONSTANTS.USER_TYPE.STAFF,
        requestId,
        otp,
        docId,
      });

    if (!isSuccess) {
      if (!isServerError) {
        return res.status(400).json({
          msg: msg,
          isSuccess: false,
          isDown,
          isTimeout,
        });
      } else {
        return res.status(500).json({
          msg: CONSTANTS.MSG.ERROR_MESSAGE,
          isSuccess: false,
          isDown,
          isTimeout,
        });
      }
    }

    //Sukhbir - 20th Jan 2025 to make sure there should not be conflict with Eqaro Verification
    const { name, dob, gender, address, care_of, zip, photo_link } = result;

    let genderVal = 0;
    if (gender && gender.toLowerCase() === "m") {
      genderVal = CONSTANTS.GENDER.MALE;
    } else if (gender && gender.toLowerCase() === "f") {
      genderVal = CONSTANTS.GENDER.FEMALE;
    } else {
      genderVal = CONSTANTS.GENDER.OTHER;
    }

    log.info(`[${C}], [${F}], Staff Id [${staffId}], Name [${name}], DOB [${dob}], Gender [${gender}], Address [${address}], Care Of [${care_of}], Zip [${zip}]`);

    await staffDB.updatePersonalInfo({
      id: staffId,
      gender: genderVal,
      address: address || "",
      name: name || "",
      aadharNumber: IdNumber || "",
    });

    await staffDB.updateIsVerified({
      id: staffId,
      isVerified: 1,
    });

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

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

    await sendWhatsappStaffKycDone(
      client.mobile,
      updatedStaff.name,
      updatedStaff.fatherName || "",
      updatedStaff.address || "",
      updatedStaff.aadharNumber || "",
      Number(staff?.clientId)
    );

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Request Id [${requestId}], OTP [${otp}], Doc Id [${docId}], Details fetched & sent sucessfully`
    );

    return res.status(200).json({
      msg: "Details fetched sucessfully",
      data: { image: photo || null },
      tenant: updatedStaff,
      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,
    });
  }
};

staffs.EditStaffReportingLocation = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "EditStaffReportingLocation";

  try {
    const { staffId, longitude, latitude } = req.body;

    log.info(`[${C}], Staff Id [${staffId}], [${F}], Longitude [${longitude}], Latitude [${latitude}]`);

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Requesting Staff Id [${req.id}], Requesting Staff Role [${staff.role}], Longitude [${longitude}], Latitude [${latitude}], Platform [${req.platform}], Admin Requesting....`
      );
    }

    await staffDB.editReportingLocation({
      id: staffId,
      clientId: clientId,
      reportingLongitude: longitude,
      reportingLatitude: latitude,
    });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Longitude [${longitude}], Latitude [${latitude}], Reporting Location Updated Successfully`);

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

staffs.InitiateAadharVerifyDigiLocker = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "InitiateAadharVerifyDigiLocker";

  try {
    const { staffId } = req.body;

    let IP = req.headers["x-forwarded-for"] || req.socket.remoteAddress;

    //log.info(`IP [${IP}]`);

    log.info(
      `[${C}], [${F}], Staff ID [${staffId}], Staff request for aadhar verify`
    );
    const staff = await staffDB.getById({ id: staffId });
    if (!staff) {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], No Staff Found`
      );
      res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
        isDown: false,
      });
    }

    const hitCount = await ipAddressDB.getCountByHour({ ip: IP });

    if (hitCount > 20) {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Hit more than thrice in an hour`
      );

      return res.status(400).json({
        msg: "Service is currently unavailable. Please try to upload aadhaar photo or try agian later.",
        isSuccess: false,
      });
    }

    await ipAddressDB.add({
      userId: req.id,
      userType: req.userType,
      ip: IP,
      target: CONSTANTS.IP_ADRESSES_TARGET.AADHAAR_OTP_VERIFICATION,
    });

    const provider = await serviceProviderDB.getActiveByType({
      type: CONSTANTS.SERVICE_PROVIDERS_TYPE.AADHAAR_NUMBER,
    });

    if (!provider) {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], No service provider is active`
      );

      return res.status(400).json({
        msg: "Service is currently unavailable, please try again later.",
        isSuccess: false,
      });
    }

    const { isSuccess, isServerError, msg, isDown, url } =
      await createDigiLockerCashfreeLinkViaClient({
        C,
        F,
        userId: Number(staffId),
        userType: CONSTANTS.USER_TYPE.STAFF,
        redirectionUrl: process.env.CASHFREE_DIGILOCKER_STAFF_REDIRECTION_URL || "",
        clientId: staff.clientId,
      });

    if (!isSuccess) {
      if (!isServerError) {
        return res.status(400).json({
          msg: msg,
          isSuccess: false,
          isDown
        });
      } else {
        return res.status(500).json({
          msg: CONSTANTS.MSG.ERROR_MESSAGE,
          isSuccess: false,
          isDown
        });
      }
    } else {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Verification Link generated successfully`
      );
      return res.status(200).json({
        msg: "Link generated successfully",
        url,
        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,
    });
  }
};

staffs.VerifyDigilockerRequest = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "VerifyDigilockerRequest";

  try {
    let verificationId = req.query.verificationId || "";

    log.info(
      `[${C}], [${F}], Verification Id [${verificationId}], Verify aadhar Digilocker request`
    );

    let staff = [];

    if (null != verificationId || undefined != verificationId || "" != verificationId) {
      staff = await staffDB.getByVerificationId({ verificationId });
    }
    if (null === staff || false === staff) {
      log.info(
        `[${C}], [${F}], Verification Id [${verificationId}], Type [AADHAAR] Verification ID not found `
      );
      return res.status(400).json({
        isServerError: false,
        isSuccess: false,
        msg: "Unable to create aadhar link. Please try again later.",
        isDown: true,
      });
    }

    let staffId = staff?.id;
    let referenceId = staff?.referenceId;


    const { isServerError, isSuccess, msg, isDown, photo, completeAddress, result, frontUrl, backUrl } =
      await createDigiLockerVerifyRequestViaClient({
        C,
        F,
        verificationId: staff?.verificationId,
        userType: CONSTANTS.USER_TYPE.STAFF,
        userId: Number(staffId),
        referenceId: Number(referenceId),
        clientId: staff?.clientId,
      });

    if (!isSuccess) {
      if (!isServerError) {
        return res.status(400).json({
          msg: msg,
          isSuccess: false,
          isDown,
        });
      } else {
        return res.status(500).json({
          msg: CONSTANTS.MSG.ERROR_MESSAGE,
          isSuccess: false,
          isDown,
        });
      }
    }

    //Sukhbir - 20th Jan 2025 to make sure there should not be conflict with Eqaro Verification
    const { name, dob, gender, address, care_of, zip, photo_link, uid } = result;
    const idNumberDB = "XXXX-XXXX-" + uid.slice(-4);
    let genderVal = 0;
    if (gender && gender.toLowerCase() === "m") {
      genderVal = CONSTANTS.GENDER.MALE;
    } else if (gender && gender.toLowerCase() === "f") {
      genderVal = CONSTANTS.GENDER.FEMALE;
    } else {
      genderVal = CONSTANTS.GENDER.OTHER;
    }

    log.info(`[${C}], [${F}], Staff Id [${staffId}], Name [${name}], DOB [${dob}], Gender [${gender}], Address [${completeAddress}], Care Of [${care_of}], Zip [${zip}]`);

    await staffDB.updatePersonalInfoX({
      id: staffId,
      gender: genderVal,
      fatherName: care_of?.slice(4) || "",
      address: completeAddress || "",
      name: name || "",
      aadharNumber: idNumberDB || "",
      dob: (dob && moment(dob, "DD-MM-YYYY").format("YYYY-MM-DD")) || null
    });

    await staffDB.updateIsVerified({
      id: staffId,
      isVerified: 1,
    });

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


    const isAadharExists = await staffDocumentDB.getByStaffIdAndType({
      staffId: staffId,
      clientId: staff.clientId,
      type: CONSTANTS.DOCUMENT_TYPES.AADHAAR,
    });

    if (isAadharExists) {
      await staffDocumentDB.updateDoc({
        staffId,
        clientId: staff.clientId,
        type: CONSTANTS.DOCUMENT_TYPES.AADHAAR,
        value: idNumberDB,
        id: isAadharExists.id,
      });
    } else {
      await staffDocumentDB.add({
        staffId: staffId,
        clientId: staff.clientId,
        type: CONSTANTS.DOCUMENT_TYPES.AADHAAR,
        value: idNumberDB,
      });
    }

    const folderPath = `uploads/documents/client_${staff.clientId}/staffs/${staffId}`;
    const urlBase = folderPath.replace(
      "uploads/",
      `${process.env.UPLOAD_PATH}/`
    );

    if (!fs.existsSync(folderPath)) {
      await fsPromises.mkdir(folderPath, { recursive: true });
    }
    const imageBuffer = Buffer.from(photo, 'base64');
    let fileName = `profile.jpeg`;
    await fsPromises.writeFile(`${folderPath}/${fileName}`, imageBuffer as Uint8Array);
    const isExists = await staffDocumentDB.getByStaffIdAndType({
      staffId: staffId,
      clientId: staff.clientId,
      type: CONSTANTS.DOCUMENT_TYPES.SELFI,
    });
    let docId = null;
    if (isExists) {
      docId = isExists.id;
      await staffDocumentDB.updateDoc({
        staffId,
        clientId: staff.clientId,
        type: CONSTANTS.DOCUMENT_TYPES.SELFI,
        value: `${urlBase}/${fileName}`,
        id: isExists.id,
      });
    } else {
      docId = await staffDocumentDB.add({
        staffId: staffId,
        clientId: staff.clientId,
        type: CONSTANTS.DOCUMENT_TYPES.SELFI,
        value: `${urlBase}/${fileName}`,
      });
    }

    const client = await clientDB.getById({
      id: updatedStaff.clientId,
    });
    if(updatedStaff?.fatherName === "" || updatedStaff?.fatherName === null) {
      updatedStaff.fatherName = "NA";
    }
    await sendWhatsappStaffKycDone(
      client.mobile,
      updatedStaff.name,
      updatedStaff.fatherName || "",
      updatedStaff.address || "",
      updatedStaff.aadharNumber || "",
      Number(staff?.clientId),
    );



    let designation = await roleDescription(staff.role);
    let businessName = client?.businessName || "";
    let website = client?.website || "";
    let businessEmail = client?.businessEmail || "";
    let businessAddress = client?.businessAddress || "";
    if (businessName != "" && website != "" && businessEmail != "" && businessAddress != "") {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Client Id [${staff.clientId}], Name [${updatedStaff.name}], Role [${staff.role}], Mobile [${staff.mobile}], Business Email [${businessEmail}], Business Address [${businessAddress}], Website [${website}], Business Name [${businessName}], Generating Business Card`
      );
      const businessCard = await createBusinessCard(
        staff.clientId,
        staffId,
        updatedStaff.name,
        designation,
        businessName,
        staff.mobile,
        businessEmail,
        website,
        businessAddress,
        true
      );
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Client Id [${staff.clientId}], Name [${updatedStaff.name}], Role [${staff.role}], Mobile [${staff.mobile}], Business Card Created [${businessCard.url}], Business card generated successfully`
      );
      const isExists = await staffDocumentDB.getByStaffIdAndType({
        staffId: staffId,
        clientId: staff.clientId,
        type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
      });
      let docId = null;
      if (isExists) {
        docId = isExists.id;
        await staffDocumentDB.updateDoc({
          staffId,
          clientId: staff.clientId,
          type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
          value: businessCard.url,
          id: isExists.id,
        });
      } else {
        docId = await staffDocumentDB.add({
          staffId: staffId,
          clientId: staff.clientId,
          type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
          value: businessCard.url,
        });
      }
    } else {
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Client Id [${staff.clientId}], Name [${updatedStaff.name}], Role [${staff.role}], Mobile [${staff.mobile}], Business Email [${businessEmail}], Business Address [${businessAddress}], Website [${website}], Business Name [${businessName}], Staff Id [${staffId}], Business card generation skipped as business details are not complete`
      );
    }
    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Details fetched & sent sucessfully`
    );

    return res.status(200).json({
      msg: "Details fetched sucessfully",
      data: { image: photo || null },
      tenant: updatedStaff,
      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,
    });
  }
};

staffs.ViewProfile = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "ViewProfile";

  try {
    const staffId = req.id;

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

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

    const document = await staffDocumentDB.getByStaffIdAndType({
      staffId,
      clientId: staff.clientId,
      type: CONSTANTS.DOCUMENT_TYPES.SELFI
    });
    staff.profilePic = "";
    if (true == document) {
      staff.profilePic = document.value;
    }

    const isExists = await staffDocumentDB.getByStaffIdAndType({
      staffId: staffId,
      clientId: staff.clientId,
      type: CONSTANTS.DOCUMENT_TYPES.BUSINESS_CARD,
    });
    staff.businessCard = "";
    if (isExists) {
      staff.businessCard = isExists.value;
    }

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Profile details sent successfully`
    );
    return res.status(200).json({
      msg: "Profile details sent successfully",
      data: { ...staff },
      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,
    });
  }
};

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

  try {
    const { amount, type, paidDate, mode, transId, staffId } = req.body;

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Amount [${amount}], Type [${type}], Paid Date [${paidDate}], Mode [${mode}], Transaction Id [${transId}]`
    );

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

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

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

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

    let expenseId = null;

    if (Number(type) === 1) {
      //salary
      expenseId = await expenseDB.create({
        type: 14,
        amount: amount,
        clientId,
        paidDate: paidDate,
        paidByUserType: userType,
        paidBy: req.id,
        paidTo: staffId,
        paidToUserType: CONSTANTS.USER_TYPE.STAFF,
        description: "Salary",
        paymentMethod: mode,
        repetitionType: 1,
        noOfMonths: 0,
        dueDate: null,
        isPaid: 1,
        bankRefNum: transId || null,
      });

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

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

      // await staffLedgerDB.add({
      //   staffId: staffId,
      //   amount: amount,
      //   type: CONSTANTS.STAFF_LEDGER_TYPES.SALARY,
      //   mode: mode,
      //   description: `Salary`,
      //   transactionId: transId || null,
      //   paidDate: moment(paidDate).format("YYYY-MM-DD"),
      // });
      await staffLedgerDB.addExpense({
        staffId: staffId,
        amount: amount,
        type: CONSTANTS.STAFF_LEDGER_TYPES.SALARY,
        mode: mode,
        description: `Salary`,
        expenseId: expenseId || null,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        dueDate: moment(paidDate).format("YYYY-MM-DD"),
      });
    } else if (Number(type) === 2) {
      //expense returned
      // await staffLedgerDB.add({
      //   staffId: staffId,
      //   amount: amount,
      //   type: CONSTANTS.STAFF_LEDGER_TYPES.EXPENSE_RETURN,
      //   mode: mode,
      //   description: `Expense reimbursed`,
      //   transactionId: transId || null,
      //   paidDate: moment(paidDate).format("YYYY-MM-DD"),
      // });
      await staffLedgerDB.addExpense({
        staffId: staffId,
        amount: amount,
        type: CONSTANTS.STAFF_LEDGER_TYPES.EXPENSE_RETURN,
        mode: mode,
        description: `Expense reimbursed`,
        expenseId: expenseId || null,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        dueDate: moment(paidDate).format("YYYY-MM-DD"),
      });

      await staffBalanceDB.updateReimbursedAmount({
        staffId: staffId,
        amount: amount,
      });

    } else {
      //invalid type
      log.info(`[${C}], [${F}], Type [${type}], Invalid type [${type}]`);
      return res.status(400).json({
        msg: "Invalid type",
        isSuccess: false,
      });
    }

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Expense Id [${expenseId}], Amount [${amount}], Type [${type}], Paid Date [${paidDate}], Mode [${mode}], Transaction Id [${transId}], Payment recorded successfully`
    );

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

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

  try {
    let { amount, type, paidDate, dueDate, mode=1, transId, staffId, description, paymentAccountNo = null, paymentAccountName = null } = req.body;

    if (!dueDate) {
      dueDate = paidDate;
    }

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Amount [${amount}], Type [${type}], Paid Date [${paidDate}], Due Date [${dueDate}], Mode [${mode}], Description [${description}], Transaction Id [${transId}], Payment Account Number [${paymentAccountNo}], Payment Account Name [${paymentAccountName}]`
    );

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

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

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

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

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

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

    let expenseId = null;

    let expenseType = null;
    let isPaid = 0;
    let makeExpenseEntry = true;
    // let staffLedgerType = CONSTANTS.STAFF_LEDGER_TYPES.EXPENSES;

    if (Number(type) === CONSTANTS.STAFF_LEDGER_TYPES.SALARY_ADVANCE) {
      description = description && description.trim() !== "" ? description : `Advance Salary for ${moment(dueDate).format("MMM YYYY")}`;
      expenseType = 14;
      isPaid = 1;
    } else if (Number(type) === CONSTANTS.STAFF_LEDGER_TYPES.BONUS) {
      description = description && description.trim() !== "" ? description : "Bonus";
      expenseType = 52;
    } else if (Number(type) === CONSTANTS.STAFF_LEDGER_TYPES.TRAVEL_ALLOWANCE) {
      description = description && description.trim() !== "" ? description : "Travel Allowance";
      expenseType = 23;
    } else if (Number(type) === CONSTANTS.STAFF_LEDGER_TYPES.HRA) {
      description = description && description.trim() !== "" ? description : "HRA";
      expenseType = 53;
    } else if (Number(type) === CONSTANTS.STAFF_LEDGER_TYPES.EXPENSE_RETURN) {
      description = description && description.trim() !== "" ? description : "Advance Expense";
      isPaid = 1;
      makeExpenseEntry = false;
    } else if (Number(type) === CONSTANTS.STAFF_LEDGER_TYPES.REIMBURSEMENT) {
      description = description && description.trim() !== "" ? description : "Expense Reimbursement";
      makeExpenseEntry = false;
    } else {
      //invalid type
      log.info(`[${C}], [${F}], Client Id [${clientId}], Type [${type}],`);
      return res.status(400).json({
        msg: "Invalid type",
        isSuccess: false,
      });
    }

    if (makeExpenseEntry) {
      // advance salary || bonus || travel allowance || hra
      expenseId = await expenseDB.create({
        type: expenseType,
        amount: amount,
        clientId,
        paidDate: paidDate ? moment(paidDate + " " + moment().format("HH:mm:ss")).format("YYYY-MM-DD HH:mm:ss") : null,
        paidByUserType: userType,
        paidBy: req.id,
        paidTo: staffId,
        paidToUserType: CONSTANTS.USER_TYPE.STAFF,
        description: description,
        paymentMethod: mode || CONSTANTS.TRANSACTION_MODES.OFFLINE,
        repetitionType: 1,
        noOfMonths: 0,
        dueDate: dueDate,
        isPaid: isPaid,
        bankRefNum: transId || null,
      });

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

      //Due entry
      await staffLedgerDB.addExpense({
        staffId: staffId,
        amount: -amount,
        type: type,
        mode: mode || CONSTANTS.TRANSACTION_MODES.OFFLINE,
        description: description,
        expenseId: expenseId || null,
        // paidDate: moment(paidDate).format("YYYY-MM-DD"),
        paidDate: null,
        dueDate: moment(dueDate).format("YYYY-MM-DD"),
      });

      //Paid entry
      if (isPaid === 1) {
        await staffLedgerDB.addExpense({
          staffId: staffId,
          amount: amount,
          type: type,
          mode: mode || CONSTANTS.TRANSACTION_MODES.OFFLINE,
          description: description,
          expenseId: expenseId || null,
          paidDate: moment(paidDate).format("YYYY-MM-DD"),
          dueDate: moment(dueDate).format("YYYY-MM-DD"),
        });

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

        await setExpensePaymentTallyStatus(
          Number(clientId),
          Number(expenseId),
          CONSTANTS.TALLY_STATUS.PENDING,
        );
      }
    } else {
      //Advance Expense || reimbursment
      await staffLedgerDB.addExpense({
        staffId: staffId,
        amount: amount,
        type: type,
        mode: mode || CONSTANTS.TRANSACTION_MODES.OFFLINE,
        description: description,
        expenseId: null,
        paidDate: moment(paidDate).format("YYYY-MM-DD"),
        dueDate: moment(dueDate).format("YYYY-MM-DD"),
      });

      await staffBalanceDB.updateReimbursedAmount({
        staffId: staffId,
        amount: amount,
      });
    }

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Expense Id [${expenseId}], Amount [${amount}], Type [${type}], Paid Date [${paidDate}], Mode [${mode}], Transaction Id [${transId}], Payment recorded successfully`
    );

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

staffs.ListPayables = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "ListPayables";

  try {
    let { staffId } = req.params;

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Platform [${req.platform}]`
    );

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

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

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

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

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

    const payables = await expenseDB.getStaffPayables({
      staffId,
      clientId,
    });

    let totalDues = 0;
    if (payables) {
      for (const payable of payables) {
        totalDues += Math.abs(Number(payable.amount));
      }
    }

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

    return res.status(200).json({
      msg: "Payables fetched successfully",
      payables: payables || [],
      totalDues: totalDues || 0,
      isSuccess: true,
    });

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

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

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

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

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

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

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

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

    const folderName = `staff${staffId}`;
    const folderPath = `uploads/documents/client_${staff.clientId}/staffs/${folderName}`;
    const urlBase = folderPath.replace(
      "uploads/",
      `${process.env.UPLOAD_PATH}/`
    );

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

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

    const isExists = await staffDocumentDB.getByStaffIdAndType({
      staffId,
      clientId,
      type: type,
    });

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

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

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

      await staffDocumentDB.updateDoc({
        staffId,
        clientId,
        type: type,
        value: url,
        id: isExists.id,
      });
    } else {
      await staffDocumentDB.add({
        staffId,
        clientId,
        type: type,
        value: url,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Type [${type}], Document uploaded successfully`
      );
    }

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

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

      const isExists = await staffDocumentDB.getByStaffIdAndType({
        staffId,
        clientId,
        type: CONSTANTS.STAFF_DOCUMENT_TYPES.AADHAAR_BACK,
      });

      if (isExists) {
        await staffDocumentDB.updateDoc({
          staffId,
          clientId,
          type: CONSTANTS.STAFF_DOCUMENT_TYPES.AADHAAR_BACK,
          value: url,
          id: isExists.id,
        });

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Type [AADHAAR], URL [${url}], Aadhaar Front document re-uploaded successfully`
        );
      } else {
        await staffDocumentDB.add({
          staffId,
          clientId,
          type: CONSTANTS.STAFF_DOCUMENT_TYPES.AADHAAR_BACK,
          value: url,
        });

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

    if (Number(type) === CONSTANTS.STAFF_DOCUMENT_TYPES.AADHAAR) {
      log.info(`1.`)
      await staffDB.updateIsVerified({
        id: staffId,
        isVerified: 1,
      });
      log.info(`2.`);
    }

    const docs = await staffDocumentDB.getByStaffId({ staffId, clientId});
    if (docs && docs.length > 0) {
      for (let doc of docs) {
        const title = await getStaffDocumentTitle(doc.type);
        doc.title = title;
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Id doc(s) uploaded sucessfully`
    );

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

    await removeTmpImages();

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

staffs.ListStaffBookings = async (req: CustomRequest, res: Response) => {
  const C = "Staff Controller";
  const F = "ListStaffBookings";

  try {
    const { pageNum, bookedBy, startDate = moment().startOf("month").format("YYYY-MM-DD"), endDate = moment().endOf("month").format("YYYY-MM-DD")} = req.query;
    const userType = req.userType;
    log.info(
        `[${C}], [${F}], Page Num [${pageNum}], Staff Id [${bookedBy}], Start Date [${startDate}], End Date [${endDate}]`
      );
    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

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

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

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

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting....`
      );
    }
    
    const limit = 10;
    
    let occTenants = await occupancyDB.getByClientIdAndBookedByAndDate({
      clientId,
      bookedBy,
      startDate,
      endDate,
      pageNum,
      limit,
    });

    let occTenantCount = await occupancyDB.getCountByClientIdAndBookedByAndDate({
      clientId,
      bookedBy,
      startDate,
      endDate,
    });
    
    let tenants: any = [];
    let tenantCount = 0;
    if (occTenants && occTenants.length > 0) {
      tenants = [...occTenants]
      tenantCount += Number(occTenantCount);
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Staff Booking Sent Successfully`
    );

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

export default staffs;
