const Buildcredit = require("../../models/Rpaf");
const BuildcreditPayment = require("../../models/RpafPayment");
const Customers = require("../../models/Customers");
const OrderForm = require("../../models/OrderForm");
const BuildcreditOverpayments = require("../../models/BuildcreditOverPayments");
const BuildcreditOverpaymentsOrders = require("../../models/BuildcreditOverPaymentsOrders");
const { Op } = require("sequelize");
const Sequelize = require("sequelize");
const { generateRandomString, generateSecureRandomSixDigit } = require("../../helpers/common");

const convertToPeso = (number) => {
  return new Intl.NumberFormat("en-PH", { style: "currency", currency: "PHP" }).format(number);
};

const convertDate = (date) => {
  return new Date(date).toLocaleString("en-PH", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
  });
};

exports.getBuildcreditCustomers = async (req, res, next) => {
  try {
    const buildCreditCustomers = await Buildcredit.findAll({
      where: {
        status: {
          [Op.in]: ["Approved", "Suspended", "Case to case"],
        },
      },
      attributes: ["id"],
      include: [
        {
          model: Customers,
          as: "customer_detail",
          attributes: ["id", "first_name", "last_name", "hardware_name"],
          required: true,
        },
      ],
    });

    res.status(200).json({ success: true, buildcredit_customers: buildCreditCustomers });
  } catch (err) {
    next(err);
  }
};

exports.getAllOverpayments = async (req, res, next) => {
  const { customer_id } = req.query;
  if (!customer_id) return res.status(401).json({ success: false, message: "Customer Id missing" });

  try {
    const customerOrderForm = await OrderForm.findAll({
      where: {
        customer_id,
        payment_method: {
          [Op.in]: ["RPAF", "Build Credit", "BUILDCREDIT INTEREST", "EOS ORDER"],
        },
        status: {
          [Op.notIn]: ["Pending", "Denied"],
        },
        is_waived: false,
      },
      attributes: ["id", "reference_no", "total_amount", "payment_method", "status"],
      include: [
        {
          model: BuildcreditPayment,
          as: "rpaf_payment",
          where: {
            status: {
              [Op.in]: ["Verified", "PDC Received"],
            },
          },
          attributes: ["id", "reference_no", "cheque_no", "amount_paid", "date", "status"],
          order: [["date", "ASC"]],
        },
      ],
    });

    const overPaymentsOrder = [];

    for (const order of customerOrderForm) {
      const { id, reference_no, total_amount, rpaf_payment } = order;

      let totalAmountPaid = 0;
      let paymentsDetails = [];

      for (const payment of rpaf_payment) {
        const { amount_paid, reference_no: payment_reference_no, date, status } = payment;
        totalAmountPaid += parseFloat(amount_paid);
        paymentsDetails.push({
          reference_no: payment_reference_no,
          amount_paid: parseFloat(amount_paid),
          date,
          status,
        });
      }

      if (totalAmountPaid > parseFloat(total_amount)) {
        const overpaymentAmount = totalAmountPaid - parseFloat(total_amount);

        let remainingOverpayment = overpaymentAmount;
        let overpaymentDetails = [];

        for (const payment of paymentsDetails) {
          if (remainingOverpayment <= 0) break;
          let paymentOverpayment = Math.min(payment.amount_paid, remainingOverpayment);
          remainingOverpayment -= paymentOverpayment;
          overpaymentDetails.push({
            reference_no: payment.reference_no,
            overpayment: paymentOverpayment,
            date: payment.date,
            status: payment.status,
          });
        }

        overPaymentsOrder.push({
          order_id: id,
          order_reference_no: reference_no,
          order_total_amount: total_amount,
          order_total_amount_paid: totalAmountPaid,
          overpayment_amount: overpaymentAmount,
          payments: overpaymentDetails,
        });
      }
    }

    res.status(200).json({ success: true, customer: customer_id, order: overPaymentsOrder });
  } catch (err) {
    next(err);
  }
};

