const OrderForm = require("../../models/OrderForm");
const OrderFormDetails = require("../../models/OrderFormDetails");
const Vouchers = require("../../models/Vouchers");
const Customers = require("../../models/Customers");
const Product = require("../../models/Products");
const buildCreditService = require("../../helpers/buildcredit");
const SellerBadOrderContainer = require("../../models/SellerBadOrderContainer");
const OrderStatusEvents = require("../../models/OrderStatusEvents");

exports.importOrders = async (req, res, next) => {
  try {
    const { customerId, voucherCode, shippingMethod, jsonData } = req.body;

    const customerPromise = Customers.findByPk(customerId);
    const voucherPromise = Vouchers.findOne({
      where: {
        voucher_code: voucherCode,
      },
    });

    const shippingMethods = ["Standard Shipping", "Pick-up"];

    if (!shippingMethods.includes(shippingMethod))
      return res
        .status(400)
        .json({ success: false, message: "Invalid Shipping Method" });

    const [customer, voucher] = await Promise.all([
      customerPromise,
      voucherPromise,
    ]);

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

    const buildCredit = await buildCreditService.getBuildCreditBalance(
      customer.id,
    );

    if (buildCredit && buildCredit.current_bal === 0) {
      return res.status(400).json({
        success: false,
        message: "No Buildcredit found for this Customer",
      });
    }
    if (voucherCode) {
      if (!voucher)
        return res
          .status(400)
          .json({ success: false, message: "Invalid Voucher Code" });
    }

    let successCount = 0;
    let failedCount = 0;
    if (jsonData) {
      const reference_no =
        "ORD-IM" + Math.random().toString(36).substring(2, 10).toUpperCase();

      const orderForm = await OrderForm.create({
        email: customer.email,
        name: `${customer.first_name} ${customer.last_name}`,
        customer_id: customer.id,
        contact_number: customer.contact_number,
        pickup_date: new Date(),
        encode_date: new Date(),
        reference_no: reference_no,
        status: "Verified",
        payment_method: "Build Credit",
        address: customer.address,
        longitude: customer.longitude,
        latitude: customer.latitude,
        date_created: new Date(),
        delivery_fee: 0,
        shipping_type: shippingMethod,
        voucher_id_used: voucher ? voucher.id : null,
      });

      let cleanedOrderDetail = [];
      let orderTotalAmount = 0;
      let orderTotalQty = 0;
      let seller_id = null;
      const parsedJsonData = JSON.parse(jsonData);
      for (const data of parsedJsonData) {
        const { productID, originalPrice, quantity, totalAmount } = data;
        if (!productID || !originalPrice || !quantity || !totalAmount) {
          failedCount += 1;
          await orderForm.destroy();
          return res.status(400).json({
            success: false,
            message:
              "Either ProductID / Original Price / Quantity / Total Amount is empty or invalid.",
            details: `PRODUCT ID: ${productID}, ORIG PRICE: ${originalPrice}, QUANTITY: ${quantity}, TOTAL AMOUNT: ${totalAmount}`,
          });
        }

        const parsedPrice = parseFloat(originalPrice);
        const parsedQuantity = parseFloat(quantity);
        const parsedTotalAmount = parseFloat(totalAmount);

        if (
          isNaN(parsedPrice) ||
          parsedPrice <= 0 ||
          isNaN(parsedQuantity) ||
          parsedQuantity <= 0 ||
          isNaN(parsedTotalAmount) ||
          parsedTotalAmount <= 0
        ) {
          failedCount += 1;
          await orderForm.destroy();
          return res.status(400).json({
            success: false,
            message:
              "Either ProductID / Original Price / Quantity / Total Amount is empty or invalid.",
            details: `PRODUCT ID: ${productID}, PARSED ORIG PRICE: ${parsedPrice}, PARSED QUANTITY: ${parsedQuantity}, PARSED TOTAL AMOUNT: ${parsedTotalAmount}`,
          });
        }

        const product = await Product.findByPk(productID);

        if (!product) {
          failedCount += 1;
          await orderForm.destroy();
          return res.status(400).json({
            success: false,
            message:
              "Either ProductID / Original Price / Quantity / Total Amount is empty or invalid.",
            details: `PRODUCT ID: ${productID}`,
          });
        }

        cleanedOrderDetail.push({
          order_form_id: orderForm.id,
          product_id: productID,
          quantity: parsedQuantity,
          price: parsedPrice,
          amount: parsedTotalAmount,
        });
        orderTotalQty += parsedQuantity;
        orderTotalAmount += parsedTotalAmount;
        seller_id = product.seller_id;
      }

      if (orderTotalAmount > buildCredit.current_bal) {
        await orderForm.destroy();
        return res.status(400).json({
          success: false,
          message: "Buildcredit Balance for this customer is insufficient",
        });
      }
      const orderDetailPromises =
        await OrderFormDetails.bulkCreate(cleanedOrderDetail);
      orderForm.subtotal_amount = orderTotalAmount;
      orderForm.total_amount = orderTotalAmount;
      orderForm.total_qty = orderTotalQty;
      orderForm.seller_id = seller_id;
      successCount = cleanedOrderDetail.length;

      await orderForm.save();

      res.status(200).json({
        success: true,
        order: {
          reference_no: orderForm.reference_no,
          total_amount: orderForm.total_amount,
        },
        count: {
          success: successCount,
          failed: failedCount,
        },
      });
    }
  } catch (err) {
    next(err);
  }
};

