import moment from "moment";
import CONSTANTS from "../config/constants";
import log from "../config/log";
import clientDB from "../models/client.model";
import ledgerDB from "../models/ledger.model";
import moveOutDuesDB from "../models/moveOutDues.model";
import propertyDB from "../models/property.model";
import settingsDB from "../models/settings.model";
import tenantDB from "../models/tenant.model";
import getModeName from "./getModeName";
import { logActivity } from "./logActivity";
import moveOutDB from "../models/moveOut.model";
import roomDB from "../models/room.model";
import transactionDB from "../models/transaction.model";
import generateTransId from "./generateTransId";
import flatDB from "../models/flat.model";
import { generateRentReciept } from "./generateRentReciept";
import jwt from "jsonwebtoken";
import axios from "axios";
import requestDB from "../models/request.model";
import hiddenDuesDB from "../models/hiddenDues.model";

const refundExcessPayment = async (
  tenantId: number,
  amount: number,
  mode: number,
  paidDate: string,
  recordedBy: string,
  clientId: number,
  req: any,
  utrNum: any = null,
  accountName: any = null,
  accountNumber: any = null,
  transferId: any= null,
  gateway: number = CONSTANTS.PAYMENT_GATEWAY.CASHFREE
) => {
  const U = "Refund Excess Payment";
  const F = "refundExcessPayment";

  try {

    log.info(
      `[${U}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Mode [${mode}], Paid Date [${paidDate}], Recorded By [${recordedBy}]`
    );

    let amtReturn = Math.abs(amount);


    const modeVal = getModeName(Number(mode));

    const allDues = await moveOutDuesDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });

    const securityAdjustEntry = await ledgerDB.getLastEntry({
      tenantId,
      clientId,
    });

    if (allDues && allDues.length > 0) {

      if (securityAdjustEntry !== false) {
        await ledgerDB.removeById({
          id: securityAdjustEntry.id,
        });
      }

      for (let due of allDues) {
        // if (amtReturn === 0) break;

        await moveOutDuesDB.removeDue({
          id: due.id,
        });

        await ledgerDB.add({
          tenantId,
          roomId: due.roomId,
          propId: due.propId,
          clientId: due.clientId,
          amount: -Math.abs(due.balance),
          balance: 0,
          referenceId: due.ledgerReferenceId,
          transactionId: null,
          type: due.type,
          rentStartDate: due.rentStartDate,
          rentEndDate: due.rentEndDate,
          dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
          description: due.discount > 0
            ? `Settled while paying back security deposit and any excess amount, Discount of ₹${due.discount} given for this due`
            : `Settled while paying back security deposit and any excess amount`,
          discount: due.discount,
          title: due?.title || null,
          subType: CONSTANTS.LEDGER_SUBTYPES.MARKED_FROM_SECURITY,
        });

      }

      await ledgerDB.add({
        tenantId: securityAdjustEntry.tenantId,
        roomId: securityAdjustEntry.roomId,
        propId: securityAdjustEntry.propId,
        clientId: clientId,
        amount: securityAdjustEntry.amount,
        balance: securityAdjustEntry.balance,
        referenceId: securityAdjustEntry.referenceId,
        transactionId: securityAdjustEntry.transactionId,
        type: securityAdjustEntry.type,
        rentStartDate: securityAdjustEntry.rentStartDate,
        rentEndDate: securityAdjustEntry.rentEndDate,
        description: securityAdjustEntry.description,
        dueDate: securityAdjustEntry.dueDate,
      });
    }

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

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

    const room = await roomDB.getById({ id: occupancy.roomId });
    const client = await clientDB.getById({ id: occupancy.clientId });
    const tenant = await tenantDB.getById({ id: tenantId });

    let isGstEnabled = property?.isGstEnabled || 0;
    let invoiceNo = property?.invoiceNo || null;
    let invoiceNoPrefix = property?.invoiceNoPrefix || null;
    let gstNo = property?.gstNo || null;
    let businessName = property?.ownerName || ""
    let transactionInvoiceNo = ""
    let prefix = "";
    if (0 != isGstEnabled && 0 != invoiceNo) {
      let getGstTransactions = await transactionDB.getByGstNo({ gstNo });
      if (false == getGstTransactions) {
        if (null != invoiceNoPrefix) {
          prefix = invoiceNoPrefix;
        }
        transactionInvoiceNo = `${prefix}${invoiceNo}`;
      } else {
        invoiceNo = getGstTransactions?.invoiceNo;
        if (null != invoiceNoPrefix) {
          prefix = invoiceNoPrefix;
          invoiceNo = invoiceNo.replace(prefix, "");
          transactionInvoiceNo = `${prefix}${String(Number(invoiceNo) + 1)}`;
        } else {
          transactionInvoiceNo = String(Number(invoiceNo) + 1);
        }
      }
      businessName = property?.businessName || "";
    } else {
      isGstEnabled = 0;
    }

    let transId = null;
    if (amtReturn > 0) {
      const transGId: string = await generateTransId();
      transId = await transactionDB.add({
        gId: transGId,
        clientId: occupancy.clientId,
        tenantId: occupancy.tenantId,
        roomId: occupancy.roomId,
        propId: occupancy.propId,
        amount: -amount,
        name: "Excess payment or security returned",
        type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
        transactionFor: CONSTANTS.TRANSACTION_FOR.SETTLEMENT,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
        dueDate: moment(occupancy.moveOutDate).format("YYYY-MM-DD"),
        receipt: "",
        mode: mode,
        collectionDate: moment(paidDate).format("YYYY-MM-DD HH:mm:ss"),
        propName: property.name,
        roomNum: room.roomNum,
        ledgerReferenceId: securityAdjustEntry?.referenceId || null,
        recordedBy: recordedBy,
        title: null,
        isFinanciallyApplicable: 1,
        paymentAccountNo : accountName,
        paymentAccountName : accountNumber,
      });
      transactionDB.updateUtrOnPayOut({
        id: transId, 
        utrNo: utrNum, 
        settleStatus: CONSTANTS.PAYOUT_STATUS.COMPLETED,
        settledOn: moment().format("YYYY-MM-DD HH:mm:ss"),
        bankRefNum: transferId,
        gateway: gateway,
      });

      let settings = await settingsDB.getByClientIdAndPropId({
        clientId,
        propId: occupancy.propId,
      });

      let logo = settings?.logo
        ? process.env.RS_LOGO_URI + client?.id + "/" + occupancy?.propId + "/" + settings?.logo
        : client?.logo
          ? process.env.RS_LOGO_URI + client?.id + "/" + client?.logo
          : String(process.env.RS_DEFAULT_LOGO_URI);

      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 generateRentReciept({
        title: `${transId}_${transGId}`,
        roomNum: room.roomNum,
        propName: property.name,
        dueDate: moment(occupancy?.moveOutDate).format("MMM, YYYY") || null,
        mode: getModeName(mode),
        transGId,
        paidDate: moment(paidDate).format("DD MMM, YYYY HH:mm:ss"),
        month:
          moment(occupancy?.moveOutDate).format("MMM, YYYY") ||
          moment().format("MMM, YYYY"),
        tenantName: tenant?.name || "",
        amount: Number(amtReturn) || 0,
        address: `${property?.address}`,
        landlord: businessName || "",
        landlordNumber: property?.ownerMobile || "",
        logo: logo,
        "landlord-pan": "",
        occupancy,
        dueStats: [{ title: "Excess Payment/Security returned", amount: amount }],
        flatName,
        propType: property.type,
        isGstEnabled,
        transactionInvoiceNo,
        gstNo,
        recordedBy
      });
      if (0 != isGstEnabled && 0 != invoiceNo) {
        await transactionDB.updateReceiptWithInvoice({ id: transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
      } else {
        await transactionDB.updateReceipt({
          id: transId,
          receipt,
        });
      }
    }

    await ledgerDB.settleExcessPaymentAfterEviction({
      tenantId,
      clientId,
      transactionId: transId,
      description: `Paid back ₹${amount} to tenant via ${modeVal} on ${moment(paidDate).format("DD MMM YYYY")}`,
    });

    const moveOutOccupancies = await moveOutDB.getAllByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    for (let occupancy of moveOutOccupancies) {
      await moveOutDB.updateRefundStatus({
        id: occupancy.id,
        refundStatus: CONSTANTS.REFUND_STATUS.PROCESSED,
      });
    }

    const token = jwt.sign(
      {
        id: client?.id,
        type: CONSTANTS.USER_TYPE.CLIENT,
        platform: client?.device,
      },
      process.env.TOKEN_SECRET!,
      { expiresIn: process.env.TOKEN_EXPIRY }
    );

    const axiosHeaders = {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${token}`,
      },
    };

    const data = await axios.get(
      `${process.env.API_BASE}/tenant/get/final/breakdown?tenantId=${tenantId}&sendFnF=0`,
      axiosHeaders,
    );

    if (data?.data.link) {
      const link = data?.data.link;
      if (0 != isGstEnabled && 0 != invoiceNo) {
        await transactionDB.updateReceiptWithInvoice({ id: transId, receipt: link, invoiceNo: transactionInvoiceNo, gstNo });
      } else {
        await transactionDB.updateReceipt({
          id: transId,
          receipt: link,
        });
      }
    }

    await logActivity(
      req.userType!,
      Number(req.id!),
      Number(req.parentClientId!),
      CONSTANTS.TENANT_DEVICE_TYPE.WEB,
      tenantId,
      CONSTANTS.ACTIVITY_TYPES.SETTLE_EXCESS_VIA_PAYOUT,
      amount,
      paidDate,
      modeVal,
      null,
      null,
    );


    let requests = await requestDB.getRefundRequestByTenantAndClient({ tenantId, clientId });
    if (requests) {
      for (const request of requests) {
        let requestId = request?.id;
        if (requestId) {
          const request = await requestDB.getById({ id: requestId });

          if (!request) {
            log.info(
              `[${U}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Request Found`
            );
          } else {
            await requestDB.updateStatus({
              id: requestId,
              status: CONSTANTS.REQUEST_STATUS.APPROVED,
            });
            log.info(
              `[${U}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Refund Request Approved`
            );
          }
        }
      }
    }

    const pendingRefundRequest = await requestDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
      status: CONSTANTS.REQUEST_STATUS.PENDING,
      type: CONSTANTS.REQUEST_TYPE.REFUND,
    });

    if (pendingRefundRequest) {
      log.info(
        `[${U}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Refund Request Id [${pendingRefundRequest?.id}], Pending Refund Request Found, Updating Status To Approved`
      )
      await requestDB.updateStatus({
        id: pendingRefundRequest?.id,
        status: CONSTANTS.REQUEST_STATUS.APPROVED,
      });
    }

    await hiddenDuesDB.removeByTenantIdAndClientIdAndOccupancyId({
      clientId,
      tenantId,
      occupancyId: occupancy?.id,
    });

    log.info(
      `[${U}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Mode [${mode}], Amount [${amount}], Paid Date [${paidDate}], Excess Payment Settled Successfully`
    );

    return true;

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

    return false;
  }
};

export default refundExcessPayment;
