import moment from "moment";
import log from "../config/log";

interface props {
  monthlyRent: string;
  moveInDate: string;
  rentalCycle: string;
}
interface props2 {
  dueDate: string;
  moveOutDate: string;
  monthlyRent: number;
}

export const calculateRentPerDay = ({
  monthlyRent,
  moveInDate,
  rentalCycle,
}: props) => {
  let rentalCycleDay = Number(rentalCycle);

  let days = 0;
  let rentalCycleDate = null;

  //const rentPerDay = Number(monthlyRent) / 30;
  const rentPerDay =
    Number(monthlyRent) / moment(moveInDate, "YYYY-MM-DD").daysInMonth();

  const isRentalCyclePassed = moment(moveInDate).isAfter(
    moment(rentalCycleDay, "D")
  );

  const isSameMoveInDayofRentCycle = moment(moveInDate).isSame(
    moment(rentalCycleDay, "D")
  );

  if (isRentalCyclePassed || isSameMoveInDayofRentCycle) {
    const nextMonthOfMoveInDate =
      Number(moment(moveInDate).format("M")) - Number(moment().format("M")) + 1;

    rentalCycleDate = moment(rentalCycleDay, "D").add(
      nextMonthOfMoveInDate,
      "month"
    );
  } else {
    const sameMonthOfMoveInDate =
      Number(moment(moveInDate).format("M")) - Number(moment().format("M"));

    rentalCycleDate = moment(rentalCycleDay, "D").add(
      sameMonthOfMoveInDate,
      "month"
    );
  }

  days = moment(rentalCycleDate).diff(moment(moveInDate), "days");

  const actualRent = Math.round(rentPerDay * days);

  return actualRent;
};

export const calculateRentPerDayForMoveOut = ({
  dueDate,
  monthlyRent,
  moveOutDate,
}: props2) => {
  let days = 0;

  // const rentPerDay = Number(monthlyRent) / 30;
  const rentPerDay =
    Number(monthlyRent) / moment(moveOutDate, "YYYY-MM-DD").daysInMonth();

  days = Math.abs(moment(dueDate).diff(moment(moveOutDate), "days")) + 1;

  const actualRent = Math.ceil(rentPerDay * days);

  return actualRent || 0;
};

type RentDetails = {
  month: string;
  year: number;
  rent: number;
  startDate: string; // Start date of the rent cycle
  endDate: string; // End date of the rent cycle
};

export const calculateMonthlyRent = (
  moveInDate: string,
  rentalCycleDay: number,
  monthlyRent: number
): RentDetails[] => {
  const moveIn = moment(moveInDate, "YYYY-MM-DD");
  const today = moment();

  const rents: RentDetails[] = [];
  let current = moveIn.clone();

  // Loop through each month from move-in to the current month
  //while (current.isSameOrBefore(today, "month")) { This line is causing the issue because we are comparing months, not days. So for back in move in date, it create one month advance rent.
  while (current.isSameOrBefore(today)) {
    const isFirstMonth = current.isSame(moveIn, "month");
    const isCurrentMonth = current.isSame(today, "month");

    // Calculate the cycle start and end dates for the current month
    const rentStart = isFirstMonth
      ? moveIn.clone()
      : moment(current).date(rentalCycleDay).startOf("day");
    const monthStart = moment(current).date(rentalCycleDay).startOf("day");

    const nextCycleStart = moment(monthStart)
      .add(1, "month")
      .date(rentalCycleDay)
      .startOf("day");
    const monthEnd = nextCycleStart.clone().subtract(1, "day").endOf("day");

    const totalDaysInCycle = monthEnd.diff(monthStart, "days") + 1; // Days in the rental cycle
    const dailyRent = monthlyRent / totalDaysInCycle;

    let rentForMonth: number;
    if (isFirstMonth) {
      // Prorated rent for the first cycle
      const daysRemaining = monthEnd.diff(moveIn, "days") + 1;
      rentForMonth = Math.round(dailyRent * daysRemaining);
    }
    // else if (isCurrentMonth) {
    //   // Prorated rent for the current cycle up to today
    //   const daysUntilToday = today.diff(monthStart, "days") + 1;
    //   rentForMonth = Math.round(dailyRent * daysUntilToday);
    // }
    else {
      // Full cycle rent
      rentForMonth = monthlyRent;
    }

    // Push the calculated rent for the cycle
    rents.push({
      month: monthStart.format("MMMM"),
      year: monthStart.year(),
      rent: rentForMonth,
      startDate: rentStart.format("YYYY-MM-DD"), // Start date in YYYY-MM-DD
      endDate: monthEnd.format("YYYY-MM-DD"), // End date in YYYY-MM-DD
    });

    // Move to the next rental cycle
    current = nextCycleStart.clone();
  }

  return rents;
};