exports.getCustomerUnpaidOrders = async (req, res, next) => {
  const { customer_id } = req.query;
  if (!customer_id) return res.status(401).json({ success: false, message: "Customer Id missing" });

  try {
    const customerOrderForm = await OrderForm.findAll({
      where: {
        customer_id,
        payment_method: {
          [Op.in]: ["RPAF", "Build Credit", "BUILDCREDIT INTEREST", "EOS ORDER"],
        },
        status: {
          [Op.notIn]: ["Pending", "Denied", "Release Order - Expired"],
        },
        is_waived: false,
      },
      attributes: ["id", "reference_no", "total_amount", "payment_method", "status"],
    });

    const unpaidOrders = [];
    for (const order of customerOrderForm) {
      const { id, reference_no, total_amount } = order;

      const orderPayments = await BuildcreditPayment.sum("amount_paid", {
        where: {
          order_id: reference_no,
          status: {
            [Op.in]: ["Verified", "PDC Received"],
          },
        },
      });

      const orderOverpayments = await BuildcreditOverpaymentsOrders.sum("amount_paid", {
        where: {
          order_reference_no: reference_no,
        },
        include: [
          {
            model: BuildcreditOverpayments,
            as: "buildcredit_overpayment",
            attributes: [],
            where: {
              payment_status: {
                [Op.in]: ["Verified", "PDC Received"],
              },
            },
          },
        ],
      });

      const parsedOrderPayments = parseFloat(orderPayments || 0);
      const parsedOrderOverpayments = parseFloat(orderOverpayments || 0);

      const totalPayments = parsedOrderPayments + parsedOrderOverpayments;
      const balance = parseFloat(total_amount) - parseFloat(totalPayments || 0);

      if (balance > 0) {
        unpaidOrders.push({
          order_id: id,
          order_reference_no: reference_no,
          unpaid_amount: convertToPeso(balance),
          unpaid_amount_int: balance,
        });
      }
    }

    res.status(200).json({ success: true, unpaid_orders: unpaidOrders });
  } catch (err) {
    next(err);
  }
};

exports.getOverpayments = async (req, res, next) => {
  const { customer_id } = req.query;

  try {
    if (!customer_id) return res.status(400).json({ success: false, message: "Missing customer id." });
    const customer = await Customers.findByPk(customer_id);
    if (!customer) return res.status(400).json({ success: false, message: "Invalid customer." });

    const overpayment = await BuildcreditOverpayments.findAll({
      where: {
        customer_id,
      },
      include: [
        {
          model: BuildcreditOverpaymentsOrders,
          as: "buildcredit_overpayment_orders",
        },
      ],
    });

    const overPaymentResponse = [];

    if (overpayment.length > 0) {
      for (const payment of overpayment) {
        const { amount, buildcredit_overpayment_orders } = payment;

        let totalAmountPaid = 0;

        const parsedAmount = parseFloat(amount);

        for (const orders of buildcredit_overpayment_orders) {
          const { amount_paid, status } = orders;

          if (!status) {
            continue;
          }
          const parsedAmountPaid = parseFloat(amount_paid);
          totalAmountPaid += parsedAmountPaid;
        }

        const overpaymentRemainingAmount = parsedAmount - totalAmountPaid;
        if (overpaymentRemainingAmount > 0) {
          const response = {
            id: payment.id,
            reference_no: payment.reference_no,
            remaining_amount_int: overpaymentRemainingAmount,
            remaining_amount: convertToPeso(overpaymentRemainingAmount),
            payment_status: payment.payment_status,
            paid_orders: payment.buildcredit_overpayment_orders,
          };
          overPaymentResponse.push(response);
        }
      }
    }

    res.status(200).json({ success: true, overpayment: overPaymentResponse });
  } catch (err) {
    next(err);
  }
};
exports.addOverPayment = async (req, res, next) => {
  const {
    customer_id,
    amount = 0,
    is_pdc_payment = false,
    cheque_no = null,
    date_cheque = null,
    bank = null,
  } = req.body;

  try {
    if (!customer_id) return res.status(400).json({ success: false, message: "Missing customer id." });

    const customersPromise = Customers.findByPk(customer_id);
    const buildcreditPromise = Buildcredit.findOne({
      where: {
        customer_id,
      },
    });

    const [customer, buildcredit] = await Promise.all([customersPromise, buildcreditPromise]);

    if (!customer || !buildcredit)
      return res
        .status(400)
        .json({ success: false, message: "Invalid customer or customer doesn't have any buildcredit." });

    if (!amount > 0) return res.status(400).json({ success: false, message: "Invalid amount." });

    const paymentReferenceNumber = `OP${generateRandomString()}${generateSecureRandomSixDigit()}`;
    const overPayment = await BuildcreditOverpayments.create({
      reference_no: paymentReferenceNumber,
      customer_id,
      buildcredit_id: buildcredit.id,
      amount,
      is_pdc_payment,
      cheque_date: date_cheque,
      cheque_number: cheque_no,
      bank,
      payment_status: is_pdc_payment ? "PDC Received" : "Verified",
    });

    res.status(200).json({ success: true, overPayment });
  } catch (err) {
    next(err);
  }
};

