const Product = require('../models/Product'); const Category = require('../models/Category'); const Brand = require('../models/Brand'); const { successResponse, errorResponse } = require('../utils/apiResponse'); // @desc Get all products with filtering, search, sorting & pagination // @route GET /api/products // @access Public const getProducts = async (req, res, next) => { try { const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 12; const skip = (page - 1) * limit; const query = { isActive: true }; // Search query if (req.query.search) { const searchRegex = new RegExp(req.query.search, 'i'); query.$or = [ { name: searchRegex }, { description: searchRegex }, { tags: searchRegex }, { sku: searchRegex } ]; } // Category filter if (req.query.category) { const cat = await Category.findOne({ slug: req.query.category }); if (cat) query.category = cat._id; } // Brand filter if (req.query.brand) { const brand = await Brand.findOne({ slug: req.query.brand }); if (brand) query.brand = brand._id; } // Price range filter if (req.query.minPrice || req.query.maxPrice) { query.price = {}; if (req.query.minPrice) query.price.$gte = Number(req.query.minPrice); if (req.query.maxPrice) query.price.$lte = Number(req.query.maxPrice); } // Guitar specific filters if (req.query.guitarType) { query['specs.guitarType'] = req.query.guitarType; } if (req.query.bodyShape) { query['specs.bodyShape'] = req.query.bodyShape; } if (req.query.handOrientation) { query['specs.handOrientation'] = req.query.handOrientation; } if (req.query.numberOfStrings) { query['specs.numberOfStrings'] = Number(req.query.numberOfStrings); } if (req.query.isFeatured) { query.isFeatured = true; } if (req.query.isNewArrival) { query.isNewArrival = true; } if (req.query.isBestSeller) { query.isBestSeller = true; } // Sorting let sortOption = { createdAt: -1 }; if (req.query.sort === 'price_asc') sortOption = { price: 1 }; if (req.query.sort === 'price_desc') sortOption = { price: -1 }; if (req.query.sort === 'rating') sortOption = { rating: -1 }; if (req.query.sort === 'bestselling') sortOption = { salesCount: -1 }; const total = await Product.countDocuments(query); const products = await Product.find(query) .populate('category', 'name slug') .populate('brand', 'name slug logo') .sort(sortOption) .skip(skip) .limit(limit); return successResponse(res, 'Products retrieved successfully', { products, pagination: { total, page, pages: Math.ceil(total / limit), limit } }); } catch (error) { next(error); } }; // @desc Guitar Finder Recommendation Tool // @route POST /api/products/guitar-finder // @access Public const getGuitarRecommendations = async (req, res, next) => { try { const { skillLevel, acousticElectric, musicStyle, maxBudget, handOrientation } = req.body; const query = { isActive: true }; if (acousticElectric && acousticElectric !== 'Any') { query['specs.guitarType'] = acousticElectric; } if (maxBudget) { query.price = { $lte: Number(maxBudget) }; } if (handOrientation) { query['specs.handOrientation'] = handOrientation; } const recommendations = await Product.find(query) .populate('category', 'name slug') .populate('brand', 'name slug logo') .sort({ rating: -1, salesCount: -1 }) .limit(6); return successResponse(res, 'Guitar recommendations generated', recommendations); } catch (error) { next(error); } }; // @desc Get single product by slug // @route GET /api/products/:slug // @access Public const getProductBySlug = async (req, res, next) => { try { const product = await Product.findOne({ slug: req.params.slug }) .populate('category', 'name slug') .populate('brand', 'name slug logo description'); if (!product) { return errorResponse(res, 'Product not found', [], 404); } // Related products (same category) const relatedProducts = await Product.find({ category: product.category._id, _id: { $ne: product._id }, isActive: true }) .limit(4) .select('name slug price salePrice images rating brand'); return successResponse(res, 'Product retrieved successfully', { product, relatedProducts }); } catch (error) { next(error); } }; // @desc Compare products by array of IDs // @route POST /api/products/compare // @access Public const compareProducts = async (req, res, next) => { try { const { productIds } = req.body; if (!productIds || !Array.isArray(productIds)) { return errorResponse(res, 'Please provide an array of product IDs', [], 400); } const products = await Product.find({ _id: { $in: productIds } }) .populate('brand', 'name logo') .populate('category', 'name'); return successResponse(res, 'Comparison data retrieved', products); } catch (error) { next(error); } }; // @desc Create new product (Admin) // @route POST /api/products // @access Private/Admin const createProduct = async (req, res, next) => { try { const product = new Product(req.body); const createdProduct = await product.save(); return successResponse(res, 'Product created successfully', createdProduct, 201); } catch (error) { next(error); } }; // @desc Update product (Admin) // @route PUT /api/products/:id // @access Private/Admin const updateProduct = async (req, res, next) => { try { const product = await Product.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true }); if (!product) { return errorResponse(res, 'Product not found', [], 404); } return successResponse(res, 'Product updated successfully', product); } catch (error) { next(error); } }; // @desc Delete product (Admin) // @route DELETE /api/products/:id // @access Private/Admin const deleteProduct = async (req, res, next) => { try { const product = await Product.findByIdAndDelete(req.params.id); if (!product) { return errorResponse(res, 'Product not found', [], 404); } return successResponse(res, 'Product deleted successfully'); } catch (error) { next(error); } }; module.exports = { getProducts, getGuitarRecommendations, getProductBySlug, compareProducts, createProduct, updateProduct, deleteProduct };