const Dinero = require("dinero.js");
const qs = require("query-string");
const axios = require("axios");
const logger = require("../utils/logger");
const { isEmpty, isNumber } = require("lodash");
const handleCommentBasedPayment = require("./lib/handleCommentBasedPayment");
const handleInvoiceBasedPayment = require("./lib/handleInvoiceBasedPayment");
const logValidationError = require("./lib/handlePaymentValidationError");
const getCptellerUrl = require("./lib/getCptellerUrl");
const getShopCredentials = require("./lib/getShopCredentials");
const decodeComment = require("./lib/decodeComment");

/**
 * @description Get lightbox credentials for the shop
 * @param req
 * @param res
 * @returns {Promise<void>}
 */
const lightboxCredentials = async (req, res) => {
  const decodedComment = decodeComment(req.body?.comment);
  const logMeta = {
    iPayData: req.body?.iPayData,
    decodedComment,
    bodyshop: {
      id: req.body?.bodyshop?.id,
      imexshopid: req.body?.bodyshop?.imexshopid,
      name: req.body?.bodyshop?.shopname
    }
  };

  logger.log("intellipay-lightbox-credentials", "DEBUG", req.user?.email, null, logMeta);

  const shopCredentials = await getShopCredentials(req.body.bodyshop);

  if (shopCredentials?.error) {
    logger.log("intellipay-credentials-error", "ERROR", req.user?.email, null, {
      message: shopCredentials.error?.message,
      ...logMeta
    });

    return res.json({
      message: shopCredentials.error?.message,
      type: "intellipay-credentials-error",
      ...logMeta
    });
  }

  try {
    const options = {
      method: "POST",
      headers: { "content-type": "application/x-www-form-urlencoded" },
      data: qs.stringify({
        ...shopCredentials,
        operatingenv: "businessattended"
      }),
      url: getCptellerUrl({
        apiType: "custapi",
        params: { method: `autoterminal${req.body.refresh ? "_refresh" : ""}` }
      })
    };

    const response = await axios(options);

    logger.log("intellipay-lightbox-success", "DEBUG", req.user?.email, null, {
      requestOptions: options,
      ...logMeta
    });

    return res.send(response.data);
  } catch (error) {
    logger.log("intellipay-lightbox-error", "ERROR", req.user?.email, null, {
      message: error?.message,
      ...logMeta
    });

    return res.json({
      message: error?.message,
      type: "intellipay-lightbox-error",
      ...logMeta
    });
  }
};

/**
 * @description Process payment refund
 * @param req
 * @param res
 * @returns {Promise<void>}
 */
const paymentRefund = async (req, res) => {
  const decodedComment = decodeComment(req.body.iPayData?.comment);
  const logResponseMeta = {
    iPayData: req.body?.iPayData,
    bodyshop: {
      id: req.body.bodyshop?.id,
      imexshopid: req.body.bodyshop?.imexshopid,
      name: req.body.bodyshop?.shopname
    },
    paymentid: req.body?.paymentid,
    amount: req.body?.amount,
    decodedComment
  };

  logger.log("intellipay-refund-request-received", "DEBUG", req.user?.email, null, logResponseMeta);

  const shopCredentials = await getShopCredentials(req.body.bodyshop);

  if (shopCredentials?.error) {
    logger.log("intellipay-refund-credentials-error", "ERROR", req.user?.email, null, {
      credentialsError: shopCredentials.error,
      ...logResponseMeta
    });

    return res.status(400).json({
      credentialsError: shopCredentials.error,
      type: "intellipay-refund-credentials-error",
      ...logResponseMeta
    });
  }

  try {
    const options = {
      method: "POST",
      headers: { "content-type": "application/x-www-form-urlencoded" },
      data: qs.stringify({
        method: "payment_refund",
        ...shopCredentials,
        paymentid: req.body.paymentid,
        amount: req.body.amount
      }),
      url: getCptellerUrl({
        apiType: "webapi",
        version: "26",
        params: { method: "payment_refund" }
      })
    };

    logger.log("intellipay-refund-options-prepared", "DEBUG", req.user?.email, null, {
      requestOptions: options,
      ...logResponseMeta
    });

    const response = await axios(options);

    logger.log("intellipay-refund-success", "DEBUG", req.user?.email, null, {
      requestOptions: options,
      response: response?.data,
      ...logResponseMeta
    });

    return res.send(response.data);
  } catch (error) {
    logger.log("intellipay-refund-error", "ERROR", req.user?.email, null, {
      message: error?.message,
      ...logResponseMeta
    });

    return res.status(500).json({
      message: error?.message,
      type: "intellipay-refund-error",
      ...logResponseMeta
    });
  }
};

/**
 * @description Generate payment URL for the shop
 * @param req
 * @param res
 * @returns {Promise<void>}
 */