export const calculateRentAsPerRentalType = (
  moveInDate: string,
  rentalCycleDay: number,
  rent: number,
  rentalType: number,

): RentDetails[] => {
  const moveIn = moment(moveInDate, "YYYY-MM-DD");
  const today = moment();

  const rents: RentDetails[] = [];
  let current = moveIn.clone();

  const cycleDurations: { [key: string]: moment.Duration } = {
    1: moment.duration(1, "month"), //monthly
    3: moment.duration(3, "months"), //quarterly
    6: moment.duration(6, "months"), //half-yearly
    12: moment.duration(12, "months"), // annually
  };

  let infLoopFailSafe = 0;

  // Loop through each month from move-in to the current month
  //while (current.isSameOrBefore(today, "month")) { This line is causing the issue because we are comparing months, not days. So for back in move in date, it create one month advance rent.
  while (current.isSameOrBefore(today)) {

    infLoopFailSafe += 1;
    if (infLoopFailSafe === 50) {
      log.info(`[CalculateRentPerDay], [calculateRentAsPerRentalType], Move-In Date [${moveInDate}], Rental Cycle [${rentalCycleDay}], Rent [${rent}], Rental Type [${rentalType}], [Error] Stuck In Infinite Loop, Breaking Loop`);

      break;
    }

    const isFirstMonth = current.isSame(moveIn, "day");

    log.info(`Current [${current}], Is First Month [${isFirstMonth}]`);

    // Calculate the cycle start and end dates for the current month
    const rentStart = isFirstMonth
      ? moveIn.clone()
      : moment(current).startOf("day");

    const cycleStart = moment(current).date(rentalCycleDay).startOf("day");
    const cycleStartOfMonth = moment(current).startOf("day");

    log.info(`Rent Start [${rentStart}], Cycle Start [${cycleStart}], Cycle Start Of Month [${cycleStartOfMonth}], `)

    let nextCycleStart = moment(cycleStartOfMonth)
      .add(cycleDurations[rentalType], "months")
      .date(rentalCycleDay)
      .startOf("day");

    log.info(`Next Cycle Start [${nextCycleStart}], Is First Month [${isFirstMonth}]`);

    if ((Number(rentalCycleDay) > Number(moment(moveInDate).date())) && isFirstMonth) {
      nextCycleStart = moment(cycleStartOfMonth).date(rentalCycleDay).startOf("day");
    }

    log.info(`After If Next Cycle [${nextCycleStart}], Is First Month [${isFirstMonth}]`);

    const cycleEnd = nextCycleStart.clone().subtract(1, "day").endOf("day");

    log.info(`Cycle End [${cycleEnd}]`);

    const totalDaysInCycle = cycleEnd.diff(cycleStart, "days") + 1; // Days in the rental cycle

    // const dailyRent = rent / totalDaysInCycle;
    const dailyRent = rent / (moment(rentStart).daysInMonth() * rentalType);

    let rentForMonth: number;
    if (isFirstMonth && (Number(rentalCycleDay) !== Number(moment(moveInDate).date()))) {
      // Prorated rent for the first cycle
      const daysRemaining = cycleEnd.diff(moveIn, "days") + 1;
      rentForMonth = Math.round(dailyRent * daysRemaining);
    }
    else {
      // Full cycle rent
      rentForMonth = rent;
    }

    // Push the calculated rent for the cycle
    rents.push({
      month: cycleStartOfMonth.format("MMMM"),
      year: cycleStartOfMonth.year(),
      rent: rentForMonth,
      startDate: rentStart.format("YYYY-MM-DD"), // Start date in YYYY-MM-DD
      endDate: cycleEnd.format("YYYY-MM-DD"), // End date in YYYY-MM-DD
    });

    // Move to the next rental cycle
    current = nextCycleStart.clone();
  }

  return rents;
};

