import { Product } from "./ProductModel.js";
import cloudinary from "../../Utils/cloudinary.js";
import { v4 as uuidv4 } from "uuid";
import { CategoryModel } from "../Category/CategoryModel.js";
import { BrandModel } from "../Brands/BrandsModel.js";
import XLSX from "xlsx";
import fs from "fs";
import path from "path";
import mongoose from "mongoose";

// Function to handle product upload
export const uploadProducts = async (req, res) => {
  try {
    if (!req.files || !req.files.file) {
      return res.status(400).json({ message: "No file uploaded" });
    }

    const file = req.files.file;
    const filePath = path.join("public", "uploads", file.name);

    // Ensure 'uploads' directory exists
    if (!fs.existsSync(path.dirname(filePath))) {
      fs.mkdirSync(path.dirname(filePath), { recursive: true });
    }

    // Move the file from temp to the uploads directory
    await file.mv(filePath);

    // Process the file
    const fileBuffer = fs.readFileSync(filePath);
    const workbook = XLSX.read(fileBuffer, { type: "buffer" });
    const sheetName = workbook.SheetNames[0];
    const worksheet = workbook.Sheets[sheetName];
    const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 });

    if (data.length <= 1) {
      return res
        .status(400)
        .json({ message: "Empty spreadsheet or no data found" });
    }

    const headers = data[0];

    // Map headers from the Excel file to your schema
    const headerMapping = {
      SKU: "SKU",
      "Product Name": "name",
      "Category Name": "category",
      "Brand Name": "brand",
      Price: "price",
      "GST (in %)": "GST",
      "HSN Code": "HSN_Code",
      "Description (Optional)": "description",
    };

    const requiredHeaders = Object.keys(headerMapping);

    if (!requiredHeaders.every((header) => headers.includes(header))) {
      return res
        .status(400)
        .json({ message: "Missing required columns in spreadsheet" });
    }

    const errors = [];
    const newlyCreated = [];
    const updatedProducts = [];

    const capitalizeWords = (str) =>
      str
        .toLowerCase()
        .replace(/\b\w/g, (char) => char.toUpperCase())
        .trim();

    for (let i = 1; i < data.length; i++) {
      const row = data[i];
      const item = {};

      headers.forEach((header, index) => {
        if (headerMapping[header]) {
          item[headerMapping[header]] =
            row[index] !== undefined ? row[index] : "";
        }
      });

      // Check if the row has meaningful data, skip if it's mostly empty
      const hasValidData = Object.values(item).some((value) => value && value.trim());
      if (!hasValidData) {
        continue;
      }

      // Initialize error tracking for each item
      const missingFields = new Set();

      let { SKU, name, category, brand, price, GST, HSN_Code, description } =
        item;

      // Trim leading and trailing spaces and apply case formatting
      name = name ? name.trim() : "";
      category = category ? capitalizeWords(category) : "";
      brand = brand ? capitalizeWords(brand) : "";

      // Validate required fields
      if (!SKU) missingFields.add("SKU");
      if (!name) missingFields.add("name");
      if (!category) missingFields.add("category");
      if (!brand) missingFields.add("brand");
      if (price === undefined || price === "") missingFields.add("price");
      if (!GST) missingFields.add("GST");
      if (!HSN_Code) missingFields.add("HSN_Code");

      // Validate or create category
      let categoryDoc = null;
      let categoryname = "";
      if (category) {
        categoryDoc = await CategoryModel.findOne({
          categoryName: { $regex: new RegExp(`^${category}$`, "i") },
        }).exec();

        if (!categoryDoc) {
          categoryDoc = await CategoryModel.create({
            categoryName: category,
            addedBy: req.user._id,
          });
        }
        categoryname = categoryDoc.categoryName;
        item.category = categoryDoc._id;
      }

      // Validate or create brand
      let brandDoc = null;
      let brandname = "";
      if (brand) {
        brandDoc = await BrandModel.findOne({
          brandName: { $regex: new RegExp(`^${brand}$`, "i") },
        }).exec();

        if (!brandDoc) {
          brandDoc = await BrandModel.create({
            brandName: brand,
            addedBy: req.user._id,
          });
        }
        brandname = brandDoc.brandName;
        item.brand = brandDoc._id;
      }

      // Combine all errors into a single message
      let errorMessage = "";
      if (missingFields.size > 0) {
        errorMessage += `Missing fields: ${Array.from(missingFields).join(
          ", "
        )}. `;
      }

      // If there are errors, push them to the errors array
      if (errorMessage.trim()) {
        errors.push({
          SKU: SKU || "N/A",
          productName: name || "N/A",
          category: categoryname || "N/A",
          brand: brandname || "N/A",
          GST: GST || "N/A",
          HSN_Code: HSN_Code || "N/A",
          price: price || "N/A",
          message: errorMessage.trim(),
        });
        continue;
      }

      // Ensure fields are set to empty strings if not provided
      description = description !== undefined ? description : "";

      // Check for existing product by SKU
      let existingProduct = await Product.findOne({ SKU }).exec();

      if (existingProduct) {
        // Track changes
        const updatedFields = [];
        let updatedProduct = { ...existingProduct._doc };

        // Update product fields if they have changed
        if (name !== existingProduct.name) {
          updatedFields.push("name");
          updatedProduct.name = name;
        }
        if (
          categoryDoc &&
          existingProduct.category.toString() !== item.category.toString()
        ) {
          updatedFields.push("category");
          updatedProduct.category = categoryname;
        } else {
          updatedProduct.category = categoryDoc.categoryName;
        }
        if (
          price !== undefined &&
          price !== "" &&
          existingProduct.price !== price
        ) {
          updatedFields.push("price");
          updatedProduct.price = price;
        }
        if (
          brandDoc &&
          existingProduct.brand.toString() !== item.brand.toString()
        ) {
          updatedFields.push("brand");
          updatedProduct.brand = brandname;
        } else {
          updatedProduct.brand = brandDoc.brandName;
        }
        if (HSN_Code !== existingProduct.HSN_Code) {
          updatedFields.push("HSN_Code");
          updatedProduct.HSN_Code = HSN_Code;
        }
        if (GST !== existingProduct.GST) {
          updatedFields.push("GST");
          updatedProduct.GST = GST;
        }
        if (description !== existingProduct.description) {
          updatedFields.push("description");
          updatedProduct.description = description;
        }

        // Only update if there are changes
        if (updatedFields.length > 0) {
          try {
            await Product.updateOne(
              { SKU: existingProduct.SKU },
              {
                $set: {
                  category: item.category || existingProduct.category,
                  brand: item.brand || existingProduct.brand,
                  price:
                    price !== undefined && price !== ""
                      ? price
                      : existingProduct.price,
                  GST: GST || existingProduct.GST,
                  HSN_Code: HSN_Code || existingProduct.HSN_Code,
                  name: name,
                  description: description,
                  product_Status:
                    item.product_Status ||
                    existingProduct.product_Status ||
                    "Active",
                },
              }
            );
            updatedProducts.push({
              ...updatedProduct,
              updatedFields: updatedFields.join(", "), // Track updated fields
            });
          } catch (error) {
            errors.push({
              SKU,
              message: "Failed to update product",
            });
          }
        }
        continue;
      }

      // Create new product
      if (item.category && item.brand) {
        const productData = {
          SKU,
          name,
          category: item.category,
          brand: item.brand,
          price,
          GST,
          HSN_Code,
          description: description,
          product_Status: item.product_Status || "Active",
          addedBy: req.user._id,
        };
        try {
          const newProduct = await Product.create(productData);
          newlyCreated.push({
            ...newProduct._doc,
            category: categoryDoc.categoryName,
            brand: brandDoc.brandName,
          });
        } catch (error) {
          errors.push({
            SKU,
            message: "Failed to create product",
          });
        }
      }
    }

    fs.unlinkSync(filePath); // Clean up uploaded file

    res.status(201).json({
      message:
        errors.length > 0
          ? "Products processed with errors!"
          : "Products processed successfully!",
      newlyCreated: newlyCreated,
      updatedProducts: updatedProducts,
      errors,
    });
  } catch (error) {
    console.error("Error uploading products:", error);
    res.status(500).json({ message: "Server error" });
  }
};

