const Sequelize = require("sequelize");
const Products = require("../../models/Products");
const ProductMenu = require("../../models/ProductMenu");
const ProductVariation = require("../../models/ProductVariation");
const ProductSubVariation = require("../../models/ProductSubVariation");
const ProductImages = require("../../models/ProductImages");
const productMapper = require("../../helpers/product-mapper");

exports.getAllSellerProducts = async (req, res, next) => {
  const { seller_id } = req;
  const { name_sort = null, price_sort = null, stock_sort = null, product_name = null } = req.query;
  const { limit, page } = req.pagination;

  const whereClause = {
    seller_id,
    available: true,
  };
  const orderClause = [];

  if (product_name) {
    const productNameWhereClause = {
      [Sequelize.Op.like]: `%${product_name}%`,
    };

    whereClause.menu = productNameWhereClause;
  }
  if (name_sort) orderClause.push(["menu", name_sort]);
  if (price_sort) orderClause.push(["price", price_sort]);
  if (stock_sort) orderClause.push(["stocks", stock_sort]);

  const offset = (page - 1) * limit;

  Products.findAll({
    where: whereClause,
    limit,
    offset,
    include: [
      {
        model: ProductMenu,
        as: "menus",
        attributes: {
          exclude: ["upload_photo", "status"],
        },
      },
      {
        model: ProductVariation,
        as: "product_variations",
        include: [
          {
            model: ProductSubVariation,
            as: "ProductSubVariations",
            required: false,
          },
        ],
      },
      {
        model: ProductImages,
        as: "product_images",
      },
    ],
    order: orderClause,
  })
    .then(async (sellerProducts) => {
      const _sellerProducts = productMapper(sellerProducts);

      const productsCount = await Products.count({
        where: whereClause,
      });

      const totalPages = Math.ceil(productsCount / limit);
      const hasNextPage = (page - 1) * limit + _sellerProducts.length < totalPages;
      const hasPreviousPage = page > 1;

      res.status(200).json({
        success: true,
        products: _sellerProducts,
        pagination: {
          totalPages,
          currentPage: page,
          hasNextPage,
          hasPreviousPage,
        },
      });
    })
    .catch((err) => {
      next(err);
    });
};

exports.getSellerProductById = (req, res, next) => {
  const { seller_id } = req;
  const { product_id } = req.params;
  Products.findOne({
    where: {
      seller_id,
      available: true,
      id: product_id,
    },
    include: [
      {
        model: ProductMenu,
        as: "menus",
        attributes: {
          exclude: ["upload_photo", "status"],
        },
      },
      {
        model: ProductVariation,
        as: "product_variations",
        include: [
          {
            model: ProductSubVariation,
            as: "ProductSubVariations",
            required: false,
          },
        ],
      },
      {
        model: ProductImages,
        as: "product_images",
      },
    ],
  }).then((sellerProduct) => {
    if (!sellerProduct) return res.status(404).json({ success: false, msg: "Product not found." });
    const mappedProduct = productMapper(sellerProduct);
    res.status(200).json({ success: true, product: mappedProduct });
  });
};

exports.createSellerProduct = async (req, res, next) => {
  const { seller_id } = req;
  const { data } = req.body;
  const { product_images } = req.files;
  const { product_images_variation } = req.files;
  const { product_images_sub_variation } = req.files;

  const {
    name,
    description,
    price,
    discounted_price = 0.0,
    quantity_limit,
    stocks,
    available = true,
    sku,
    category_id,
    variations,
  } = data;

  const selectedCategory = await ProductMenu.findOne({
    where: {
      id: category_id,
      status: true,
    },
  });

  if (!selectedCategory) return res.status(404).json({ success: false, msg: "Invalid category" });

  Products.create({
    menu_id: selectedCategory.id,
    seller_id,
    menu: name,
    description,
    price,
    discounted_price,
    quantity_limit,
    stocks,
    available,
    sku,
    seller_dashboard_src: true,
  })
    .then(async (createdProduct) => {
      // Product Images
      const productImagesArray = product_images?.map(({ filename, path }) => ({
        product_id: createdProduct.id,
        file_name: filename,
        file_path: path,
      }));
      if (productImagesArray) {
        const productImages = await ProductImages.bulkCreate(productImagesArray);
        createdProduct.photo =
          productImagesArray && Array.isArray(productImagesArray) && productImagesArray.length > 0
            ? productImagesArray[0]?.file_name
            : null;
        await createdProduct.save();
      }
      // Product Images

      const productVariations = variations.map(
        async ({
          name,
          description,
          price,
          discounted_price,
          has_sub_variation,
          quantity_limit,
          stocks,
          sku,
          is_available,
          photo,
          product_sub_variation,
        }) => {
          const productImageVariationFile = product_images_variation?.find(
            (variationImage) => variationImage.originalname === photo
          );
          const createdProductVariation = await ProductVariation.create({
            product_id: createdProduct.id,
            variation: name,
            description,
            quantity_limit,
            price,
            discounted_price,
            hasSubVariation: has_sub_variation,
            isAvailable: is_available,
            variationImage: productImageVariationFile ? productImageVariationFile.filename : "",
            sku,
            stocks,
          });

          const productSubVariations = product_sub_variation?.map(
            ({ name, description, price, discounted_price, quantity_limit, stocks, sku, is_available, photo }) => ({
              product_variation_id: createdProductVariation.id,
              custom: name,
              description,
              price,
              discounted_price,
              quantity_limit,
              stocks,
              sku,
              isAvailable: is_available,
              photo: product_images_sub_variation
                ? product_images_sub_variation?.find((subVariationImage) => subVariationImage.originalname === photo)
                    .filename
                : "",
            })
          );

          if (productSubVariations) {
            const _productSubVariations = await ProductSubVariation.bulkCreate(productSubVariations);
          }
        }
      );

      res.status(201).json({ success: true, msg: "Product created", product_id: createdProduct.id });
    })
    .catch((err) => {
      next(err);
    });
};