// export const calculateRentAsPerRentalType = (
//   moveInDate: string,
//   rentalCycleDay: number,
//   rent: number,
//   rentalType: number,

// ): RentDetails[] => {
//   const moveIn = moment(moveInDate, "YYYY-MM-DD");
//   const today = moment();

//   const rents: RentDetails[] = [];
//   let current = moveIn.clone();

//   const cycleDurations: { [key: string]: moment.Duration } = {
//     1: moment.duration(1, "month"), //monthly
//     3: moment.duration(3, "months"), //quarterly
//     6: moment.duration(6, "months"), //half-yearly
//     12: moment.duration(12, "months"), // annually
//   };

//   // Loop through each month from move-in to the current month
//   //while (current.isSameOrBefore(today, "month")) { This line is causing the issue because we are comparing months, not days. So for back in move in date, it create one month advance rent.
//   while (current.isSameOrBefore(today)) {
//     const isFirstMonth = current.isSame(moveIn, "month");

//     // Calculate the cycle start and end dates for the current month
//     const rentStart = isFirstMonth
//       ? moveIn.clone()
//       : moment(current).startOf("day");

//     const cycleStart = moment(current).date(rentalCycleDay).startOf("day");
//     const cycleStartOfMonth = moment(current).startOf("day");

//     const nextCycleStart = moment(cycleStartOfMonth)
//       .add(cycleDurations[rentalType], "months")
//       .date(rentalCycleDay)
//       .startOf("day");
//     const cycleEnd = nextCycleStart.clone().subtract(1, "day").endOf("day");

//     const totalDaysInCycle = cycleEnd.diff(cycleStart, "days") + 1; // Days in the rental cycle
//     const dailyRent = rent / totalDaysInCycle;

//     let rentForMonth: number;
//     if (isFirstMonth) {
//       // Prorated rent for the first cycle
//       const daysRemaining = cycleEnd.diff(moveIn, "days") + 1;
//       rentForMonth = Math.round(dailyRent * daysRemaining);
//     }
//     else {
//       // Full cycle rent
//       rentForMonth = rent;
//     }

//     // Push the calculated rent for the cycle
//     rents.push({
//       month: cycleStartOfMonth.format("MMMM"),
//       year: cycleStartOfMonth.year(),
//       rent: rentForMonth,
//       startDate: rentStart.format("YYYY-MM-DD"), // Start date in YYYY-MM-DD
//       endDate: cycleEnd.format("YYYY-MM-DD"), // End date in YYYY-MM-DD
//     });

//     // Move to the next rental cycle
//     current = nextCycleStart.clone();
//   }

//   return rents;
// };