export const createProduct = async (req, res) => {
  try {
    let findProduct = "";
    let product = { _id: "" };
    const sku = req.body?.SKU;
    // console.log(req.body);
    if (!sku) {
      return res.status(400).json({ message: "SKU is required!" });
    }

    // Check if SKU exists in the request body
    if (req.body?.product_id) {
      findProduct = await Product.findById(req.body.product_id);
    }

    if (!findProduct) {
      // Check if a product with the same SKU already exists
      const existingProduct = await Product.findOne({
        SKU: { $regex: new RegExp(`^${sku}$`, "i") },
      }).exec();

      if (existingProduct) {
        return res
          .status(400)
          .json({ message: "Product with this SKU already exists!" });
      }

      // Add user ID to the request body
      req.body.addedBy = req.user._id;
      product = await Product.create(req.body);
    } else {
      // Check if another product with the same SKU exists, excluding the current one
      const existingProduct = await Product.findOne({
        _id: { $ne: findProduct._id },
        SKU: { $regex: new RegExp(`^${sku}$`, "i") },
      }).exec();

      if (existingProduct) {
        return res
          .status(400)
          .json({ message: "Product with this SKU already exists!" });
      }

      product = await Product.findByIdAndUpdate(req.body.product_id, req.body, {
        new: true,
      });
    }

    res.status(201).json({
      message: "Product details added/updated successfully!",
      product_id: product._id,
    });
  } catch (error) {
    res.status(500).json({
      message: error.message ? error.message : "Something went wrong!",
    });
  }
};