const fs = require("fs");
const path = require("path");
const csv = require("csv-parser");
const sequelizeConnection = require("../../connection/db");
const SellersCompany = require("../../models/SellersCompany");
const { generateReferenceNumber } = require("../../helpers/common");
const OrderStatusEventsAdminReads = require("../../models/OrderStatusEventsAdminReads");

exports.keyPairing = [
  async (req, res, next) => {
    const t = await sequelizeConnection.transaction();
    try {
      const { keypairerFile } = req.files;
      if (!keypairerFile) {
        return res.status(400).json({ error: "No file uploaded" });
      }

      const filePath = path.join(keypairerFile[0].path);

      const results = await new Promise((resolve, reject) => {
        const rows = [];

        fs.createReadStream(filePath)
          .pipe(
            csv({
              separator: ",",
              mapHeaders: ({ header }) => header.trim(),
            }),
          )
          .on("data", (row) => {
            rows.push({
              id: row.id,
              seller_id: row.seller_id,
              seller_name: row.seller_name,
              thd_id: row.thd_id || null,
              menu: row.menu,
              description: row.description,
              price: parseFloat(row.price) || 0,
              discounted_price: parseFloat(row.discounted_price) || 0,
              quantity_limit: parseInt(row.quantity_limit) || 0,
              stocks: parseFloat(row.stocks) || 0,
            });
          })
          .on("end", () => {
            fs.unlinkSync(filePath);
            resolve(rows);
          })
          .on("error", (err) => {
            fs.unlinkSync(filePath);
            reject(err);
          });
      });

      const filteredResults = results.filter((r) => {
        const val = (r.thd_id || "").trim().toUpperCase();
        return val !== "" && val !== "NULL" && val !== "NULL ";
      });

      for (const result of filteredResults) {
        await Product.update(
          { secondary_id: result.thd_id },
          { where: { id: result.id }, transaction: t },
        );
      }

      await t.commit();
      res.json({ data: filteredResults });
    } catch (err) {
      await t.rollback();
      next(err);
    }
  },
];

exports.verifyDataIntegrity = async (req, res, next) => {
  const { data = [], sellerID } = req.body;

  if (!Array.isArray(data)) {
    return res.status(400).json({
      success: false,
      message: "Invalid data format: Expected an array",
    });
  }

  try {
    const results = await Promise.all(
      data.map(async (item) => {
        const { secondaryID } = item;

        const product = await Product.findOne({
          where: { secondary_id: secondaryID, seller_id: sellerID },
          attributes: ["id"],
        });

        return {
          ...item,
          buildhubID: product ? product.id : null,
        };
      }),
    );

    res.status(200).json({
      success: true,
      results,
    });
  } catch (err) {
    console.error("Verification failed:", err);
    next(err);
  }
};

