const UserCart = require("../../models/UserCart");
const Products = require("../../models/Products");
const ProductMenu = require("../../models/ProductMenu");
const ProductsVariation = require("../../models/ProductVariation");
const ProductsSubVariation = require("../../models/ProductSubVariation");
const { getUserCartExcludes, getProductUserCartExcludes } = require("../../helpers/model-excludes-attributes/excludes");
const ProductVariation = require("../../models/ProductVariation");
const ProductSubVariation = require("../../models/ProductSubVariation");
const Sequelize = require("sequelize");
const { sellerProductImageBuilder } = require("../../helpers/image-src-builder");

exports.getSelfCart = async (req, res, next) => {
  const { customerId } = req;
  UserCart.findAll({
    where: {
      user_id: customerId,
    },
    include: [
      {
        model: Products,
        as: "product",
        attributes: {
          exclude: getProductUserCartExcludes(),
        },
      },
      {
        model: ProductsVariation,
        as: "product_variation",
      },
      {
        model: ProductsSubVariation,
        as: "product_sub_variation",
      },
    ],
    attributes: {
      exclude: getUserCartExcludes(),
    },
  })
    .then(async (cart) => {
      const _totalCartPrice = await UserCart.findAll({
        where: {
          user_id: customerId,
        },
        // attributes: ["user_id", [Sequelize.fn("sum", Sequelize.literal("price")), "total_cart_price"]], // Uncomment this to support v3 Checkout and Add to Cart
        attributes: ["user_id", [Sequelize.fn("sum", Sequelize.literal("price * quantity")), "total_cart_price"]],
      });

      const __totalCartPrice = parseFloat(_totalCartPrice[0].dataValues.total_cart_price);

      const _userCart = cart.map(
        ({
          id,
          quantity,
          has_sub_variation_chosen,
          created_at,
          price,
          product,
          product_variation,
          produce_sub_variation,
        }) => {
          console.log(price);
          return {
            id,
            quantity,
            // price: parseFloat(price), // Uncomment this to support v3 Checkout and Add to Cart
            price: parseFloat(price) * parseFloat(quantity),
            has_sub_variation_chosen,
            product,
            product_variation,
            produce_sub_variation,
            created_at,
          };
        }
      );
      for (let userCart of cart) {
        // userCart.sub_total = parseFloat(userCart.price) * parseFloat(userCart.price);
        userCart.product.photo = sellerProductImageBuilder(
          userCart.product.photo,
          userCart.product.seller_dashboard_src
        );
      }
      res.status(200).json({ success: true, cart: _userCart, total_price: __totalCartPrice });
    })
    .catch((err) => {
      next(err);
    });
};

exports.createUserCart = async (req, res, next) => {
  try {
    const customerId = req.customerId;
    const { data } = req.body;

    const response = {
      success: "User Cart successfully created!",
      newUserCarts: [],
      totalCartPrice: 0,
    };

    for (const item of data) {
      const { menu_id, quantity, variation_id, sub_variation_id } = item;

      const product = await Products.findByPk(menu_id, {
        include: [],
      });
      if (!product) {
        return res.status(404).json({ error: `Product with menu_id ${menu_id} not found` });
      }

      if (product.stocks < quantity) {
        return res.status(400).json({ error: `Not enough stocks available for product with menu_id ${menu_id}` });
      }

      if (product.quantity_limit < quantity) {
        return res.status(400).json({ error: `Quantity limit reached! Allowed qty:  ${product.quantity_limit}` });
      }

      let chosenVariation = null;
      let chosenSubVariation = null;

      if (variation_id) {
        chosenVariation = await ProductsVariation.findOne({
          where: {
            id: variation_id,
            product_id: menu_id,
          },
        });

        if (!chosenVariation) {
          return res.status(404).json({ error: `Chosen variation not found for product with menu_id ${menu_id}` });
        }

        if (chosenVariation.hasSubVariation && sub_variation_id) {
          chosenSubVariation = await ProductsSubVariation.findOne({
            where: {
              id: sub_variation_id,
              product_variation_id: variation_id,
            },
          });

          if (!chosenSubVariation) {
            return res.status(404).json({
              error: `Chosen sub-variation not found or does not belong to the chosen variation for product with menu_id ${menu_id}`,
            });
          }
        }
      }

      const pricePerItem = chosenSubVariation
        ? chosenSubVariation.discounted_price !== null
          ? chosenSubVariation.discounted_price
          : chosenSubVariation.price
        : chosenVariation
        ? chosenVariation.discounted_price !== null
          ? chosenVariation.discounted_price
          : chosenVariation.price
        : product.price;

      const totalPriceForItem = parseFloat(pricePerItem) * parseFloat(quantity);
      response.totalCartPrice += totalPriceForItem;

      const currentDateTime = new Date();

      const newUserCart = await UserCart.create({
        user_id: customerId,
        menu_id: menu_id,
        quantity: quantity,
        has_sub_variation_chosen: chosenSubVariation ? "true" : "false",
        product_variation_id: chosenVariation ? chosenVariation.id : null,
        product_sub_variation_id: chosenSubVariation ? chosenSubVariation.id : null,
        created_at: currentDateTime,
        price: pricePerItem,
      });

      if (product.stocks >= quantity) {
        // product.stocks -= quantity;
        // await product.save();
      }

      response.newUserCarts.push({
        user_cart_id: newUserCart.id,
        user_id: newUserCart.user_id,
        product_id: newUserCart.menu_id,
        product_name: product.menu,
        quantity: newUserCart.quantity,
        has_sub_variation_chosen: newUserCart.has_sub_variation_chosen,
        product_variation_id: newUserCart.product_variation_id,
        product_sub_variation_id: newUserCart.product_sub_variation_id,
        created_at: newUserCart.created_at,
        price: pricePerItem,
        total_price_for_item: totalPriceForItem,
        remaining_stock: product.stocks,
      });
    }

    res.status(201).json(response);
  } catch (err) {
    next(err);
  }
};