///////////////////////////////////////////////////////////////////////////////////////
export const updateProduct = async (req, res) => {
  try {
    // console.log("Product ID:", req.params.id);
    // console.log("Files:", req.files);
    // console.log("Body:", req.body);

    const { removedImages } = req.body;
    const files = req.files?.images || [];

    // Parse removedImages
    const removedImageIds = removedImages ? JSON.parse(removedImages) : [];

    // Find the product
    const product = await Product.findById(req.params.id);
    if (!product) {
      return res.status(404).json({ message: "Product not found!" });
    }

    // Existing images
    let existingImages = product.image || [];
    let newImagesLinks = [];

    // Ensure files is always an array
    const filesArray = Array.isArray(files) ? files : [files];

    // Process new images
    for (const file of filesArray) {
      if (file && file.tempFilePath) {
        // Check if file has tempFilePath
        try {
          const result = await cloudinary.v2.uploader.upload(
            file.tempFilePath,
            {
              folder: "chemiNova/product",
            }
          );
          newImagesLinks.push({
            public_id: result.public_id,
            url: result.secure_url,
          });
          // console.log("Uploaded image:", result.secure_url);
        } catch (uploadError) {
          console.error("Error uploading image:", uploadError);
          return res.status(500).json({
            message: "Failed to upload image",
            error: uploadError.message,
          });
        }
      }
    }

    // Remove images from Cloudinary and database
    // for (const public_id of removedImageIds) {
    //   try {
    //     await cloudinary.v2.uploader.destroy(public_id);
    //     // console.log("Deleted image from Cloudinary:", public_id);
    //   } catch (deleteError) {
    //     console.error("Error deleting image from Cloudinary:", deleteError);
    //     return res.status(500).json({ message: "Failed to delete image", error: deleteError.message });
    //   }
    // }

    // Filter out removed images
    const updatedImages = existingImages.filter(
      (img) => !removedImageIds.includes(img.public_id)
    );
    const allImages = [...updatedImages, ...newImagesLinks];

    // Update product
    product.image = allImages;
    await product.save();

    res
      .status(200)
      .json({ message: "Product updated successfully!", images: allImages });
  } catch (error) {
    console.error(error); // Log error for debugging
    res.status(500).json({ message: "Server error!", error: error.message });
  }
};