const generatePaymentUrl = async (req, res) => {
  const decodedComment = decodeComment(req.body.comment);
  const logResponseMeta = {
    iPayData: req.body?.iPayData,
    bodyshop: {
      id: req.body.bodyshop?.id,
      imexshopid: req.body.bodyshop?.imexshopid,
      name: req.body.bodyshop?.shopname
    },
    amount: req.body?.amount,
    account: req.body?.account,
    comment: req.body?.comment,
    invoice: req.body?.invoice,
    decodedComment
  };

  logger.log("intellipay-generate-payment-url-received", "DEBUG", req.user?.email, null, logResponseMeta);

  const shopCredentials = await getShopCredentials(req.body.bodyshop);

  if (shopCredentials?.error) {
    logger.log("intellipay-generate-payment-url-credentials-error", "ERROR", req.user?.email, null, {
      message: shopCredentials.error?.message,
      ...logResponseMeta
    });

    return res.status(400).json({
      message: shopCredentials.error?.message,
      type: "intellipay-generate-payment-url-credentials-error",
      ...logResponseMeta
    });
  }

  try {
    const options = {
      method: "POST",
      headers: { "content-type": "application/x-www-form-urlencoded" },
      data: qs.stringify({
        ...shopCredentials,
        amount: Dinero({ amount: Math.round(req.body.amount * 100) }).toFormat("0.00"),
        account: req.body.account,
        comment: req.body.comment,
        invoice: req.body.invoice,
        createshorturl: true
      }),
      url: getCptellerUrl({
        apiType: "custapi",
        params: { method: "generate_lightbox_url" }
      })
    };

    logger.log("intellipay-generate-payment-url-options-prepared", "DEBUG", req.user?.email, null, {
      requestOptions: options,
      ...logResponseMeta
    });

    const response = await axios(options);

    logger.log("intellipay-generate-payment-url-success", "DEBUG", req.user?.email, null, {
      requestOptions: options,
      shortUrl: response.data?.shorturl,
      ...logResponseMeta
    });

    return res.send(response.data);
  } catch (error) {
    logger.log("intellipay-generate-payment-url-error", "ERROR", req.user?.email, null, {
      message: error?.message,
      ...logResponseMeta
    });

    return res.status(500).json({ message: error?.message, ...logResponseMeta });
  }
};

/**
 * @description Check the fee for a given amount
 * Reference: https://intellipay.com/dist/webapi26.html#operation/fee
 * @param req
 * @param res
 * @returns {Promise<void>}
 */
const checkFee = async (req, res) => {
  const { bodyshop = {}, amount } = req.body || {};
  const { id, imexshopid, shopname, state } = bodyshop;
  const logResponseMeta = { bodyshop: { id, imexshopid, name: shopname, state }, amount };

  logger.log("intellipay-checkfee-request-received", "DEBUG", req.user?.email, null, logResponseMeta);

  if (!isNumber(amount) || amount <= 0) {
    logger.log("intellipay-checkfee-skip", "DEBUG", req.user?.email, null, {
      message: "Amount is zero or undefined, skipping fee check.",
      ...logResponseMeta
    });
    return res.json({ fee: 0 });
  }

  const shopCredentials = await getShopCredentials(bodyshop);

  if (shopCredentials?.error) {
    logger.log("intellipay-checkfee-credentials-error", "ERROR", req.user?.email, null, {
      message: shopCredentials.error?.message,
      ...logResponseMeta
    });
    return res.status(400).json({ error: shopCredentials.error?.message, ...logResponseMeta });
  }

  try {
    const options = {
      method: "POST",
      headers: { "content-type": "application/x-www-form-urlencoded" },
      data: qs.stringify(
        {
          method: "fee",
          ...shopCredentials,
          amount: String(amount), // Type cast to string as required by API
          paymenttype: "CC",
          cardnum: "4111111111111111", // Required for compatibility with API
          state: state?.toUpperCase() || "ZZ"
        },
        { sort: false } // Ensure query string order is preserved
      ),
      url: getCptellerUrl({ apiType: "webapi", version: "26" })
    };

    logger.log("intellipay-checkfee-options-prepared", "DEBUG", req.user?.email, null, {
      requestOptions: options,
      ...logResponseMeta
    });

    const { data } = await axios(options);

    if (data?.error || data < 0) {
      const errorType = data?.error ? "intellipay-checkfee-api-error" : "intellipay-checkfee-negative-fee";
      const errorMessage = data?.error
        ? data?.error
        : "Fee amount negative. Check API credentials & account configuration.";
      logger.log(errorType, "ERROR", req.user?.email, null, { message: errorMessage, data, ...logResponseMeta });
      return res.status(400).json({ error: errorMessage, type: errorType, data, ...logResponseMeta });
    }

    logger.log("intellipay-checkfee-success", "DEBUG", req.user?.email, null, { fee: data, ...logResponseMeta });
    return res.json({ fee: data, ...logResponseMeta });
  } catch (error) {
    logger.log("intellipay-checkfee-error", "ERROR", req.user?.email, null, {
      message: error?.message,
      ...logResponseMeta
    });
    return res.status(500).json({ error: error?.message, logResponseMeta });
  }
};

/**
 * @description Handle the postback from Intellipay
 * @param req
 * @param res
 * @returns {Promise<void>}
 */
/**
 * Handle the postback from Intellipay payment system
 */
const postBack = async (req, res) => {
  const { body: values } = req;
  const decodedComment = decodeComment(values?.comment);
  const logMeta = { iprequest: values, decodedComment };

  logger.log("intellipay-postback-received", "DEBUG", "api", null, logMeta);

  try {
    // Handle empty/invalid requests
    if (isEmpty(values?.invoice) && !decodedComment) {
      logger.log("intellipay-postback-ignored", "DEBUG", "api", null, {
        message: "No invoice or comment provided",
        ...logMeta
      });
      return res.sendStatus(200);
    }

    // Process payment based on data type
    if (decodedComment) {
      return await handleCommentBasedPayment(values, decodedComment, logger, logMeta, res);
    } else if (values?.invoice) {
      return await handleInvoiceBasedPayment(values, logger, logMeta, res);
    } else {
      // This should be caught by first validation, but as a safeguard
      logValidationError("intellipay-postback-invalid", "No valid invoice or comment provided", logMeta);
      return res.status(400).send("Bad Request: No valid invoice or comment provided");
    }
  } catch (error) {
    logger.log("intellipay-postback-error", "ERROR", "api", null, {
      message: error?.message,
      ...logMeta
    });
    return res.status(400).json({ successful: false, error: error.message, ...logMeta });
  }
};

module.exports = {
  lightboxCredentials,
  paymentRefund,
  generatePaymentUrl,
  checkFee,
  postBack
};