exports.importOrders2 = async (req, res, next) => {
  const transaction = await sequelizeConnection.transaction();
  try {
    const {
      customerID,
      sellerID,
      shippingMethod,
      voucherCode,
      agentCode,
      data,
      adminToken,
      orderInstructions,
    } = req.body;

    if (!customerID || !sellerID || !shippingMethod || !data) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: "Missing required fields",
      });
    }

    const customerPromise = Customers.findByPk(customerID, { transaction });
    const sellerPromise = SellersCompany.findByPk(sellerID, { transaction });
    const voucherPromise = voucherCode
      ? Vouchers.findOne({
          where: { voucher_code: voucherCode },
          transaction,
        })
      : Promise.resolve(null);
    const referenceNoPromise = generateReferenceNumber();

    const buildCreditPromise =
      buildCreditService.getBuildCreditBalance(customerID);

    const [customer, seller, voucher, referenceNo, buildCreditDetail] =
      await Promise.all([
        customerPromise,
        sellerPromise,
        voucherPromise,
        referenceNoPromise,
        buildCreditPromise,
      ]);

    if (!customer || !seller) {
      await transaction.rollback();
      return res.status(200).json({
        success: false,
        message: "Invalid Customer or Seller ID",
      });
    }

    if (!buildCreditDetail) {
      await transaction.rollback();
      return res.status(200).json({
        success: false,
        message: "Build Credit not found for the customer.",
      });
    }

    const validShippingMethods = ["Standard Shipping", "Pick-up"];
    if (!validShippingMethods.includes(shippingMethod)) {
      await transaction.rollback();
      return res.status(200).json({
        success: false,
        message: "Invalid shipping method",
      });
    }

    if (!Array.isArray(data) || data.length === 0) {
      await transaction.rollback();
      return res.status(200).json({
        success: false,
        message: "Order items are required",
      });
    }

    const orderForm = await OrderForm.create(
      {
        email: customer.email,
        name: `${customer.first_name} ${customer.last_name}`,
        customer_id: customer.id,
        agentCode: agentCode ?? null,
        contact_number: customer.contact_number,
        pickup_date: new Date(),
        encode_date: new Date(),
        reference_no: referenceNo,
        status: "Verified",
        payment_method: "Build Credit",
        address: customer.address,
        longitude: customer.longitude,
        latitude: customer.latitude,
        date_created: new Date(),
        delivery_fee: 0,
        shipping_type: shippingMethod,
        voucher_id_used: voucher ? voucher.id : null,
        seller_id: seller.id,
        is_v2_order: true,
        imported_by_admin_token: adminToken,
        proof_of_payment: orderInstructions || null,
      },
      { transaction },
    );

    const orderFormDetails = [];
    let orderTotalAmount = 0;
    let orderTotalQty = 0;

    for (const d of data) {
      const { buildhubID, qty, price, discountedPrice, totalAmount } = d;

      const parsedQuantity = parseFloat(qty);
      const parsedPrice = parseFloat(price);
      const parsedDiscountedPrice = parseFloat(discountedPrice);
      const parsedTotalAmount = parseFloat(totalAmount);

      await OrderFormDetails.create(
        {
          order_form_id: orderForm.id,
          product_id: buildhubID,
          quantity: parsedQuantity,
          price: parsedPrice,
          discounted_price: parsedDiscountedPrice,
          amount: parsedTotalAmount,
        },
        { transaction },
      );

      orderTotalAmount += parsedTotalAmount;
      orderTotalQty += parsedQuantity;
    }

    if (parseFloat(orderTotalAmount) > buildCreditDetail.current_bal) {
      await transaction.rollback();
      return res.status(200).json({
        success: false,
        message: "Build Credit current balance insufficient.",
      });
    }

    await orderForm.update(
      {
        subtotal_amount: orderTotalAmount,
        total_amount: orderTotalAmount,
        total_quantity: orderTotalQty,
      },
      { transaction },
    );

    await transaction.commit();

    return res.status(200).json({
      success: true,
      message: "Order created successfully",
      orderId: orderForm.id,
      referenceNo: orderForm.reference_no,
    });
  } catch (err) {
    await transaction.rollback();
    next(err);
  }
};