////////////////////////////////////////////////////////////////////////////
//get All Product
export const getAllProductAdmin = async (req, res) => {
  try {
    const PAGE_SIZE = parseInt(req.query.show) || 10;
    const page = parseInt(req.query.page) || 1;
    const skip = (page - 1) * PAGE_SIZE;

    let filter = {};

    if (req.query.name) {
      filter.name = {
        $regex: new RegExp(req.query.name, "i"),
      };
    }
    if (req.query.category) {
      filter.category = mongoose.Types.ObjectId(req.query.category);
    }
    if (req.query.brand) {
      filter.brand = mongoose.Types.ObjectId(req.query.brand);
    }

    const total = await Product.countDocuments(filter);

    const products = await Product.find(filter)
      .populate({
        path: "category addedBy brand",
        select: "categoryName name brandName",
      })
      .limit(PAGE_SIZE)
      .skip(skip)
      .sort({ createdAt: -1 })
      .exec();

    return res.status(200).json({
      success: true,
      total_data: total,
      total_pages: Math.ceil(total / PAGE_SIZE),
      products,
    });
  } catch (error) {
    console.error(error); // Add logging for better debugging
    res.status(500).json({
      success: false,
      msg: error.message || "Something went wrong!",
    });
  }
};

//get All Product User(website)
export const getAllProductUser = async (req, res) => {
  try {
    // Set default values for pagination
    const PAGE_SIZE = parseInt(req.query?.show || "10");
    const page = parseInt(req.query?.page || "1") - 1;
    let filter = {};

    // Filter by category name
    if (req.query?.category) {
      const category = await CategoryModel.findOne({
        categoryName: req.query.category,
      });
      if (category) {
        filter.category = category._id;
      } else {
        return res.status(400).json({
          success: false,
          msg: "Category not found!",
        });
      }
    }
    if (req.query.brand) {
      filter.brand = mongoose.Types.ObjectId(req.query.brand);
    }
    // Filter by SKU
    if (req.query?.SKU) {
      filter.SKU = req.query.SKU;
    }

    // Filter by product name using regex for case-insensitive partial matching
    if (req.query?.name) {
      filter.name = {
        $regex: new RegExp(req.query.name, "i"),
      };
    }

    // Only get active products
    filter.product_Status = "Active";

    // Get total count of filtered products
    const total = await Product.countDocuments(filter);

    // Retrieve products with pagination, filtering, and sorting
    const products = await Product.find(filter)
      .populate({
        path: "category addedBy brand",
        select: "categoryName name brandName",
      })
      .limit(PAGE_SIZE)
      .skip(PAGE_SIZE * page)
      .sort({ createdAt: -1 })
      .exec();

    // Check if any products were found
    if (products && products.length > 0) {
      return res.status(200).json({
        success: true,
        total_data: total,
        total_pages: Math.ceil(total / PAGE_SIZE),
        products,
      });
    } else {
      return res.status(404).json({
        success: false,
        msg: "No products found!",
      });
    }
  } catch (error) {
    return res.status(500).json({
      success: false,
      msg: error.message || "Something went wrong!",
    });
  }
};

//Change Product status
export const ChangeProductStatus = async (req, res) => {
  try {
    const data = await Product.findById(req.params.id);
    if (data) {
      if (data?.product_Status === "Active") {
        let product = await Product.findByIdAndUpdate(
          req.params.id,
          { product_Status: "inActive" },
          { new: true } // Return the updated document
        );
        return res.status(200).json({
          success: true,
          msg: "Changed status inActive",
        });
      } else {
        let product = await Product.findByIdAndUpdate(
          req.params.id,
          { product_Status: "Active" },
          { new: true } // Return the updated document
        );
        return res.status(200).json({
          success: true,
          msg: "Changed status Active",
        });
      }
    }
  } catch (error) {
    res.status(500).json({
      success: false,
      msg: error.message ? error.message : "Something went wrong!",
    });
  }
};
//get One Product
export const getOneProduct = async (req, res) => {
  try {
    const data = await Product.findById(req.params.id).populate({
      path: "category addedBy brand",
      select: "categoryName name brandName",
    });
    if (data) {
      return res.status(200).json({
        success: true,
        data,
      });
    }
  } catch (error) {
    // console.log(error)
    res.status(500).json({
      success: false,
      msg: error.message ? error.message : "Something went wrong!",
    });
  }
};