exports.updateSellerProduct = async (req, res, next) => {
  const { seller_id } = req;
  const { product_id } = req.params;
  const {
    name,
    description,
    price,
    discounted_price,
    quantity_limit,
    stocks,
    available = true,
    sku,
    category_id,
  } = req.body;

  const updateJson = {
    menu: name,
    description: description,
    price: price,
    discounted_price: discounted_price,
    quantity_limit: quantity_limit,
    stocks: stocks,
    available: available,
    sku: sku,
    category_id: category_id,
  };

  try {
    const filteredUpdateJson = Object.fromEntries(
      Object.entries(updateJson).filter(([_, value]) => value != null && value !== "")
    );

    // Check if there's category id in the filtered update json
    const isUpdatingCategory = !!filteredUpdateJson.category_id;
    if (isUpdatingCategory) {
      const selectedCategory = await ProductMenu.findOne({
        where: {
          id: filteredUpdateJson.category_id,
          status: true,
        },
      });

      if (!selectedCategory) return res.status(404).json({ success: false, msg: "Invalid category" });
    }

    // Check for seller product
    const productQuery = await Products.findOne({
      where: {
        seller_id: seller_id,
        id: product_id,
      },
    });

    if (!productQuery) return res.status(404).json({ success: false, msg: "Invalid product." });

    await Products.update(filteredUpdateJson, {
      where: {
        seller_id: seller_id,
        id: product_id,
      },
    });

    res.status(200).json({ success: true, msg: "Product updated.", updatedProperties: filteredUpdateJson });
  } catch (err) {
    next(err);
  }
};

exports.deleteSellerProduct = async (req, res, next) => {
  const { seller_id } = req;
  const { product_id } = req.params;

  try {
    const product = await Products.findOne({
      where: {
        seller_id,
        id: product_id,
        available: true,
      },
    });

    if (!product) return res.status(404).json({ success: false, msg: "Invalid product id" });

    product.available = false;
    await product.save();

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

exports.updateProductPhoto = async (req, res, next) => {
  try {
    const { seller_id } = req;
    const { product_id, photo_id = "000" } = req.params;

    if (!req.files || !req.files.product_image_update) {
      return res.status(400).json({ success: false, msg: "Empty product image" });
    }

    const productFile = req.files.product_image_update[0];
    if (!productFile) {
      return res.status(400).json({ success: false, msg: "Invalid file upload" });
    }

    const { filename, path } = productFile;

    const productTobeUpdated = await Products.findOne({
      where: { seller_id, id: product_id, available: true },
    });

    if (!productTobeUpdated) {
      return res.status(404).json({ success: false, msg: "Invalid product id" });
    }

    if (photo_id !== "000") {
      const productImage = await ProductImages.findByPk(photo_id);
      if (!productImage) {
        return res.status(404).json({ success: false, msg: "Invalid product image id" });
      }

      productImage.file_name = filename;
      productImage.file_path = path;
      await productImage.save();

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

    productTobeUpdated.photo = filename;
    productTobeUpdated.seller_dashboard_src = true;
    await productTobeUpdated.save();

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