import log from "../config/log";
import tenantDB from "../models/tenant.model";
import CONSTANTS from "../config/constants";
import occupancyDB from "../models/occupancy.model";
import propertyDB from "../models/property.model";
import roomDB from "../models/room.model";
import duesDB from "../models/dues.model";
import generateTransId from "./generateTransId";
import createReceiptMultipleDues from "./createReceiptMultipleDues";
import flatDB from "../models/flat.model";
import moment from "moment";
import getModeName from "./getModeName";
import transactionDB from "../models/transaction.model";
import sendNotification from "./sendNotification";
import sendSMS from "./sendSMS";
import clientDB from "../models/client.model";
import ledgerDB from "../models/ledger.model";
import getDueDescription from "./getDueDescription";
import addTransactions from "./addTransaction";

const adjustInitialSettlement = async ({
  tenantId,
  clientId,
  ledgerReferenceId,
  amountPaid,
  propId,
  type,
  mode,
  recordedBy,
}: any) => {
  try {
    const tenant = await tenantDB.getById({ id: tenantId });
    const client = await clientDB.getById({ id: clientId });
    // const occupancy = await occupancyDB.getByTenantId({ tenantId });
    let occupancy = await occupancyDB.getByTenantIdAndClientId({
      clientId: clientId,
      tenantId: tenantId,
    });
    const property = await propertyDB.getById({ id: occupancy.propId });
    const room = await roomDB.getById({ id: occupancy.roomId });
    const postAffixForDesc =
      type === CONSTANTS.INITIAL_SETTLEMENT_TYPE.TOKEN_AMOUNT ? "[Token]" : "";
    let isTransactionRecorded = false;
    let amtPaid = amountPaid;
    let duesStatsArr = [];
    let dueStats = {};
    let excessDueStats = {};
    let excessDueflag = false;
    // let allDues = await duesDB.getByTenantIdAndLedgerReferenceId({
    //   tenantId,
    //   ledgerReferenceId,
    // });
    let allDues = await duesDB.getByTenantIdAndPropId({
      tenantId,
      propId,
    });

    let count = 1;
    let lengthOfDues = allDues === false ? 0 : allDues.length;
    let security = null;
    if (allDues !== false) {
      security = await allDues.find(
        (due: any) => due.type === CONSTANTS.DUES_TYPES.SECURITY
      );
    }

    let transId = null;

    let dueDescription = "";
    if (allDues !== false) {
      if (security && security.balance > 0) {
        let due = security;
        dueDescription = getDueDescription(due.type);
        if (security.balance <= amtPaid) {
          if (count == lengthOfDues) {
            transId = await addTransactions({
              due: due,
              clientId: clientId,
              tenantId: tenantId,
              amount: amountPaid,
              description: `Payment for Initial security deposit`,
              mode: mode,
              recordedBy: recordedBy,
              duesStatsArr: [
                { title: dueDescription, amount: due.balance }, 
                { title: "Excess Payment", amount: Math.abs(amtPaid - due.balance) },
              ]
            });
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -due.balance,
              balance: -(amtPaid - due.balance),
              // referenceId: ledgerReferenceId,
              referenceId: due.ledgerReferenceId,
              transactionId: transId,
              type: due.type,
              rentStartDate: due.rentStartDate,
              rentEndDate: due.rentEndDate,
              dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
              description: dueDescription + " " + postAffixForDesc,
              title: due?.title || null,
            });
            //Creating DueStats for Recipt
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
            excessDueflag = true;
            excessDueStats = {
              title: "Excess Payment",
              amount: Math.abs(amtPaid - due.balance),
            };
            amtPaid = 0;
          } else {
            transId = await addTransactions({
              due: due,
              clientId: clientId,
              tenantId: tenantId,
              amount: due.balance,
              description: `Payment for Initial security deposit`,
              mode: mode,
              recordedBy: recordedBy,
              duesStatsArr: [
                { title: dueDescription, amount: due.balance }, 
              ]
            });
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -due.balance,
              balance: 0,
              // referenceId: ledgerReferenceId,
              referenceId: due.ledgerReferenceId,
              transactionId: transId,
              type: due.type,
              rentStartDate: due.rentStartDate,
              rentEndDate: due.rentEndDate,
              dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
              description: dueDescription + " " + postAffixForDesc,
              title: due?.title || null,
            });
            amtPaid -= due.balance;
            //Creating DueStats for Recipt
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
          }
          await duesDB.removeDue({ id: due.id });

          isTransactionRecorded = true;
        } else if (security.balance > amtPaid) {
          transId = await addTransactions({
            due: due,
            clientId: clientId,
            tenantId: tenantId,
            amount: amtPaid,
            description: `Partial Payment for Initial security deposit`,
            mode: mode,
            recordedBy: recordedBy,
            duesStatsArr: [
              { title: dueDescription, amount: amtPaid }, 
            ]
          });
          await ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId: occupancy.clientId,
            amount: -amtPaid,
            balance: due.balance - amtPaid,
            // referenceId: ledgerReferenceId,
            referenceId: due.ledgerReferenceId,
            transactionId: transId,
            type: due.type,
            rentStartDate: due.rentStartDate,
            rentEndDate: due.rentEndDate,
            dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
            description: dueDescription + " " + postAffixForDesc,
            title: due?.title || null,
          });

          //Creating DueStats for Recipt
          dueStats = { title: dueDescription, amount: amtPaid };
          duesStatsArr.push(dueStats);

          await duesDB.updateBalance({
            id: due.id,
            balance: due.balance - amtPaid,
          });
          amtPaid = 0;
          isTransactionRecorded = true;
        }
        count++;
      }

      let discountAmount = 0;

      if (amtPaid != 0) {
        for (const due of allDues) {
          if (amtPaid == 0) break;
          if (due.type === CONSTANTS.DUES_TYPES.SECURITY) {
            continue;
          }
          dueDescription = getDueDescription(due.type);
          let dueName = getDueDescription(due.type);
          if (due.type === CONSTANTS.DUES_TYPES.OTHER) {
            dueName = due.title;
            dueDescription = due.title;
          }
          dueDescription = `${dueDescription} for ${moment(due.dueDate).format(
            "YYYY-MM-DD"
          )}`;
          if (amtPaid <= due.balance) {
            transId = await addTransactions({
              due: due,
              clientId: clientId,
              tenantId: tenantId,
              amount: amtPaid,
              description: due.balance == amtPaid ? `Payment for ${dueName}` : `Partial Payment for ${dueName}`,
              mode: mode,
              recordedBy: recordedBy,
              duesStatsArr: due.balance == amtPaid 
              ? due.discount > 0
              ? [{ title: dueDescription, amount: amtPaid }, { title: "Discount", amount: -due.discount }, ]
              : [{ title: dueDescription, amount: amtPaid },]
              : [{ title: dueDescription, amount: amtPaid },],
            });
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -amtPaid,
              balance: due.balance - amtPaid,
              referenceId: due.ledgerReferenceId,
              transactionId: transId,
              type: due.type,
              rentStartDate: due.rentStartDate,
              rentEndDate: due.rentEndDate,
              dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
              description: due.discount > 0 
                ? amtPaid === due.balance
                  ? `${dueDescription} ${postAffixForDesc}, Discount of ₹${due.discount} given for this due` 
                  : dueDescription + " " + postAffixForDesc
                : dueDescription + " " + postAffixForDesc,
              discount: due.discount,
              title: due?.title || null,
            });
            if (amtPaid == due.balance) {
              await duesDB.removeDue({ id: due.id });
              //Creating DueStats for Recipt
              dueStats = { title: dueDescription, amount: due.balance + due.discount };
              duesStatsArr.push(dueStats);
            } else {
              await duesDB.updateBalance({
                id: due.id,
                balance: due.balance - amtPaid,
              });
              //Creating DueStats for Recipt
              dueStats = { title: dueDescription, amount: amtPaid };
              duesStatsArr.push(dueStats);
            }
            amtPaid = 0;
          } else {
            if (count == lengthOfDues) {
              transId = await addTransactions({
                due: due,
                clientId: clientId,
                tenantId: tenantId,
                amount: amtPaid,
                description: `Payment for ${dueName}`,
                mode: mode,
                recordedBy: recordedBy,
                duesStatsArr: due.discount > 0 
                  ? [{ title: dueDescription, amount: due.balance }, { title: "Discount", amount: -due.discount }, { title: "Excess Payment", amount: Math.abs(amtPaid - due.balance) },]
                  : [{ title: dueDescription, amount: due.balance }, { title: "Excess Payment", amount: Math.abs(amtPaid - due.balance) },],
              });
              amtPaid -= due.balance;
              await ledgerDB.add({
                tenantId,
                roomId: occupancy.roomId,
                propId: occupancy.propId,
                clientId: occupancy.clientId,
                amount: -due.balance,
                balance: -amtPaid,
                referenceId: due.ledgerReferenceId,
                transactionId: transId,
                type: due.type,
                rentStartDate: due.rentStartDate,
                rentEndDate: due.rentEndDate,
                dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
                description: due.discount > 0 
                  ? `${dueDescription} ${postAffixForDesc}, Discount of ₹${due.discount} given for this due` 
                  : dueDescription + " " + postAffixForDesc,
                discount: due.discount,
                title: due?.title || null,
              });
              //Creating DueStats for Recipt
              dueStats = { title: dueDescription, amount: due.balance + due.discount };
              duesStatsArr.push(dueStats);
              //Exesss Due
              excessDueflag = true;
              excessDueStats = {
                title: "Excess Payment",
                amount: Math.abs(amtPaid),
              };
              //break;
            } else {
              transId = await addTransactions({
                due: due,
                clientId: clientId,
                tenantId: tenantId,
                amount: due.balance,
                description: `Payment for ${dueName}`,
                mode: mode,
                recordedBy: recordedBy,
                duesStatsArr: due.discount > 0 
                  ? [{ title: dueDescription, amount: due.balance }, { title: "Discount", amount: -due.discount }]
                  : [{ title: dueDescription, amount: due.balance }, ],
              });
              await ledgerDB.add({
                tenantId,
                roomId: occupancy.roomId,
                propId: occupancy.propId,
                clientId: occupancy.clientId,
                amount: -due.balance,
                balance: 0,
                referenceId: due.ledgerReferenceId,
                transactionId: transId,
                type: due.type,
                rentStartDate: due.rentStartDate,
                rentEndDate: due.rentEndDate,
                dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
                description: due.discount > 0 
                  ? `${dueDescription} ${postAffixForDesc}, Discount of ₹${due.discount} given for this due` 
                  : dueDescription + " " + postAffixForDesc,
                discount: due.discount,
                title: due?.title || null,
              });
              amtPaid -= due.balance;
              //Creating DueStats for Recipt
              dueStats = { title: dueDescription, amount: due.balance + due.discount };
              duesStatsArr.push(dueStats);
            }
            await duesDB.removeDue({ id: due.id });
            if (count == lengthOfDues) break;
          }
          isTransactionRecorded = true;
          count++;
          discountAmount += due.discount;
        }
        if (discountAmount > 0) {
          dueStats = { title: "Discount", amount: -discountAmount };
          duesStatsArr.push(dueStats);
        }
      }
    } else {
      if (
        type === CONSTANTS.INITIAL_SETTLEMENT_TYPE.TOKEN_AMOUNT ||
        type === CONSTANTS.INITIAL_SETTLEMENT_TYPE.EXCESS_AMOUNT
      ) {
        dueStats = { title: "Token Payment", amount: amountPaid };
        duesStatsArr.push(dueStats);
        transId = await addTransactions({
          due: null,
          clientId: clientId,
          tenantId: tenantId,
          amount: amtPaid,
          description: "Token amount paid",
          mode: mode,
          recordedBy: recordedBy,
          duesStatsArr: [
            { title: dueDescription, amount: amtPaid }, 
          ]
        });
        await ledgerDB.add({
          tenantId,
          roomId: occupancy.roomId,
          propId: occupancy.propId,
          clientId: occupancy.clientId,
          amount: -amtPaid,
          // amount: 0,
          balance: -amtPaid,
          referenceId: ledgerReferenceId,
          transactionId: transId,
          type: CONSTANTS.DUES_TYPES.OTHER,
          rentStartDate: null,
          rentEndDate: null,
          dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
          description: "Token amount paid",
        });
      }
    }

    // if (transId !== null) {
    //   let logo = client?.logo
    //     ? process.env.RS_LOGO_URI + client?.id + "/" + client?.logo
    //     : String(process.env.RS_DEFAULT_LOGO_URI);
    //   if (excessDueflag) {
    //     duesStatsArr.push(excessDueStats);
    //   }
    //   let flatName = "";
    //   if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
    //     const { name } = await flatDB.getById({ id: occupancy.flatId });
    //     flatName = name;
    //   } else {
    //     flatName =
    //       occupancy.floor === "G" ? "Ground Floor" : "Floor " + occupancy.floor;
    //   }
    //   const receipt = await createReceiptMultipleDues({
    //     // title: transTitle,
    //     title: `${transId}_${transGId}`,
    //     roomNum: room.roomNum,
    //     propName: property.name,
    //     dueDate: moment(allDues[0]?.dueDate).format("MMM, YYYY") || null,
    //     mode: getModeName(mode),
    //     transGId,
    //     paidDate: moment().format("DD MMM, YYYY"),
    //     month:
    //       moment(allDues[0]?.dueDate).format("MMM, YYYY") ||
    //       moment().format("MMM, YYYY"),
    //     tenantName: tenant?.name || "",
    //     amount: Number(amountPaid || 0),
    //     address: `${property?.address}`,
    //     landlord: client?.name || "",
    //     landlordNumber: client?.mobile || "",
    //     logo: logo,
    //     "landlord-pan": "",
    //     occupancy,
    //     dueStats: duesStatsArr,
    //     flatName,
    //     propType: property.type,
    //   });
    //   await transactionDB.updateReceipt({
    //     id: transId,
    //     receipt,
    //   });
    //   receipts.push({
    //     name: transTitle,
    //     receipt,
    //     paidDate: moment().format("DD MMM, YYYY"),
    //     amount: Number(amtPaid || 0),
    //     mode: mode,
    //     transGId,
    //   });
    // }
    if (isTransactionRecorded && amtPaid > 0) {
      let isNotiSent = false;
      if (tenant.regId) {
        isNotiSent = await sendNotification({
          title: `Received ₹${amountPaid} for due payments`,
          message: `Thank you for paying for your due payments timely.`,
          regId: tenant.regId,
          userId: tenant.id,
          userType: CONSTANTS.USER_TYPE.TENANT,
          notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.EXCESS_PAYMENT,
          clientId: client.id,
        });
      }

      const msg = CONSTANTS.MSG.DUE_PAID.replace(
        "{#var#}",
        tenant.name
      ).replace("{#var2#}", amountPaid.toString());

      const isSent = await sendSMS(
        tenant.mobile,
        msg,
        CONSTANTS.SMS_TEMPLATE_IDS.DUE_PAID
      );
    }

    if (!isTransactionRecorded) {
      return "No Dues Found";
    }

    return "Transaction Recorded Successfully";
  } catch (error: any) {
    return `Error: ${error?.message || error}`;
  }
};