// get all product with device products first
export const getAllProductsDevicesFirst = async (req, res) => {
  try {
    // we want products with category name Device to be displayed first, so i have first found the products with category name Devices then made another request to find all products and filtered products with category devices , then merged both arrays so we get devices first then all other categories
    const categoryName = "Devices";
    // Find the category object by name first
    const category = await CategoryModel.findOne({ categoryName });
    if (!category) {
      throw new Error("Category not found");
    }
    // products with device category
    const deviceProducts = await Product.find({
      category: category._id,
    }).populate("category");

    // all products
    const allProducts = await Product.find()
      .populate({
        path: "category gst addedBy",
        select: "name categoryName tax",
      })
      .sort({
        createdAt: -1,
      });
    // filtering out products with device category
    const filteredProducts = allProducts.filter((ele) => {
      return ele.category?.categoryName !== categoryName;
    });

    // merging both deviceProcuts and filtered products
    const product = deviceProducts.concat(filteredProducts);
    if (product) {
      return res.status(200).json({
        success: true,
        product,
      });
    }
  } catch (error) {
    res.status(500).json({
      success: false,
      msg: error.message ? error.message : "Something went wrong!",
    });
  }
};

// 3.update Product
// export const updateProduct = async (req, res) => {
//   const {
//     name,
//     description,
//     price,
//     category,
//     image,
//     gst_amount,
//     gst,
//     total_amount,
//   } = req.body;
//   console.log(gst_amount, gst, total_amount);

//   try {
//     // Prepare an array for the images
//     const jsonArray = JSON.parse(image);
//     const AllImages = jsonArray.map(({ public_id, url }) => ({
//       public_id,
//       url,
//     }));

//     if (req.files && req.files.newImages) {
//       const newuploadImages = Array.isArray(req.files.newImages)
//         ? req.files.newImages
//         : [req.files.newImages];

//       const imagesLinks = [];

//       for (let i = 0; i < newuploadImages.length; i++) {
//         const result = await cloudinary.v2.uploader.upload(
//           newuploadImages[i].tempFilePath,
//           {
//             folder: "chemiNova/product",
//           }
//         );

//         imagesLinks.push({
//           public_id: result.public_id,
//           url: result.secure_url,
//         });
//       }

//       // Combine the existing images and the newly uploaded images
//       const updatedImages = [...AllImages, ...imagesLinks];

//       // Perform the product update
//       const ModifyProduct = await Product.findOneAndUpdate(
//         { _id: req.params.id },
//         {
//           $set: {
//             name,
//             description,
//             price,
//             category,
//             image: updatedImages,
//             gst,
//             gst_amount,
//             total_amount,
//           },
//         },
//         { new: true }
//       );
//       return res.status(200).json({
//         success: true,
//         ModifyProduct,
//       });
//     } else {
//       const ModifyProduct = await Product.findOneAndUpdate(
//         { _id: req.params.id },
//         {
//           $set: {
//             name,
//             description,
//             price,
//             category,
//             image: AllImages,
//           },
//         },
//         { new: true }
//       );
//       return res.status(200).json({
//         success: true,
//         ModifyProduct,
//       });
//     }
//   } catch (error) {
//     res.status(500).json({
//       success: false,
//       msg: error.message ? error.message : "Something went wrong!",
//     });
//   }
// };
// export const updateProduct = async (req, res) => {
//   const {
//     name,
//     description,
//     price,
//     category,
//     image,
//     gst_amount,
//     product_Status,
//     gst,
//     total_amount,
//   } = req.body;
//   try {
//     // Prepare an array for the images
//     const jsonArray = JSON.parse(image);
//     const AllImages = jsonArray.map(({ public_id, url }) => ({
//       public_id,
//       url,
//     }));