const getProductDetails = async (product_id, customerId) => {
  const product = await Products.findByPk(product_id, {
    include: [
      {
        model: ProductMenu,
        as: "menus",
      },
    ],
  });

  if (!product) throw new Error("Product id not found.");

  const newCartItemSizeCategory = product.menus.size_category;
  const cartSizeCategory = await getUserCart(customerId);

  if (newCartItemSizeCategory !== cartSizeCategory && cartSizeCategory !== "None")
    throw new Error(`Cart items size category not matched. Current Item Size Category: ${cartSizeCategory}`);
  return product;
};

const getUserCart = async (user_id) => {
  const userCart = await UserCart.findOne({
    where: {
      user_id,
    },
    include: [
      {
        model: Products,
        as: "product",
        include: [
          {
            model: ProductMenu,
            as: "menus",
          },
        ],
      },
    ],
  });

  let sizeCategory = "None";
  if (userCart) {
    sizeCategory = userCart.product.menus.size_category;
  }
  return sizeCategory;
};

const getVariationDetails = async (variation_id) => {
  const variation = await ProductVariation.findByPk(variation_id);
  if (!variation) throw new Error("Product Variation Id not found.");
  return variation;
};

const getSubVariationDetails = async (variation_id) => {
  const variation = await ProductSubVariation.findByPk(variation_id);
  if (!variation) throw new Error("Product Variation Id not found.");
  return variation;
};

const checkStockAndQuantity = async (item, quantity) => {
  const { stocks, quantity_limit, discounted_price, price } = item;
  const productPrice = discounted_price > 0 ? discounted_price : price;

  if (stocks > quantity && quantity_limit > quantity) {
    const itemSubTotal = productPrice * quantity;
    return { productPrice, itemSubTotal };
  } else {
    throw new Error(`Product stock insufficient or quantity limit reached for item id: ${item.id}.`);
  }
};