export const propertySwitchExcessSettlement = async ({
  tenantId,
  clientId,
  ledgerReferenceId,
  amountPaid,
  propId,
  type,
}: any) => {
  try {
    const occupancy = await occupancyDB.getByTenantId({ tenantId });
    const postAffixForDesc =
      type === CONSTANTS.INITIAL_SETTLEMENT_TYPE.TOKEN_AMOUNT ? "[Token]" : "";
    let amtPaid = amountPaid;
    let duesStatsArr = [];
    let dueStats = {};
    let excessDueStats = {};
    let excessDueflag = false;
    // let allDues = await duesDB.getByTenantIdAndLedgerReferenceId({
    //   tenantId,
    //   ledgerReferenceId,
    // });
    let allDues = await duesDB.getByTenantIdAndPropId({
      tenantId,
      propId,
    });

    let count = 1;
    let lengthOfDues = allDues === false ? 0 : allDues.length;
    let security = null;
    if (allDues !== false) {
      security = await allDues.find(
        (due: any) => due.type === CONSTANTS.DUES_TYPES.SECURITY
      );
    }
    let receipts = [];

    const transGId: string = await generateTransId();
    const transTitle: string = "Payment";

    let transId = null;

    let dueDescription = "";
    if (allDues !== false) {
      if (security && security.balance > 0) {
        let due = security;
        dueDescription = getDueDescription(due.type);
        if (security.balance <= amtPaid) {
          if (count == lengthOfDues) {
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -due.balance,
              balance: -(amtPaid - due.balance),
              referenceId: ledgerReferenceId,
              transactionId: transId,
              type: due.type,
              rentStartDate: null,
              rentEndDate: null,
              dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
              description: dueDescription + " " + postAffixForDesc,
              title: due?.title || null,
            });
            //Creating DueStats for Recipt
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
            excessDueflag = true;
            excessDueStats = {
              title: "Excess Payment",
              amount: Math.abs(amtPaid - due.balance),
            };
            amtPaid = 0;
          } else {
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -due.balance,
              balance: 0,
              referenceId: ledgerReferenceId,
              transactionId: transId,
              type: due.type,
              rentStartDate: null,
              rentEndDate: null,
              dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
              description: dueDescription + " " + postAffixForDesc,
              title: due?.title || null,
            });
            amtPaid -= due.balance;
            //Creating DueStats for Recipt
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
          }
          await duesDB.removeDue({ id: due.id });
        } else if (security.balance > amtPaid) {
          await ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId: occupancy.clientId,
            amount: -amtPaid,
            balance: due.balance - amtPaid,
            referenceId: ledgerReferenceId,
            transactionId: transId,
            type: due.type,
            rentStartDate: null,
            rentEndDate: null,
            dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
            description: dueDescription + " " + postAffixForDesc,
            title: due?.title || null,
          });

          //Creating DueStats for Recipt
          dueStats = { title: dueDescription, amount: amtPaid };
          duesStatsArr.push(dueStats);

          await duesDB.updateBalance({
            id: due.id,
            balance: due.balance - amtPaid,
          });
          amtPaid = 0;
        }
        count++;
      }

      if (amtPaid != 0) {
        for (const due of allDues) {
          if (amtPaid == 0) break;
          if (due.type === CONSTANTS.DUES_TYPES.SECURITY) {
            continue;
          }
          dueDescription = getDueDescription(due.type);
          dueDescription = `${dueDescription} for ${moment(due.dueDate).format(
            "YYYY-MM-DD"
          )}`;
          if (amtPaid <= due.balance) {
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -amtPaid,
              balance: due.balance - amtPaid,
              referenceId: ledgerReferenceId,
              transactionId: transId,
              type: due.type,
              rentStartDate: null,
              rentEndDate: null,
              dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
              description: dueDescription + " " + postAffixForDesc,
              title: due?.title || null,
            });
            if (amtPaid == due.balance) {
              await duesDB.removeDue({ id: due.id });
              //Creating DueStats for Recipt
              dueStats = { title: dueDescription, amount: due.balance };
              duesStatsArr.push(dueStats);
            } else {
              await duesDB.updateBalance({
                id: due.id,
                balance: due.balance - amtPaid,
              });
              //Creating DueStats for Recipt
              dueStats = { title: dueDescription, amount: amtPaid };
              duesStatsArr.push(dueStats);
            }
            amtPaid = 0;
          } else {
            if (count == lengthOfDues) {
              amtPaid -= due.balance;
              await ledgerDB.add({
                tenantId,
                roomId: occupancy.roomId,
                propId: occupancy.propId,
                clientId: occupancy.clientId,
                amount: -due.balance,
                balance: -amtPaid,
                referenceId: ledgerReferenceId,
                transactionId: transId,
                type: due.type,
                rentStartDate: null,
                rentEndDate: null,
                dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
                description: dueDescription + " " + postAffixForDesc,
                title: due?.title || null,
              });
              //Creating DueStats for Recipt
              dueStats = { title: dueDescription, amount: due.balance };
              duesStatsArr.push(dueStats);
              //Exesss Due
              excessDueflag = true;
              excessDueStats = {
                title: "Excess Payment",
                amount: Math.abs(amtPaid),
              };
              //break;
            } else {
              await ledgerDB.add({
                tenantId,
                roomId: occupancy.roomId,
                propId: occupancy.propId,
                clientId: occupancy.clientId,
                amount: -due.balance,
                balance: 0,
                referenceId: ledgerReferenceId,
                transactionId: transId,
                type: due.type,
                rentStartDate: null,
                rentEndDate: null,
                dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
                description: dueDescription + " " + postAffixForDesc,
                title: due?.title || null,
              });
              amtPaid -= due.balance;
              //Creating DueStats for Recipt
              dueStats = { title: dueDescription, amount: due.balance };
              duesStatsArr.push(dueStats);
            }
            await duesDB.removeDue({ id: due.id });
          }
          count++;
        }
      }
    } else {
      if (
        type === CONSTANTS.INITIAL_SETTLEMENT_TYPE.TOKEN_AMOUNT ||
        type === CONSTANTS.INITIAL_SETTLEMENT_TYPE.EXCESS_AMOUNT
      ) {
        dueStats = { title: "Token Payment", amount: amountPaid };
        duesStatsArr.push(dueStats);
        await ledgerDB.add({
          tenantId,
          roomId: occupancy.roomId,
          propId: occupancy.propId,
          clientId: occupancy.clientId,
          amount: -amtPaid,
          // amount: 0,
          balance: -amtPaid,
          referenceId: ledgerReferenceId,
          transactionId: transId,
          type: CONSTANTS.DUES_TYPES.OTHER,
          rentStartDate: null,
          rentEndDate: null,
          dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
          description: "Token amount paid",
        });
      }
    }
    return "Transaction Recorded Successfully";
  } catch (error: any) {
    return `Error: ${error?.message || error}`;
  }
};

export default adjustInitialSettlement;
