import { 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 extraChargeDB from "../models/extraCharges.model";
import getExtraChargeName from "../utils/getExtraChargeName";
import electricityDB from "../models/electricity.model";
import roomDB from "../models/room.model";
import occupancyDB from "../models/occupancy.model";
import moveOutDB from "../models/moveOut.model";
import moment from "moment";
import occupanciesTypes from "../schemas/occupancy.schema";
import tenantsTypes from "../schemas/tenant.schema";
import duesDB from "../models/dues.model";
import staffDB from "../models/staff.model";
import tenantDB from "../models/tenant.model";
import generateLedgerReferenceId from "../utils/generateLedgerReferenceId";
import getDueDescription from "../utils/getDueDescription";
import ledgerDB from "../models/ledger.model";
import adjustExcessPayments, { adjustExcessPaymentsForMovedOutX } from "../utils/adjustExcessPayment";
import roomOptionDB from "../models/roomOption.model";
import fsPromises from "fs/promises";
import fs from "fs";
import moveOutDuesDB from "../models/moveOutDues.model";
import getDueName from "../utils/getDueName";
import { logActivity } from "../utils/logActivity";
import { AddMovedOutDuesWithoutAdjust } from "../utils/dueHandler";

type recordType = {
  tenantId: number;
  amountToPay: number;
  occupancyId: number;
};

const electricity: any = {};

electricity.AddUnits = async (req: CustomRequest, res: Response) => {
  const C = "Electricity Controller";
  const F = "AddUnits";

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

    const userType = req.userType;

    let clientId = req.id;

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

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Units Arr Length [${unitsArr.length}], Prop Id [${propId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Units Arr Length [${unitsArr.length}], Prop Id [${propId}], Client Requested....`
      );
    }

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

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

    for (const item of unitsArr) {
      const { current, previous, roomId } = item;

      if (Number(previous) >= Number(current)) continue;

      const isExists = await electricityDB.getForCurrentMonth({
        clientId,
        propId,
        roomId,
      });

      if (isExists) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Units Arr Length [${unitsArr}],  Prop Id [${propId}], Previous Units [${previous}], Current Units [${current}], Room Id [${roomId}] Electricity units already exists for this month`
        );
        continue;
      }

      await electricityDB.add({
        units: current,
        roomId,
        clientId,
        propId,
      });

      const isPrevExists = await electricityDB.getForPreviousMonth({
        clientId,
        propId,
        roomId,
      });

      if (!isPrevExists) {
        await electricityDB.addPrevious({
          units: previous,
          roomId,
          clientId,
          propId,
        });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Previous Units [${previous}], Current Units [${current}], Room Id [${roomId}], Prop Id [${propId}], Electricity units has been added successfully`
      );

      const { pricePerUnit } = await extraChargeDB.getElectricityPrice({
        clientId,
        propId,
        type: CONSTANTS.EXTRA_CHARGE_TYPES.ELECTRICITY,
      });

      const units = Number(current) - Number(previous);
      const billAmount = units * Number(pricePerUnit);
      let newTenantDeductedAmt = 0;
      let moveOutTenantDeductedAmt = 0;
      const occupancies = await occupancyDB.getOccupiedOnlyByRoomId({
        roomId,
      });

      const moveOutTenants = await moveOutDB.getForElectricity({
        roomId,
      });

      let records: recordType[] = [];
      let oldTenants = [];
      let newTenants = [];

      if (occupancies) {
        oldTenants = occupancies.filter(
          (item: occupanciesTypes) =>
            !moment().subtract(1, "month").isSame(item.moveInDate, "month") &&
            !moment().isSame(item.moveInDate, "month")
        );

        newTenants = occupancies.filter(
          (item: occupanciesTypes) =>
            moment().subtract(1, "month").isSame(item.moveInDate, "month") ||
            moment().isSame(item.moveInDate, "month")
        );
      }

      let lastReading = Number(current);
      let lastCalculatedAmt = 0;

      if (newTenants.length > 0) {
        let count = 0;
        for (let i = newTenants.length - 1; i >= 0; i--) {
          const electricityReading =
            Number(newTenants[i].electricityReading) || Number(previous);

          const totalAmount = (lastReading - electricityReading) * pricePerUnit;

          const totalOccupancy = occupancies.length - count;

          let amountToPay = totalAmount / totalOccupancy;

          amountToPay = amountToPay + lastCalculatedAmt;

          records.push({
            tenantId: newTenants[i].tenantId,
            amountToPay,
            occupancyId: newTenants[i].id,
          });

          lastReading = electricityReading;
          lastCalculatedAmt = amountToPay;

          newTenantDeductedAmt += amountToPay;
          count++;
        }
      }

      lastReading = Number(previous);
      lastCalculatedAmt = 0;

      if (moveOutTenants && moveOutTenants.length > 0) {
        let count = 0;
        for (const moveOutTenantOcc of moveOutTenants) {
          const electricityReading =
            Number(moveOutTenantOcc.electricityReading) || Number(current) / 2;

          const totalAmount = (electricityReading - lastReading) * pricePerUnit;

          const totalOccupancy =
            occupancies.length -
            newTenants.length +
            moveOutTenants.length -
            count;

          let amountToPay = totalAmount / totalOccupancy;

          amountToPay = amountToPay + lastCalculatedAmt;

          moveOutTenantDeductedAmt += amountToPay;
          lastReading = electricityReading;
          lastCalculatedAmt = amountToPay;

          count++;
        }
      }

      const remainingAmt =
        billAmount - newTenantDeductedAmt - moveOutTenantDeductedAmt;

      if (remainingAmt > 0) {
        oldTenants.forEach((tenantOcc: occupanciesTypes) => {
          records.push({
            tenantId: tenantOcc.tenantId,
            amountToPay: remainingAmt / oldTenants.length,
            occupancyId: tenantOcc.id,
          });
        });
      }

      //for those tenants who occupied entire room
      const finalRecords = records.reduce(
        (acc: recordType[], cur: recordType) => {
          const isExists = acc.find((item) => item.tenantId === cur.tenantId);

          if (isExists) {
            isExists.amountToPay += cur.amountToPay;
          } else {
            acc.push(cur);
          }

          return acc;
        },
        []
      );

      for (const record of finalRecords) {
        if (record.amountToPay <= 0) continue;

        await duesDB.add({
          tenantId: record.tenantId,
          amount: Math.round(record.amountToPay),
          occupancyId: record.occupancyId,
          roomId: roomId,
          propId: propId,
          clientId: clientId,
          type: CONSTANTS.DUES_TYPES.ELECTRICITY,
          dueDate: moment().format("YYYY-MM-DD"),
        });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Previous Units [${previous}], Current Units [${current}], Room Id [${roomId}], Prop Id [${propId}], Tenant dues added successfully`
      );
    }

    return res.status(200).json({
      msg: "Electricity units updated & tenant dues 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,
    });
  }
};