exports.addUserCart = async (req, res, next) => {
  const { customerId } = req;
  const { data } = req.body;

  let totalCartPrice = 0;
  try {
    const newUserCart = await Promise.all(
      data.map(async ({ menu_id: product_id, quantity, variation_id, sub_variation_id }) => {
        let product, productPrice;

        if (!variation_id && !sub_variation_id) {
          product = await getProductDetails(product_id, customerId);
          productPrice = await checkStockAndQuantity(product, quantity);
        } else if (variation_id && !sub_variation_id) {
          product = await getVariationDetails(variation_id);
          productPrice = await checkStockAndQuantity(product, quantity);
        } else if (variation_id && sub_variation_id) {
          product = await getSubVariationDetails(sub_variation_id);
          productPrice = await checkStockAndQuantity(product, quantity);
        }

        const { productPrice: _productPrice, itemSubTotal } = productPrice;
        totalCartPrice += itemSubTotal;
        const _newUserCart = await UserCart.create({
          user_id: customerId,
          menu_id: product_id,
          quantity,
          has_sub_variation_chosen: sub_variation_id ? "true" : "false",
          product_variation_id: variation_id ? variation_id : null,
          product_sub_variation_id: sub_variation_id ? sub_variation_id : null,
          created_at: new Date(),
          price: parseFloat(_productPrice),
        });

        return {
          user_cart_id: _newUserCart.id,
          user_id: _newUserCart.user_id,
          product_id: _newUserCart.menu_id,
          product_name: product.menu,
          quantity: _newUserCart.quantity,
          has_sub_variation_chosen: _newUserCart.has_sub_variation_chosen,
          product_variation_id: _newUserCart.product_variation_id,
          product_sub_variation_id: _newUserCart.product_sub_variation_id,
          created_at: _newUserCart.created_at,
          price: _productPrice,
          total_price_for_item: itemSubTotal,
          remaining_stock: product.stocks,
        };
      })
    );

    const _totalCartPrice = await UserCart.findAll({
      where: {
        user_id: customerId,
      },
      attributes: ["user_id", [Sequelize.fn("sum", Sequelize.literal("price * quantity")), "total_cart_price"]],
    });

    const __totalCartPrice = parseFloat(_totalCartPrice[0].dataValues.total_cart_price);

    res
      .status(200)
      .json({ newUserCart: newUserCart, totalCartPrice: __totalCartPrice, success: "User Cart successfully created!" });
  } catch (error) {
    next(error);
  }
};

exports.updateUserCart = async (req, res, next) => {
  const { customerId } = req;
  const { quantity } = req.body;
  const { user_cart_id } = req.params;

  const userCart = await UserCart.findOne({
    where: {
      user_id: customerId,
      id: user_cart_id,
    },
  });

  const newStocksDifference = Math.abs(parseInt(quantity) - parseInt(userCart.quantity));
  if (!userCart) return res.status(404).json({ success: false, msg: "User cart id not found." });

  const hasSubVariation = userCart.has_sub_variation_chosen === "true";
  const isProductVariation = userCart.product_variation_id !== null;
  const isProductSubVariation = userCart.product_sub_variation_id !== null;

  if (isProductVariation && !isProductSubVariation) {
    const _productVariation = await ProductVariation.findByPk(userCart.product_variation_id);
    if (!_productVariation) {
      return res.status(404).json({ success: false, msg: "Product Variation Id not found." });
    }
    const _productVariationStocks = _productVariation.stocks;

    if (newStocksDifference > _productVariationStocks) {
      return res.status(200).json({ success: false, msg: "Insufficient stocks." });
    } else {
      const _productVariationNewStocks = parseInt(_productVariationStocks) - parseInt(newStocksDifference);
      _productVariation.stocks = parseInt(_productVariationNewStocks);
      const __newProductVariation = await _productVariation.save();
    }
  }

  if (isProductVariation && isProductSubVariation) {
    const _productSubVariation = await ProductSubVariation.findByPk(userCart.product_sub_variation_id);
    if (!_productSubVariation) {
      return res.status(404).json({ success: false, msg: "Product Variation Id not found." });
    }
    const _productSubVariationStocks = _productSubVariation.stocks;

    if (newStocksDifference > _productSubVariationStocks) {
      return res.status(200).json({ success: false, msg: "Insufficient stocks." });
    } else {
      const _productSubVariationNewStocks = parseInt(_productSubVariationStocks) - parseInt(newStocksDifference);
      _productSubVariation.stocks = parseInt(_productSubVariationNewStocks);
      const __newProductSubVariation = await _productSubVariation.save();
    }
  }

  userCart.quantity = quantity;
  const updatedUserCart = await userCart.save();

  res.status(200).json({ success: true, updatedUserCart });
};

exports.removeUserCartByIds = async (req, res, next) => {
  try {
    const { customerId } = req;
    const { cart_ids } = req.body;

    if (!cart_ids || !Array.isArray(cart_ids) || cart_ids.length === 0) {
      return res.status(400).json({ success: false, message: "Cart IDs are missing or invalid." });
    }

    const deletedCount = await UserCart.destroy({
      where: {
        id: cart_ids,
        user_id: customerId,
      },
    });

    if (deletedCount === 0) {
      return res.status(404).json({ success: false, message: "No cart items found to remove." });
    }

    return res.status(200).json({ success: true, message: "Cart items removed successfully." });
  } catch (err) {
    next(err);
  }
};