const checkOrderPaymentstatus = async (order_reference_no) => {
  try {
    let totalAmountPaid = 0;

    let buildCreditPaymentPaid = 0;
    let buildCreditOverpaymentPaid = 0;
    const buildcreditPaymentPromise = BuildcreditPayment.findAll({
      where: {
        order_id: order_reference_no,
        status: "Verified",
      },
      include: [
        {
          model: OrderForm,
          as: "order_form_detail",
          attributes: [],
          where: {
            status: {
              [Op.notIn]: ["Cancelled", "Denied"],
            },
          },
          required: true,
        },
      ],
      attributes: [[Sequelize.fn("SUM", Sequelize.col("amount_paid")), "total_amount_paid"]],
    });

    const buildcreditOverpaymentPromise = BuildcreditOverpaymentsOrders.findAll({
      where: {
        order_reference_no,
        status: true,
      },
      include: [
        {
          model: BuildcreditOverpayments,
          as: "buildcredit_overpayment",
          attributes: [],
          where: {
            payment_status: {
              [Op.notIn]: ["Cancelled", "Denied"],
            },
          },
          required: true,
        },
      ],
      attributes: [[Sequelize.fn("SUM", Sequelize.col("amount_paid")), "total_amount_paid"]],
    });

    const [buildcreditPayment, buildcreditOverpayment] = await Promise.all([
      buildcreditPaymentPromise,
      buildcreditOverpaymentPromise,
    ]);

    if (buildcreditPayment && buildcreditPayment[0] && buildcreditPayment[0].dataValues.total_amount_paid)
      buildCreditPaymentPaid = buildcreditPayment[0].dataValues.total_amount_paid;

    if (buildcreditOverpayment && buildcreditOverpayment[0] && buildcreditOverpayment[0].dataValues.total_amount_paid)
      buildCreditOverpaymentPaid = buildcreditOverpayment[0].dataValues.total_amount_paid;

    totalAmountPaid = parseFloat(buildCreditPaymentPaid) + parseFloat(buildCreditOverpaymentPaid);
    return totalAmountPaid;
  } catch (err) {
    console.error(err);
  }
};

exports.addOverPaymentLink = async (req, res, next) => {
  const { customer_id, overpayment_id, overpayment_reference_no, link_array } = req.body;

  try {
    if (!customer_id || !overpayment_id || !overpayment_reference_no)
      return res.status(400).json({ success: false, message: "Missing required parameters." });

    const customerPromise = Customers.findByPk(customer_id);
    const overpaymentPromise = BuildcreditOverpayments.findOne({
      where: {
        id: overpayment_id,
        customer_id: customer_id,
        reference_no: overpayment_reference_no,
      },
    });

    const [customer, overpayment] = await Promise.all([customerPromise, overpaymentPromise]);

    if (!customer || !overpayment)
      return res.status(400).json({ success: false, message: "Invalid customer or overpayment." });

    if (!link_array || !link_array.length > 0)
      return res.status(400).json({ success: false, message: "Invalid link array." });

    const linkPromises = [];
    for (const link of link_array) {
      const { amount, order_id, order_reference_no } = link;
      const order = await OrderForm.findOne({
        where: {
          id: order_id,
          reference_no: order_reference_no,
          status: {
            [Op.notIn]: ["Cancelled", "Denied", "Release Order - Expired"],
          },
          payment_method: {
            [Op.in]: ["RPAF", "Build Credit", "BUILDCREDIT INTEREST", "EOS ORDER"],
          },
        },
      });

      if (!order) continue;

      const orderTotalAmountPaid = await checkOrderPaymentstatus(order_reference_no);
      const parsedOrderTotalAmountPaid = parseFloat(orderTotalAmountPaid);
      const orderTotalAmount = parseFloat(order.total_amount);

      const remainingBalance = orderTotalAmount - parsedOrderTotalAmountPaid;

      if (amount > remainingBalance) continue;

      const paymentReferenceNumber = `OPP${generateRandomString()}${generateSecureRandomSixDigit()}`;

      const overpaymentLink = BuildcreditOverpaymentsOrders.create({
        buildcredit_overpayments_id: overpayment.id,
        order_id: order.id,
        order_reference_no: order_reference_no,
        reference_no: paymentReferenceNumber,
        customer_id,
        amount_paid: amount,
      });

      linkPromises.push(overpaymentLink);
    }

    const links = await Promise.all(linkPromises);

    res.status(200).json({ success: true, overpayment: links });
  } catch (err) {
    next(err);
  }
};