export const calculateRentAsPerRentalTypeForReserved = (
  moveInDate: string,
  rentalCycleDay: number,
  rent: number,
  rentalType: number,

): RentDetails[] => {

  const rents: RentDetails[] = [];

  const cycleDurations: { [key: string]: moment.Duration } = {
    1: moment.duration(1, "month"), //monthly
    3: moment.duration(3, "months"), //quarterly
    6: moment.duration(6, "months"), //half-yearly
    12: moment.duration(12, "months"), // annually
  };

  // let rentStartDate = moment();
  let rentEndDate = moment();
  if (Number(moment(moveInDate).format("DD")) < rentalCycleDay) {
    // rentStartDate = moment()
    rentEndDate = moment(moveInDate).date(rentalCycleDay).subtract(1, 'day').startOf('day');
  } else {
    // rentStartDate = moment().year(moment(moveInDate).year()).month(moment(moveInDate).month()).date(rentalCycleDay).startOf('day');
    // rentEndDate = moment().year(moment(moveInDate).year()).month(moment(moveInDate).add(1, 'month').month()).date(rentalCycleDay).subtract(1, 'day').startOf('day');
    rentEndDate = moment(moveInDate).add(1, 'month').date(rentalCycleDay).subtract(1, 'day').startOf('day');
  }

  if (Number(moment(moveInDate).format("DD")) != rentalCycleDay) {
    const totalDaysInFirstCycle = rentEndDate.diff(moment(moveInDate).startOf('day'), "days") + 1; // Days in first cycle
    const totalDaysInOneCycle = moment(moveInDate).daysInMonth(); // Days in rental cycle
    const monthlyRent = Math.ceil(rent/rentalType);
    const dailyRent = Math.ceil(monthlyRent/Number(totalDaysInOneCycle));

    const rentToBePaid = dailyRent * totalDaysInFirstCycle;

    rents.push({
      month: moment(moveInDate).format("MMMM"),
      year: moment(moveInDate).year(),
      rent: rentToBePaid,
      startDate: moment(moveInDate).format("YYYY-MM-DD"),
      endDate: moment(rentEndDate).format("YYYY-MM-DD"),
    });
  }

  return rents;
};

export const calculateRentAsPerRentalTypeForReservedRentBooking = (
  moveInDate: string,
  rentalCycleDay: number,
  rent: number,
  rentalType: number,

): RentDetails[] => {

  const rents: RentDetails[] = [];

  const cycleDurations: { [key: string]: moment.Duration } = {
    1: moment.duration(1, "month"), //monthly
    3: moment.duration(3, "months"), //quarterly
    6: moment.duration(6, "months"), //half-yearly
    12: moment.duration(12, "months"), // annually
  };

  // let rentStartDate = moment();
  let rentEndDate = moment();
  if (Number(moment(moveInDate).format("DD")) < rentalCycleDay) {
    // rentStartDate = moment()
    rentEndDate = moment(moveInDate).date(rentalCycleDay).subtract(1, 'day').startOf('day');
  } else {
    // rentStartDate = moment().year(moment(moveInDate).year()).month(moment(moveInDate).month()).date(rentalCycleDay).startOf('day');
    // rentEndDate = moment().year(moment(moveInDate).year()).month(moment(moveInDate).add(1, 'month').month()).date(rentalCycleDay).subtract(1, 'day').startOf('day');
    rentEndDate = moment(moveInDate).add(1, 'month').date(rentalCycleDay).subtract(1, 'day').startOf('day');
  }

  if (Number(moment(moveInDate).format("DD")) != rentalCycleDay) {
    const totalDaysInFirstCycle = rentEndDate.diff(moment(moveInDate).startOf('day'), "days") + 1; // Days in first cycle
    const totalDaysInOneCycle = moment(moveInDate).daysInMonth(); // Days in rental cycle
    const monthlyRent = Math.ceil(rent/rentalType);
    const dailyRent = Math.ceil(monthlyRent/Number(totalDaysInOneCycle));

    const rentToBePaid = dailyRent * totalDaysInFirstCycle;

    rents.push({
      month: moment(moveInDate).format("MMMM"),
      year: moment(moveInDate).year(),
      rent: rentToBePaid,
      startDate: moment(moveInDate).format("YYYY-MM-DD"),
      endDate: moment(rentEndDate).format("YYYY-MM-DD"),
    });
  } else {
    const rentToBePaid = rent;

    rents.push({
      month: moment(moveInDate).format("MMMM"),
      year: moment(moveInDate).year(),
      rent: rentToBePaid,
      startDate: moment(moveInDate).format("YYYY-MM-DD"),
      endDate: moment(rentEndDate).format("YYYY-MM-DD"),
    });
  }

  return rents;
};