electricity.ListForClient = async (req: CustomRequest, res: Response) => {
  const C = "Electricity Controller";
  const F = "ListForClient";

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

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

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

    const floorWise = rooms.reduce((acc: any, room: any) => {
      const floor = room.floor;

      const isExists = acc.find((item: any) => item.floor === floor);
      if (!isExists) {
        acc.push({
          floor,
          rooms: [room],
        });
      } else {
        isExists.rooms.push(room);
      }

      return acc;
    }, []);

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], List has been sent successfully`
    );

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

electricity.ListForClientForAmount = async (req: CustomRequest, res: Response) => {
  const C = "Electricity Controller";
  const F = "ListForClientForAmount";

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

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Type [${type}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    let data = [];
    if (CONSTANTS.PROPERTY_TYPE.PG == Number(type)) {
      let allFloors = await electricityDB.getAllFloors({ propId });
      let allRooms = await electricityDB.getAllRooms({ propId });
      let previousBill = await electricityDB.getPreviousBillForRoom({ propId });
      let roomTenantCount = await occupancyDB.getTenantCountByPropId({clientId, propId});

      const tenantCountMap: any = {};
      if (Array.isArray(roomTenantCount)) {
        roomTenantCount.forEach((row: any) => {
          tenantCountMap[row.roomId] = row.tenantCount;
        });
      }

      let stats = allFloors.map((floor: any) => {
        let roomsOnFloor = allRooms.filter(
          (room: any) => room.floor === floor.floor
        );
        let rooms = [];
        let floorId = 1111;
        if (floor.floor != "G") {
          floorId = Number(floor.floor);
        }
        let floorObj: any = {
          name: `Floor ${floor.floor}`,
          id: floorId,
          roomCount: floor.roomCount,
        };
        for (let item of roomsOnFloor) {
          let prevBill = previousBill.filter(
            (curr: any) => item.roomNum === curr.roomNumber
          );
          rooms.push({
            roomNum: item.roomNum,
            id: item.id,
            prevBill: prevBill,
            amenities: item.amenities,
            tenantCount: tenantCountMap[item.id] || 0 
          });
          floorObj.rooms = rooms;
        }

        return floorObj;
      });
      data = stats;
    } else {
      let allFlats = await electricityDB.getAllFlats({ propId });
      let allRooms = await electricityDB.getAllRoomsForFlat({ propId });
      let previousBill = await electricityDB.getPreviousBill({ propId });
      
      let roomTenantCount = await occupancyDB.getTenantCountByPropId({clientId, propId});

      const tenantCountMap: any = {};
      if (Array.isArray(roomTenantCount)) {
        roomTenantCount.forEach((row: any) => {
          tenantCountMap[row.roomId] = row.tenantCount;
        });
      }

      let stats = allFlats.map((flat: any) => {
        let roomsOnFlat = allRooms.filter(
          (room: any) => room.flatId === flat.id
        );

        let rooms = roomsOnFlat.map((room: any) => {
          // let prevBill = previousBill.filter(
          //   (bill: any) => bill.roomNumber === room.roomNum
          // );
          return {
            roomNum: room.roomNum,
            id: room.id,
            // prevBill: prevBill,
            amenities: room.amenities,
            tenantCount: tenantCountMap[room.id] || 0 
          };
        });

        return {
          id: flat.id,
          name: flat.name,
          roomCount: rooms.length,
          rooms: rooms,
          prevBill: previousBill.filter((bill: any) => bill.id === flat.id)
        };
      });

      data = stats;
    }

    let electricityChargesType;
    const { pricePerUnit } = await extraChargeDB.getElectricityPrice({
      clientId,
      propId,
      type: CONSTANTS.EXTRA_CHARGE_TYPES.ELECTRICITY,
    });
    if(pricePerUnit == 0){
      electricityChargesType = CONSTANTS.CHARGE_TYPE.AMOUNT;
    }
    else{
      electricityChargesType = CONSTANTS.CHARGE_TYPE.UNIT;
    }

    // log.info(`Data [${JSON.stringify(data)}]`);

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Type [${type}], Electricity Charges Type [${electricityChargesType}] List has been sent successfully`
    );

    return res.status(200).json({
      msg: "List has been sent successfully",
      data: data || {},
      electricityChargesType: electricityChargesType || 0,
      pricePerUnit,
      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,
    });
  }
};

