import axios from "axios";
import CONSTANTS from "../config/constants";
import log from "../config/log";
import clientDB from "../models/client.model";
import clientConfigDB from "../models/clientConfig.model";
import transactionDB from "../models/transaction.model";
import bankDB from "../models/bank.model";
import occupancyDB from "../models/occupancy.model";

export const cashfreeSplit = async (
  clientId: number,
  propId: number,
  amount: number,
  charges: number,
  gstAmount: number,
  orderId: string,
  mode: number,
  tenantId: number,
  description: string,
  isSecurityPayment: boolean = false,
  transId: number,
) => {
  const U = "cashfreeSplit Util";
  const F = "cashfreeSplit";

  try {
    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${U}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${orderId}], No Client Found`,
      );
      return false;
    }

    let vendorId = null;

    const propertyVendorId = await clientConfigDB.getClientConfigByPropId({
      clientId,
      propId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.VENDOR_ID,
    });


    if (propertyVendorId) {
      vendorId = propertyVendorId.value;
    } else {
      const clientVendorId = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.VENDOR_ID,
      });
      if (clientVendorId) {
        vendorId = clientVendorId.value;
      } else {
        log.info(
          `[${U}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], Prop Id [${propId}], Split API can't be initiated due to absence of Vendor Id`,
        );
        return false;
      }
    }

    let occupancy = await occupancyDB.getByClientIdAndTenantId({
      clientId: clientId,
      tenantId: tenantId,
    });
    if (occupancy) {
      occupancy = occupancy[0];
    }
    const isPaymentAgreementRule = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.PAYMENT_AGREEMENT_RULE
    });

    /*Agreement*/
    let splittingTo = 'normal account';
    let agreementPeriod = Number(occupancy?.agreementPeriod);
    if (occupancy?.stayType === CONSTANTS.STAY_TYPE.SHORT) {
      agreementPeriod = Number(Number(occupancy?.agreementPeriod) / 30);
    }
    if (isPaymentAgreementRule && Number(agreementPeriod) <= Number(isPaymentAgreementRule?.value)) {
      log.info(
        `[${U}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${orderId}], Matched Agreement Rule`
      );
      let getVendorId = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.AGREEMENT_RULE_ACCOUNT
      });
      if (getVendorId && getVendorId?.value) {
        splittingTo = 'agreement rule account';
        vendorId = getVendorId?.value || null;
      }
    }

    /*If Payment is of security then it will be Split to Defined Account */
    if (isSecurityPayment) {
      let clientSecurityAccount = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.SECURITY_ACCOUNT
      });
      if (clientSecurityAccount && clientSecurityAccount?.value) {
        splittingTo = 'security account';
        vendorId = clientSecurityAccount?.value;
      }
    }

    if (vendorId) {
      log.info(
        `[${U}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${orderId}], Vendor Id [${vendorId}] Split to [${splittingTo}], No Hold Out Balance Remaining, Initiating Split Payment API `,
      );
      let orderSplits: any = [];
      let getHoldAccount = await clientConfigDB.getClientConfigByPropId({
        clientId,
        propId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.HOLD_ACCOUNT
      });
      if (!getHoldAccount || !getHoldAccount?.value) {
        getHoldAccount = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.HOLD_ACCOUNT
        });
      }
      if (Number(client.holdOutBalance) > 0 && getHoldAccount && getHoldAccount?.value) {
        const paymentAmount = Number(amount) + Number(gstAmount);
        const remainingHoldAmount = Number(client.holdOutBalance);
        const holdAmount = Math.min(paymentAmount, remainingHoldAmount);
        let clientAmount = paymentAmount - holdAmount;
        // let getHoldAccount = await clientConfigDB.getClientConfigByPropId({
        //   clientId,
        //   propId,
        //   provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
        //   type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.HOLD_ACCOUNT
        // });
        // if (!getHoldAccount || !getHoldAccount?.value) {
        //   getHoldAccount = await clientConfigDB.getClientConfig({
        //     clientId,
        //     provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
        //     type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.HOLD_ACCOUNT
        //   });
        // }
        if (holdAmount > 0 && getHoldAccount && getHoldAccount?.value) {
          log.info(
              `[${U}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${orderId}], Client Hold Out Balance [${client.holdOutBalance}], Amount [${amount}], Charges [${charges}], Gst Charges [${gstAmount}], Substracting The Amount and Gst Amount From Hold Out Amount`,
          );
          orderSplits.push({
            vendor_id: getHoldAccount?.value,
            amount: holdAmount,
            tags: {
              product_info: description,
              tenant_id: tenantId,
            },
          });
          const amountDiff = Number(client.holdOutBalance) - holdAmount;
          /*Updating hold for client*/
          await clientDB.updateHoldOutBalance({
            id: client.id,
            holdOutBalance: amountDiff > 0 ? amountDiff : 0,
          });
          /*Hold out balance updating to transaction*/
          await transactionDB.updateHoldAmount({
            id: transId,
            holdAmount: holdAmount,
          });
        } else {
          // clientAmount = clientAmount + holdAmount;
          clientAmount = paymentAmount;
        }

        if (clientAmount > 0) {
          orderSplits.push({
            vendor_id: vendorId,
            amount: clientAmount,
            tags: {
              product_info: description,
              tenant_id: tenantId,
            },
          });
        }
      } else {
        orderSplits = [
          {
            vendor_id: vendorId,
            amount: Number(amount) + Number(gstAmount),
            tags: {
              product_info: description,
              tenant_id: tenantId,
            },
          },
        ];
      }

      if (
        mode !== CONSTANTS.TRANSACTION_MODES.UPI &&
        mode !== CONSTANTS.TRANSACTION_MODES.OFFLINE &&
        Number(charges) !== 0
      ) {
        //In Cashfree, any amount NOT sent to a vendor stays in your main account
        //If you want to explicitly track your 'charges' as a split to your own internal ID,
        //you can add another vendor entry here, otherwise it just settles to you by default
      }

      log.info(
        `[${U}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${orderId}], Split Detail [${JSON.stringify(orderSplits)}]`,
      );
      const splitResponse = await splitPaymentApi(orderSplits, orderId, Number(client.id));

      log.info(
        `[${U}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${orderId}], Split Payment Response [${JSON.stringify(splitResponse)}]`,
      );

      if (splitResponse && splitResponse.status === "OK") {
        let isBank = false;

        const bank = await bankDB.getBankAccountByCashfreeVendorId({
          clientId,
          cashfreeVendorId: vendorId,
        });

        if (bank) isBank = true;

        await transactionDB.updateSettleDetailsByGId({
          gId: orderId,
          settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
          settleDescription: JSON.stringify(splitResponse),
          splitTnxId: null,
        });

        if (isBank) {
          await transactionDB.updateAccountDetailsByGId({
            gId: orderId,
            bankId: bank.id,
            paymentAccountNo: bank.accountNum,
            paymentAccountName: bank.holderName,
          });
        }
        return true;
      } else {
        log.info(
          `[${U}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${orderId}], Split API failed with error [${JSON.stringify(splitResponse)}]`,
        );
        return false;
      }
    } else {
      log.info(
        `[${U}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${orderId}], Split API can't be initiated due to absence of Account Details`,
      );

      return false;
    }
  } catch (error: any) {
    log.info(
      `[${U}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], Order Id [${orderId}], Error [${error?.message || error}]`,
    );
    return false;
  }
};