export const calculateAgreementEndRent = (
  moveInDate: string,
  rentalCycleDay: number,
  rent: number,
  rentalType: number,
  agreementPeriod: number,
): RentDetails [] => {
  let rents: RentDetails[] = [];

  const agreementEndDate = moment(moveInDate).add(agreementPeriod, "months").format("YYYY-MM-DD");

  if (moment(agreementEndDate).isBefore(moment(), "day")) {
    //Agreement end date is in past, hence no advance rent
    return [];
  } else if (agreementPeriod < 2) return [];

  let slotStartDate = moment(moveInDate).format("YYYY-MM-DD");

  let fullMonth = true;
  let slotEndDate = moment(moveInDate).date(rentalCycleDay).add(rentalType, "month").subtract(1, "day").format("YYYY-MM-DD");

  if (Number(rentalCycleDay) > moment(moveInDate).date()) {
    slotEndDate = moment(moveInDate).date(rentalCycleDay).subtract(1, "day").format("YYYY-MM-DD");
  }

  if (moment(slotEndDate).isAfter(moment(agreementEndDate), "day")) {
    slotEndDate = moment(agreementEndDate).subtract(1, "day").format("YYYY-MM-DD");
    fullMonth = false;
  } 

  const daysStay = Math.abs(moment(slotStartDate).diff(moment(slotEndDate), "days")) + 1;

  const perDayRent = rent / (moment(slotStartDate).daysInMonth() * rentalType);
  let slotRent = Math.ceil(daysStay * perDayRent);

  if ((Number(rentalCycleDay) === Number(moment(slotStartDate).date())) && fullMonth) {
    slotRent = rent;
  }
  
  rents.push({
    month: moment(slotStartDate).format("MMMM"),
    year: moment(slotStartDate).year(),
    rent: slotRent,
    startDate: moment(slotStartDate).format("YYYY-MM-DD"),
    endDate: moment(slotEndDate).format("YYYY-MM-DD"),
  });

  slotStartDate = moment(slotEndDate).add(1, "day").format("YYYY-MM-DD");


  let infLoopFailSafe = 0;

  while (moment(slotStartDate).isBefore(agreementEndDate, "day")) {
    infLoopFailSafe += 1;
    if (infLoopFailSafe === 50) {
      log.info(`[CalculateRentPerDay], [calculateAgreementEndRent], Move-In Date [${moveInDate}], Rental Cycle [${rentalCycleDay}], Rent [${rent}], Rental Type [${rentalType}], Agreement Period [${agreementPeriod}], [Error] Stuck In Infinite Loop, Breaking Loop`);

      break;
    }

    slotEndDate = moment(slotStartDate).add(rentalType, "month").date(rentalCycleDay).subtract(1, "day").format("YYYY-MM-DD");

    let slotRent = rent;

    if (moment(slotEndDate).isAfter(moment(agreementEndDate), "day")) slotEndDate = moment(agreementEndDate).subtract(1, "day").format("YYYY-MM-DD");

    if (moment(slotEndDate).isSame(moment(agreementEndDate).subtract(1, "day"), "day")) {
      const daysStay = Math.abs(moment(slotStartDate).diff(moment(slotEndDate), "days")) + 1;
      const perDayRent = rent / (moment(slotStartDate).daysInMonth() * rentalType);
      slotRent = Math.ceil(daysStay * perDayRent);
    }
    
    rents.push({
      month: moment(slotStartDate).format("MMMM"),
      year: moment(slotStartDate).year(),
      rent: slotRent,
      startDate: moment(slotStartDate).format("YYYY-MM-DD"),
      endDate: moment(slotEndDate).format("YYYY-MM-DD"),
    });

    slotStartDate = moment(slotEndDate).add(1, "day").format("YYYY-MM-DD");
  }

  rents = rents.slice(-2);

  let finalRents: any = [];

  if (rents && rents.length > 0) {
    for(let rent of rents) {
      if (moment(rent.startDate).isSameOrBefore(moment(), "day")) {
        continue;
      } else {
        finalRents.push(rent);
      }
    }
  }

  return finalRents;
};

// Example Usage
// const moveInDate = "2024-09-15"; // Move-in date
// const rentalCycleDay = 7; // Rental cycle starts on the 7th of each month
// const monthlyRent = 30000;

// const rents = calculateMonthlyRent(moveInDate, rentalCycleDay, monthlyRent);
// console.log(rents);

//export default calculateRentPerDay;