electricity.AddTenantUnits = async (req: CustomRequest, res: Response) => {
  const C = "Electricity Controller";
  const F = "AddTenantUnits";

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

    let clientId = req.id;

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

      clientId = staff.clientId;

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

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

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

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

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

    await occupancyDB.updateElectricityUnits({
      clientId,
      tenantId,
      electricityReading: units,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Units [${units}], Electricity units has been added successfully`
    );

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

electricity.Add = async (req: CustomRequest, res: Response) => {
  const C = "Electricity Controller";
  const F = "Add";

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

  try {
    let { propId, amount = 0, roomFlatId, startDate, endDate, previousReading, currentReading, acReading  } = req.body;

    propId = Number(propId);
    amount = Number(amount);
    acReading = acReading ? JSON.parse(acReading) : acReading;

    let acSum = 0;
    let rawAmt= amount;
    let recordedBy = "";

    const userType = req.userType;

    let clientId = req.id;

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

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}],  Prop Id [${propId}], Current Bill [${amount}], Room/Flat Id [${roomFlatId}], Start Date [${startDate}], End Date [${endDate}], Previous Reading [${previousReading}], Current Reading [${currentReading}], Room Ac Reading [${JSON.stringify(acReading)}], Is File Uploaded ${file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"}, Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}],  Prop Id [${propId}], Current Bill [${amount}], Room/Flat Id [${roomFlatId}], Start Date [${startDate}], End Date [${endDate}], Previous Reading [${previousReading}], Current Reading [${currentReading}], Room Ac Reading [${JSON.stringify(acReading)}], Is File Uploaded ${file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"}, Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], No Client Found`
      );

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

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

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

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

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

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

    if(acReading && acReading.length > 0) {
      for (let roomReading of acReading) {
        acSum += roomReading.reading; 
      }
    }

    let amountPerUnit = {
      pricePerUnit: 0,
    };

    if(previousReading && currentReading){
      amountPerUnit = await extraChargeDB.getElectricityPrice({
        clientId, 
        propId, 
        type: CONSTANTS.EXTRA_CHARGE_TYPES.ELECTRICITY
      });
      amount = (Number(currentReading) - Number(previousReading) - Number(acSum)) * Number(amountPerUnit.pricePerUnit);
      rawAmt = (Number(currentReading) - Number(previousReading)) * Number(amountPerUnit.pricePerUnit);
    }

    let tenants;
    // if (property.type == CONSTANTS.PROPERTY_TYPE.FLAT) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Flat Id [${roomFlatId}], Start Date [${startDate}], End Date [${endDate}],`
    //   );
    //   tenants = await occupancyDB.getTenantsMoveInAndMoveOutDateByFlatId({
    //     clientId,
    //     propId,
    //     flatId: roomFlatId,
    //     startDate,
    //     endDate,
    //   });
    // } else {
    // }
    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Room Id [${roomFlatId}], Start Date [${startDate}], End Date [${endDate}]`
    );
    roomFlatId = String(roomFlatId);
    let roomFlatIds = roomFlatId.split(",");

    tenants = await occupancyDB.getTenantsMoveInAndMoveOutDate({
      clientId,
      propId,
      roomIds: roomFlatIds,
      startDate,
      endDate,
    });

    let noOfTenant = tenants.length;

    if (noOfTenant === 0) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Room Id [${roomFlatId}], Start Date [${startDate}], End Date [${endDate}], No of Tenants [${noOfTenant}], No Tenants In The Room`
      );

      return res.status(400).json({
        msg: "Cannot add electricity bill for vacant rooms",
        isSuccess: false,
      });
    }
    let singleRoomFlatId = roomFlatIds[0];
    if (property.type == CONSTANTS.PROPERTY_TYPE.FLAT) {
        let room = await roomDB.getById({id:roomFlatIds[0]});
        singleRoomFlatId = room?.flatId;
    }
    const billId = await electricityDB.addAmount({
      amount: rawAmt,
      roomFlatId: Number(singleRoomFlatId),
      clientId,
      propId,
      startDate,
      endDate,
      noOfTenant,
      previousReading: previousReading || 0,
      currentReading: currentReading || 0,
    });

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

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

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

      await fsPromises.copyFile(oldPath, newPath);

      await electricityDB.updateUploadedDoc({
        id: billId,
        docs: url,
      });

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

    if (tenants.length > 0) {
      const tenantDetails = tenants.map((tenant: any) => {
        const effectiveMoveIn = new Date(tenant.effectiveMoveIn);
        const effectiveMoveOut = new Date(tenant.effectiveMoveOut);

        const daysStayed =
          (effectiveMoveOut.getTime() - effectiveMoveIn.getTime()) /
            (1000 * 60 * 60 * 24) +
          1;

        return {
          tenantId: tenant.tenantId,
          roomId: tenant.roomId,
          daysStayed,
        };
      });

      const totalDaysByRoom = new Map<number, number>();

      for (const tenant of tenantDetails) {
        const roomId = Number(tenant.roomId);

        totalDaysByRoom.set(
          roomId,
          (totalDaysByRoom.get(roomId) || 0) + tenant.daysStayed
        );
      }

      const totalDays = tenantDetails.reduce(
        (sum: any, tenant: any) => sum + tenant.daysStayed,
        0
      );

      for (const tenant of tenantDetails) {
        let billShare = Math.ceil(
          parseFloat(((tenant.daysStayed / totalDays) * amount).toFixed(2))
        );

        if (acReading) {
          const roomAcReading = acReading.find((item: any) => Number(item.roomId) === Number(tenant.roomId))?.reading
          if (roomAcReading) {
            const roomTotalDays = totalDaysByRoom.get(Number(tenant.roomId)) || tenant.daysStayed;
            billShare += Math.ceil(
            parseFloat(((tenant.daysStayed / roomTotalDays) * (Number(roomAcReading) * Number(amountPerUnit.pricePerUnit))).toFixed(2))
            );
          }
        }

        let occupancyDetail = await occupancyDB.getByTenantIdAndClientId({
          tenantId: Number(tenant.tenantId),
          clientId
        });

        let dueDescription = getDueDescription(
          CONSTANTS.DUES_TYPES.ELECTRICITY
        );

        const referenceId = await generateLedgerReferenceId({ clientId });

        if (false == occupancyDetail) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant [${tenant.tenantId}], Tenant Stayed [${tenant.daysStayed}], Tenant Share [${billShare}], Occupancy Status [Moved Out], Tenant Electricity Due`
          );

          // let tenantInMoveOut = await moveOutDB.getMovedOutTenants({
          //   tenantId: Number(tenant.tenantId)
          // });

          let tenantInMoveOut = await moveOutDB.getMovedOutTenantsWithClientId({
            tenantId: Number(tenant.tenantId),
            clientId
          });
          if (tenantInMoveOut){
            let prevBalance = await ledgerDB.getPreviousBalance({
              tenantId: tenant.tenantId,
              clientId,
            });
            //Added Due to moveOut Due pallav
            if (Number(billShare) > 0) {
              // await moveOutDuesDB.addWithStartEndDate({
              //   tenantId: Number(tenant.tenantId),
              //   amount: billShare,
              //   moveOutId: tenantInMoveOut.id,
              //   roomId: tenantInMoveOut.roomId,
              //   propId,
              //   clientId,
              //   rentStartDate: startDate,
              //   rentEndDate: endDate,
              //   dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
              //   type: CONSTANTS.DUES_TYPES.ELECTRICITY,
              //   balance: billShare,
              //   ledgerReferenceId: referenceId,
              // });
              // await ledgerDB.add({
              //   tenantId: Number(tenant.tenantId),
              //   roomId: occupancyDetail.roomId,
              //   propId,
              //   clientId,
              //   amount: billShare,
              //   balance: billShare,
              //   referenceId,
              //   transactionId: null,
              //   type: CONSTANTS.DUES_TYPES.ELECTRICITY,
              //   rentStartDate: startDate,
              //   rentEndDate: endDate,
              //   dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
              //   description: `${dueDescription} for ${startDate} to ${endDate}`,
              // });
  
              // if (prevBalance && prevBalance < 0) {
              //   await adjustExcessPaymentsForMovedOutX({
              //     tenantId: tenant.tenantId,
              //     clientId: clientId,
              //     amountPaid: Math.abs(Number(prevBalance)),
              //     ledgerReferenceId: referenceId,
              //     markedFromSecurity: 1,
              //     recordedBy: recordedBy,
              //   });
              // }

              await AddMovedOutDuesWithoutAdjust(
                Number(tenant.tenantId),
                Number(billShare),
                CONSTANTS.DUES_TYPES.ELECTRICITY,
                tenantInMoveOut, //occupancy
                String(clientId),
                startDate,
                endDate,
                moment().format("YYYY-MM-DD HH:mm:ss"), //dueDate
                1, //rentDuration
                "Electricity Charges", //description,
                "Electricity Charges", //title
                "Electricity Charges", //dueDescription
                referenceId //referenceId
              )
            }
          } else{
            log.info(`[${C}], [${F}], Client Id [${clientId}], Tenant [${tenant.tenantId}], Tenant not found in either Occupancy or MoveOut tables, Gotcha...`)
          }
        } else {
          // let dueDescription = getDueDescription(
          //   CONSTANTS.DUES_TYPES.ELECTRICITY
          // );
          // const referenceId = await generateLedgerReferenceId({ clientId });

          if (Number(billShare) > 0) {
            let prevBalance = await ledgerDB.getPreviousBalance({
              tenantId: tenant.tenantId,
              clientId,
            });
  
            await duesDB.addWithStartEndDate({
              tenantId: Number(tenant.tenantId),
              amount: billShare,
              occupancyId: occupancyDetail.id,
              roomId: occupancyDetail.roomId,
              propId,
              clientId,
              rentStartDate: startDate,
              rentEndDate: endDate,
              dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
              type: CONSTANTS.DUES_TYPES.ELECTRICITY,
              balance: billShare,
              ledgerReferenceId: referenceId,
            });
  
            await ledgerDB.add({
              tenantId: Number(tenant.tenantId),
              roomId: occupancyDetail.roomId,
              propId,
              clientId,
              amount: billShare,
              balance: billShare,
              referenceId,
              transactionId: null,
              type: CONSTANTS.DUES_TYPES.ELECTRICITY,
              rentStartDate: startDate,
              rentEndDate: endDate,
              dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
              description: `${dueDescription} for ${startDate} to ${endDate}`,
            });
  
            if (prevBalance && prevBalance < 0) {
              await adjustExcessPayments({
                tenantId: tenant.tenantId,
                clientId: clientId,
                amountPaid: Math.abs(Number(prevBalance)),
                ledgerReferenceId: referenceId,
              });
            }
          }

          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant [${tenant.tenantId}], Tenant Stayed [${tenant.daysStayed}], Tenant Share [${billShare}], Occupancy Status [Occupied], Tenant Electricity Due`
          );
        }
        const dueName = await getDueName(CONSTANTS.DUES_TYPES.ELECTRICITY);
        await logActivity(
          req.userType!,
          Number(req.id!),
          Number(req.parentClientId!),
          Number(req.platform),
          tenant.tenantId,
          CONSTANTS.ACTIVITY_TYPES.ADD_DUES,
          billShare,
          dueName,
          null,
          null,
          null
        );
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Current Total Bill [${amount}], Room/Flat Id [${roomFlatId}], Prop Id [${propId}], Bill generated for tenants No of Tenants [${noOfTenant}]`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Current Total Bill [${amount}], Room/Flat Id [${roomFlatId}], Prop Id [${propId}], No Tenant found in room`
      );
    }

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

    if (file) {
      await removeTmpImages();
    }

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

export default electricity;