//     let updatedImages = AllImages;

//     if (req.files && req.files.newImages) {
//       const newUploadImages = Array.isArray(req.files.newImages)
//         ? req.files.newImages
//         : [req.files.newImages];

//       const imagesLinks = [];

//       for (let i = 0; i < newUploadImages.length; i++) {
//         const result = await cloudinary.v2.uploader.upload(
//           newUploadImages[i].tempFilePath,
//           {
//             folder: "chemiNova/product",
//           }
//         );

//         imagesLinks.push({
//           public_id: result.public_id,
//           url: result.secure_url,
//         });
//       }

//       // Combine the existing images and the newly uploaded images
//       updatedImages = [...AllImages, ...imagesLinks];
//     }

//     // Perform the product update
//     const updatedProduct = await Product.findOneAndUpdate(
//       { _id: req.params.id },
//       {
//         $set: {
//           name,
//           description,
//           product_Status,
//           price,
//           category,
//           image: updatedImages,
//           gst,
//           gst_amount,
//           total_amount,
//         },
//       },
//       { new: true }
//     );

//     if (!updatedProduct) {
//       return res.status(404).json({ success: false, msg: "Product not found" });
//     }

//     return res.status(200).json({
//       success: true,
//       updatedProduct,
//     });
//   } catch (error) {
//     console.error("Error updating product:", error);
//     res.status(500).json({
//       success: false,
//       msg: error.message ? error.message : "Something went wrong!",
//     });
//   }
// };

export const deleteImageFromCloudinary = async (req, res) => {
  const { public_id } = req.params;

  // console.log("Received public_id:", public_id); // Debugging log

  // Ensure public_id is not empty
  if (!public_id) {
    return res.status(400).json({
      success: false,
      msg: "Public ID is required!",
    });
  }

  const decodedPublicId = decodeURIComponent(public_id);

  try {
    const response = await cloudinary.v2.uploader.destroy(decodedPublicId);

    if (response.result === "ok") {
      return res.status(200).json({
        success: true,
        msg: "Image Deleted Successfully!",
      });
    } else {
      return res.status(400).json({
        success: false,
        msg: "Image deletion failed!",
      });
    }
  } catch (error) {
    console.error("Error deleting image:", error); // Log error for debugging
    return res.status(500).json({
      success: false,
      msg: error.message || "Something went wrong!",
    });
  }
};

//delete one Product
export const deleteProduct = async (req, res) => {
  try {
    if (!req.params.id) {
      return res.status(400).json({
        success: false,
        message: "Please Provide Product ID!",
      });
    }
    const getProduct = await Product.findById(req.params.id);
    if (!getProduct) {
      return res.status(404).json({
        success: false,
        message: "Product not Found!",
      });
    }
    // Deleting Images From Cloudinary
    if (getProduct?.image?.length > 0) {
      for (let i = 0; i < getProduct.image.length; i++) {
        await cloudinary.v2.uploader.destroy(getProduct.image[i].public_id);
      }
    }

    //-------------------------//
    const product = await Product.findByIdAndDelete(req.params.id);
    if (!product) {
      return res.status(404).json({ message: "Product Not Found" });
    }
    await product.remove();
    res.status(200).json({
      success: true,
      message: "Product Deleted Successfully!!",
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      msg: error.message ? error.message : "Something went wrong!",
    });
  }
};

export const getProductsByCategory = async (req, res) => {
  const { categoryName } = req.params; // Assuming category name is in the route
  // console.log(categoryName);

  try {
    // Find the category object by name first
    const category = await CategoryModel.findOne({ categoryName });

    if (!category) {
      throw new Error("Category not found");
    }
    const products = await Product.find({ category: category._id }).populate(
      "category"
    );
    // console.log(products);

    if (products && products.length > 0) {
      return res.status(200).json({
        success: true,
        products,
      });
    } else {
      return res.status(404).json({
        success: false,
        msg: "No products found for this category",
      });
    }
  } catch (error) {
    res.status(500).json({
      success: false,
      msg: error.message ? error.message : "Something went wrong!",
    });
  }
};