const splitPaymentApi = async (orderSplits: any, orderId: string, clientId: number) => {
  try {

    const url = `${process.env.CASHFREE_PAYMENT_BASE_URL}/easy-split/orders/${orderId}/split`;
    let xClientId = process.env.CASHFREE_CLIENTID;
    let xSecretKey = process.env.CASHFREE_SECRET;

    const isXClientId = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.X_CLIENT_ID,
    });
    const isXClientSecretKey = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.X_SECRET_KEY,
    });

    if (isXClientId && isXClientId.value && isXClientSecretKey && isXClientSecretKey.value) {
      xClientId = isXClientId.value;
      xSecretKey = isXClientSecretKey.value;
      log.info(`Cashfree Split URL [${url}], Client Id [${clientId}], X Client Id [${xClientId}], Secret Key [${xSecretKey}], Client Gateway `);
    } else {
      log.info(`Cashfree Split URL [${url}], Client Id [${clientId}], X Client Id [${xClientId}], Secret Key [${xSecretKey}], Kipinn Gateway `);
    }

    const headers = {
      // "x-client-id": process.env.CASHFREE_CLIENTID,
      // "x-client-secret": process.env.CASHFREE_SECRET,
      "x-client-id": xClientId,
      "x-client-secret": xSecretKey,
      "x-api-version": "2023-08-01",
      "Content-Type": "application/json",
    };

    const data = {
      split: orderSplits,
      disable_split: false,
    };

    const response = await axios.post(url, data, { headers });
    return response.data;
  } catch (error: any) {
    log.info(`Error splitting payment [${error.message || error}]`);
    return { error: error.message };
  }
};
