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");
const ProductImages = require("../../models/ProductImages");
const { roundMoney } = require("../../helpers/common");

exports.getSelfCart = async (req, res, next) => {
  const { customerId } = req;
  UserCart.findAll({
    where: {
      user_id: customerId,
    },
    include: [
      {
        model: Products,
        as: "product",
        attributes: {
          exclude: getProductUserCartExcludes(),
        },
        include: [
          {
            model: ProductImages,
            as: "product_images",
          },
          {
            model: Products,
            as: "parent",
            include: [
              {
                model: ProductImages,
                as: "product_images",
              },
              {
                model: Products,
                as: "parent",
                include: [
                  {
                    model: ProductImages,
                    as: "product_images",
                  },
                ],
              },
            ],
          },
        ],
      },
    ],
    attributes: {
      exclude: getUserCartExcludes(),
    },
  })
    .then(async (cart) => {
      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,
      );

      let cartTotalPrice = 0;
      const _userCart = cart.map(
        ({ id, quantity, created_at, price, product }) => {
          const parentChain = [];
          let current = product;

          if (current.parent) {
            parentChain.push(current.parent);
            if (current.parent.parent) {
              parentChain.push(current.parent.parent);
            }
          }

          const lastParent =
            parentChain.length > 0
              ? parentChain[parentChain.length - 1]
              : product;

          const _discountedPrice = parseFloat(product.discounted_price) || 0;
          const _itemPrice = parseFloat(product.price) || 0;

          const priceToUse =
            _discountedPrice > 0 ? _discountedPrice : _itemPrice;

          const productImages = [];
          if (
            lastParent.product_images &&
            Array.isArray(lastParent.product_images) &&
            lastParent.product_images.length > 0
          ) {
            for (const image of lastParent.product_images) {
              productImages.push({
                id: image.id,
                url: sellerProductImageBuilder(image.file_name, true),
                is_thumbnail: image.is_thumbnail,
              });
            }
          }

          let photo = product.photo;
          if (!photo) {
            const mainImage = productImages.find((img) => img.is_thumbnail);
            if (mainImage) {
              photo = mainImage.url;
            }
          } else {
            photo = sellerProductImageBuilder(
              photo,
              product.seller_dashboard_src,
            );
          }

          const buildParentTree = (p) => {
            if (!p) return null;
            return {
              id: p.id,
              menu: p.menu,
              parent_id: p.parent_id,
              product_images: p.product_images?.map((img) => ({
                id: img.id,
                url: sellerProductImageBuilder(img.file_name, true),
                is_thumbnail: img.is_thumbnail,
              })),
              parent: buildParentTree(p.parent),
            };
          };

          const _product = {
            id: product.id,
            parent_id: product.parent_id,
            available: product.available,
            description: product.description,
            discounted_price: product.discounted_price,
            menu: product.menu,
            menu_id: product.menu_id,
            photo,
            price: product.price,
            product_images: productImages,
            product_status: product.product_status,
            quantity_limit: product.quantity_limit,
            seller_dashboard_src: product.seller_dashboard_src,
            seller_id: product.seller_id,
            sku: product.sku,
            stocks: product.stocks,
            type_id: product.type_id,
            parent: buildParentTree(product.parent),
          };

          // const cartItemTotal = parseFloat(priceToUse) * parseFloat(quantity);
          const priceCents = Math.round(Number(priceToUse) * 100);
          const cartItemTotalCents = priceCents * Number(quantity);
          const cartItemTotal = cartItemTotalCents / 100;
          cartTotalPrice += roundMoney(cartItemTotal);
          return {
            id,
            quantity,
            price: roundMoney(cartItemTotal),
            product: _product,
            created_at,
          };
        },
      );
      for (let userCart of cart) {
        userCart.product.photo = sellerProductImageBuilder(
          userCart.product.photo,
          userCart.product.seller_dashboard_src,
        );
      }

      res.status(200).json({
        success: true,
        cart: _userCart,
        total_price: cartTotalPrice,
      });
    })
    .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(
    parseFloat(quantity) - parseFloat(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 =
        parseFloat(_productVariationStocks) - parseFloat(newStocksDifference);
      _productVariation.stocks = parseFloat(_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 =
        parseFloat(_productSubVariationStocks) - parseFloat(newStocksDifference);
      _productSubVariation.stocks = parseFloat(_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);
  }
};
