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");
const { createAuditTrail } = require("../../helpers/sellerAuditTrail.service");
const {
  sellerEnabledPurchaseOrders,
} = require("../../services/purchase-order.service");

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

  const whereClause = {
    seller_id,
    product_status: true,
    [Sequelize.Op.or]: [{ parent_id: null }, { parent_id: "" }],
    // available: true,
  };
  const orderClause = [];

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

    whereClause.menu = productNameWhereClause;
  }

  if (category && category !== "" && category !== "null") {
    whereClause.menu_id = category;
  }

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

  if (!name_sort && !price_sort && !stock_sort) {
    orderClause.push(["id", "desc"]);
  }
  const offset = (page - 1) * limit;

  Products.findAll({
    where: whereClause,
    limit,
    offset,
    include: [
      {
        model: ProductMenu,
        as: "menus",
        attributes: {
          exclude: ["upload_photo", "status"],
        },
      },
      {
        model: ProductImages,
        as: "product_images",
      },
    ],
    order: orderClause,
    distinct: true,
  })
    .then(async (sellerProducts) => {
      const sellerEnabledPO = await sellerEnabledPurchaseOrders(seller_id);

      const _sellerProducts = await productMapper(
        sellerProducts,
        sellerEnabledPO
      );
      console.log(_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;

      await createAuditTrail({
        seller_id: seller_account_id,
        seller_company_id: seller_id,
        module: "Products",
        action: "view",
        action_description: ``,
      });

      res.status(200).json({
        success: true,
        products: _sellerProducts,
        pagination: {
          totalPages,
          currentPage: page,
          hasNextPage,
          hasPreviousPage,
        },
        enabledPO: sellerEnabledPO,
      });
    })
    .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,
      product_status: 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, seller_account_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
            );
          }
        }
      );

      await createAuditTrail({
        seller_id: seller_account_id,
        seller_company_id: seller_id,
        module: "Products",
        action: "add",
        action_description: `Created new product: ${createdProduct.id}`,
      });
      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, seller_account_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,
        product_status: true,
      },
    });

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

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

    await createAuditTrail({
      seller_id: seller_account_id,
      seller_company_id: seller_id,
      module: "Products",
      action: "update",
      action_description: `Update product id: ${product_id}, ${JSON.stringify(
        filteredUpdateJson
      )}`,
    });

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

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

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

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

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

    await createAuditTrail({
      seller_id: seller_account_id,
      seller_company_id: seller_id,
      module: "Products",
      action: "delete",
      action_description: `Deleted product id: ${product_id}`,
    });

    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, product_status: 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);
  }
};