exports.updateToBadOrder = async (req, res, next) => {
  try {
    const sellerCompanyContainer = await SellerBadOrderContainer.findOne({
      where: {
        status: true,
      },
    });

    if (!sellerCompanyContainer)
      return res
        .status(200)
        .json({ success: false, message: "No Seller Container Found." });

    const { order_reference_no, admin_token } = req.body;

    if (!order_reference_no)
      return res.status(200).json({
        success: false,
        message: "Order Form Reference No is required.",
      });

    const orderForm = await OrderForm.findOne({
      where: {
        reference_no: order_reference_no,
      },
    });
    if (!orderForm)
      return res.status(200).json({
        success: false,
        message: "Order Form Reference No not found.",
      });

    orderForm.seller_id = sellerCompanyContainer.seller_id;
    orderForm.status = "Bad Order";
    orderForm.admin_remarks = `Executed by: ${admin_token}`;

    await orderForm.save();

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

exports._notificationService = async (req, res, next) => {
  const transaction = await sequelizeConnection.transaction();

  try {
    const {
      admin_id = "",
      seller_id = "",
      logistics_id = "",
      adminQuery = false,
      sellerQuery = false,
      driverQuery = false,
      warehouseQuery = false,
      logisticsQuery = false,
    } = req.query;

    let processedField = null;

    if (adminQuery) processedField = "processed_admin";
    if (sellerQuery) processedField = "processed_seller";
    if (driverQuery) processedField = "processed_driver";
    if (warehouseQuery) processedField = "processed_warehouse";
    if (logisticsQuery) processedField = "processed_logistics";

    if (!processedField) {
      return res.status(400).json({ message: "Invalid notification consumer" });
    }

    const baseWhere = {
      [processedField]: false,
    };

    if (seller_id && seller_id.trim() !== "") {
      baseWhere.seller_id = seller_id;
    }
    if (logistics_id && logistics_id.trim() !== "") {
      baseWhere.logistics_id = logistics_id;
    }

    const events = await OrderStatusEvents.findAll({
      where: baseWhere,
      attributes: ["id", "new_status"],
      raw: true,
      transaction,
    });

    const response = {
      hasUnprocessedEvents: events.length > 0,
      adminNotification: false,
      sellerNotification: false,
      warehouseNotification: false,
      driverNotification: false,
      logisticsNotification: false,
    };

    for (const e of events) {
      if (["Pending", "Verified"].includes(e.new_status)) {
        response.adminNotification = true;
      }
      if (["Verified"].includes(e.new_status)) {
        response.sellerNotification = true;
      }
      if (["For Delivery - Warehouse"].includes(e.new_status)) {
        response.driverNotification = true;
      }
      if (["Release Order - Delivery"].includes(e.new_status)) {
        response.logisticsNotification = true;
      }
      if (
        [
          "Release Order - Customer Pickup",
          "Release Order - Delivery",
        ].includes(e.new_status)
      ) {
        response.warehouseNotification = true;
      }
    }

    if (events.length > 0) {
      const ids = events.map((e) => e.id);

      await OrderStatusEvents.update(
        { [processedField]: true },
        {
          where: { id: ids },
          transaction,
        },
      );
    }

    await transaction.commit();
    return res.json(response);
  } catch (err) {
    await transaction.rollback();
    next(err);
  }
};

exports.notificationService = async (req, res, next) => {
  const transaction = await sequelizeConnection.transaction();

  try {
    const {
      admin_id = "",
      seller_id = "",
      seller_account_id = "",
      adminQuery,
      sellerQuery,
      warehouseQuery,
      orderQuery,
    } = req.query;

    let statusFilter = [];
    let adminReadsBaseWhere = {};
    let baseWhere = {};

    if (adminQuery) {
      statusFilter = ["Pending", "Verified"];
      adminReadsBaseWhere = {
        user_id: admin_id,
      };
    } else if (sellerQuery) {
      statusFilter = ["Verified"];
      adminReadsBaseWhere = {
        seller_account_id: seller_account_id,
      };
      baseWhere = {
        seller_id: seller_id,
      };
    } else if (warehouseQuery) {
      statusFilter = [
        "Release Order - Customer Pickup",
        "Release Order - Delivery",
      ];
      adminReadsBaseWhere = {
        seller_account_id: seller_account_id,
      };
      baseWhere = {
        seller_id: seller_id,
      };
    } else if (orderQuery) {
      statusFilter = ["Pending", "Verified"];
      baseWhere = {
        processed_orders: false,
      };
    }

    const events = await OrderStatusEvents.findAll({
      where: {
        new_status: statusFilter,
        ...baseWhere,
      },
      attributes: ["id", "new_status"],
      raw: true,
      transaction,
    });

    const eventsToMarkRead = [];
    const eventIds = events.map((e) => e.id);

    if (orderQuery && eventIds.length > 0) {
      await OrderStatusEvents.update(
        { processed_orders: true },
        {
          where: { id: eventIds },
          transaction,
        },
      );
    }

    if (!orderQuery) {
      for (const event of events) {
        const alreadyRead = await OrderStatusEventsAdminReads.findOne({
          where: {
            ...adminReadsBaseWhere,
            order_status_events_id: event.id,
          },
          transaction,
        });

        if (!alreadyRead) {
          eventsToMarkRead.push({
            ...adminReadsBaseWhere,
            order_status_events_id: event.id,
            is_read: true,
          });
        }
      }
    }

    const response = {
      hasUnprocessedEvents: events.length > 0,
      notificationLength: eventsToMarkRead.length,
      adminNotification: false,
      sellerNotification: false,
      warehouseNotification: false,
      orderNotification: false,
    };
    if (eventsToMarkRead.length > 0) {
      await OrderStatusEventsAdminReads.bulkCreate(eventsToMarkRead, {
        transaction,
      });
      if (adminQuery) response.adminNotification = true;
      if (sellerQuery) response.sellerNotification = true;
      if (warehouseQuery) response.warehouseNotification = true;
    }
    if (orderQuery && eventIds.length > 0) {
      response.orderNotification = true
      response.notificationLength = eventIds.length;
    };

    await transaction.commit();

    return res.status(200).json(response);
  } catch (err) {
    await transaction.rollback();
    next(err);
  }
};

exports.syncProductsAndSellers = async (req, res, next) => {
  try {
    if (req.redisClient && req.redisClient.isConnected) {
      await req.redisClient.resetAll();
      return res
        .status(200)
        .json({ success: true, message: "Cache reset successfully" });
    } else {
      return res.status(500).json({
        success: false,
        message: "Redis client not available or not connected",
      });
    }
  } catch (err) {
    next(err);
  }
};
