const Product = require('../models/Product');
const Company = require('../models/Company');
const Role = require('../models/Role');
const ActivityLog = require('../models/ActivityLog');

// Get all products
exports.getAllProducts = async (req, res, next) => {
  try {
    const { 
      companyId, 
      status, 
      category,
      subCategory,
      brand,
      search, 
      minPrice,
      maxPrice,
      inStock,
      isFeatured,
      page = 1, 
      limit = 20,
      sortBy = 'createdAt',
      sortOrder = 'desc'
    } = req.query;
    
    // Build query
    let query = {};
    
    // Check user role and filter accordingly
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global') {
      query.companyId = req.user.companyId;
    } else if (companyId) {
      query.companyId = companyId;
    }
    
    // Filter by status
    if (status) {
      query.status = status;
    }
    
    // Filter by category
    if (category) {
      query.category = category;
    }
    
    // Filter by sub-category
    if (subCategory) {
      query.subCategory = subCategory;
    }
    
    // Filter by brand
    if (brand) {
      query.brand = brand;
    }
    
    // Filter by featured
    if (isFeatured !== undefined) {
      query.isFeatured = isFeatured === 'true';
    }
    
    // Filter by price range
    if (minPrice || maxPrice) {
      query['pricing.price'] = {};
      if (minPrice) query['pricing.price'].$gte = parseFloat(minPrice);
      if (maxPrice) query['pricing.price'].$lte = parseFloat(maxPrice);
    }
    
    // Filter by stock
    if (inStock === 'true') {
      query['inventory.quantity'] = { $gt: 0 };
    } else if (inStock === 'false') {
      query['inventory.quantity'] = 0;
    }
    
    // Search by name, description, or tags
    if (search) {
      query.$text = { $search: search };
    }
    
    // Pagination
    const skip = (page - 1) * limit;
    
    // Sorting
    const sort = {};
    sort[sortBy] = sortOrder === 'asc' ? 1 : -1;
    
    const products = await Product.find(query)
      .populate('companyId', 'name code')
      .populate('createdBy', 'firstName lastName email')
      .sort(sort)
      .limit(parseInt(limit))
      .skip(skip);
    
    const total = await Product.countDocuments(query);
    
    res.status(200).json({
      success: true,
      count: products.length,
      total,
      page: parseInt(page),
      pages: Math.ceil(total / limit),
      data: products
    });
  } catch (error) {
    next(error);
  }
};

// Get single product
exports.getProduct = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const product = await Product.findById(id)
      .populate('companyId', 'name code')
      .populate('createdBy', 'firstName lastName email');
    
    if (!product) {
      return res.status(404).json({
        success: false,
        message: 'Product not found'
      });
    }
    
    // Check access
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global' && product.companyId._id.toString() !== req.user.companyId.toString()) {
      return res.status(403).json({
        success: false,
        message: 'You do not have access to this product'
      });
    }
    
    // Increment view count
    product.metadata.views += 1;
    await product.save();
    
    res.status(200).json({
      success: true,
      data: product
    });
  } catch (error) {
    next(error);
  }
};

// Create product
exports.createProduct = async (req, res, next) => {
  try {
    const productData = req.body;
    
    // Verify company exists
    const company = await Company.findById(productData.companyId);
    if (!company) {
      return res.status(404).json({
        success: false,
        message: 'Company not found'
      });
    }
    
    // Check access
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global' && productData.companyId !== req.user.companyId.toString()) {
      return res.status(403).json({
        success: false,
        message: 'You can only create products for your own company'
      });
    }
    
    // Create product
    const product = await Product.create({
      ...productData,
      createdBy: req.user._id
    });
    
    // Log activity
    await ActivityLog.create({
      userId: req.user._id,
      companyId: product.companyId,
      action: 'create',
      module: 'products',
      entityType: 'Product',
      entityId: product._id,
      description: `Created product: ${product.name}`,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    });
    
    const populatedProduct = await Product.findById(product._id)
      .populate('companyId', 'name code')
      .populate('createdBy', 'firstName lastName email');
    
    res.status(201).json({
      success: true,
      message: 'Product created successfully',
      data: populatedProduct
    });
  } catch (error) {
    next(error);
  }
};

// Update product
exports.updateProduct = async (req, res, next) => {
  try {
    const { id } = req.params;
    const updates = req.body;
    
    const product = await Product.findById(id);
    
    if (!product) {
      return res.status(404).json({
        success: false,
        message: 'Product not found'
      });
    }
    
    // Check access
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global' && product.companyId.toString() !== req.user.companyId.toString()) {
      return res.status(403).json({
        success: false,
        message: 'You do not have access to this product'
      });
    }
    
    // Don't allow updating certain fields
    delete updates.productCode;
    delete updates.companyId;
    delete updates.createdBy;
    
    const updatedProduct = await Product.findByIdAndUpdate(
      id,
      updates,
      { new: true, runValidators: true }
    )
      .populate('companyId', 'name code')
      .populate('createdBy', 'firstName lastName email');
    
    // Log activity
    await ActivityLog.create({
      userId: req.user._id,
      companyId: product.companyId,
      action: 'update',
      module: 'products',
      entityType: 'Product',
      entityId: product._id,
      description: `Updated product: ${product.name}`,
      changes: updates,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    });
    
    res.status(200).json({
      success: true,
      message: 'Product updated successfully',
      data: updatedProduct
    });
  } catch (error) {
    next(error);
  }
};

// Delete product
exports.deleteProduct = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const product = await Product.findById(id);
    
    if (!product) {
      return res.status(404).json({
        success: false,
        message: 'Product not found'
      });
    }
    
    // Check access
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global' && product.companyId.toString() !== req.user.companyId.toString()) {
      return res.status(403).json({
        success: false,
        message: 'You do not have access to this product'
      });
    }
    
    await Product.findByIdAndDelete(id);
    
    // Log activity
    await ActivityLog.create({
      userId: req.user._id,
      companyId: product.companyId,
      action: 'delete',
      module: 'products',
      entityType: 'Product',
      entityId: product._id,
      description: `Deleted product: ${product.name}`,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    });
    
    res.status(200).json({
      success: true,
      message: 'Product deleted successfully'
    });
  } catch (error) {
    next(error);
  }
};

// Update product status
exports.updateProductStatus = async (req, res, next) => {
  try {
    const { id } = req.params;
    const { status } = req.body;
    
    if (!['active', 'inactive', 'discontinued', 'out_of_stock'].includes(status)) {
      return res.status(400).json({
        success: false,
        message: 'Invalid status'
      });
    }
    
    const product = await Product.findByIdAndUpdate(
      id,
      { status },
      { new: true }
    ).populate('companyId', 'name code');
    
    if (!product) {
      return res.status(404).json({
        success: false,
        message: 'Product not found'
      });
    }
    
    // Log activity
    await ActivityLog.create({
      userId: req.user._id,
      companyId: product.companyId,
      action: 'update',
      module: 'products',
      entityType: 'Product',
      entityId: product._id,
      description: `Changed product status to: ${status}`,
      changes: { status },
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    });
    
    res.status(200).json({
      success: true,
      message: `Product status updated to ${status}`,
      data: product
    });
  } catch (error) {
    next(error);
  }
};

// Update inventory
exports.updateInventory = async (req, res, next) => {
  try {
    const { id } = req.params;
    const { quantity, operation } = req.body; // operation: 'add' | 'subtract' | 'set'
    
    const product = await Product.findById(id);
    
    if (!product) {
      return res.status(404).json({
        success: false,
        message: 'Product not found'
      });
    }
    
    const oldQuantity = product.inventory.quantity;
    
    if (operation === 'add') {
      product.inventory.quantity += quantity;
    } else if (operation === 'subtract') {
      product.inventory.quantity = Math.max(0, product.inventory.quantity - quantity);
    } else if (operation === 'set') {
      product.inventory.quantity = quantity;
    } else {
      return res.status(400).json({
        success: false,
        message: 'Invalid operation. Must be: add, subtract, or set'
      });
    }
    
    // Update status based on quantity
    if (product.inventory.quantity === 0) {
      product.status = 'out_of_stock';
    } else if (product.status === 'out_of_stock') {
      product.status = 'active';
    }
    
    await product.save();
    
    // Log activity
    await ActivityLog.create({
      userId: req.user._id,
      companyId: product.companyId,
      action: 'update',
      module: 'products',
      entityType: 'Product',
      entityId: product._id,
      description: `Updated inventory: ${oldQuantity} → ${product.inventory.quantity}`,
      changes: { quantity: { old: oldQuantity, new: product.inventory.quantity }, operation },
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    });
    
    res.status(200).json({
      success: true,
      message: 'Inventory updated successfully',
      data: product
    });
  } catch (error) {
    next(error);
  }
};

// Get product statistics
exports.getProductStats = async (req, res, next) => {
  try {
    const { companyId } = req.query;
    
    let query = {};
    
    // Check user role and filter accordingly
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global') {
      query.companyId = req.user.companyId;
    } else if (companyId) {
      query.companyId = companyId;
    }
    
    // Total products
    const totalProducts = await Product.countDocuments(query);
    const activeProducts = await Product.countDocuments({ ...query, status: 'active' });
    const inactiveProducts = await Product.countDocuments({ ...query, status: 'inactive' });
    const outOfStock = await Product.countDocuments({ ...query, status: 'out_of_stock' });
    const discontinued = await Product.countDocuments({ ...query, status: 'discontinued' });
    
    // Low stock products
    const lowStock = await Product.countDocuments({
      ...query,
      $expr: { $lte: ['$inventory.quantity', '$inventory.minStockLevel'] }
    });
    
    // By category
    const byCategory = await Product.aggregate([
      { $match: query },
      {
        $group: {
          _id: '$category',
          count: { $sum: 1 },
          totalValue: { $sum: { $multiply: ['$pricing.price', '$inventory.quantity'] } }
        }
      },
      { $sort: { count: -1 } }
    ]);
    
    // By brand
    const byBrand = await Product.aggregate([
      { $match: query },
      {
        $group: {
          _id: '$brand',
          count: { $sum: 1 }
        }
      },
      { $sort: { count: -1 } },
      { $limit: 10 }
    ]);
    
    // Total inventory value
    const inventoryValue = await Product.aggregate([
      { $match: query },
      {
        $group: {
          _id: null,
          totalValue: { $sum: { $multiply: ['$pricing.price', '$inventory.quantity'] } },
          totalCost: { $sum: { $multiply: ['$pricing.cost', '$inventory.quantity'] } }
        }
      }
    ]);
    
    // Featured products
    const featuredCount = await Product.countDocuments({ ...query, isFeatured: true });
    
    res.status(200).json({
      success: true,
      data: {
        overview: {
          total: totalProducts,
          active: activeProducts,
          inactive: inactiveProducts,
          outOfStock,
          discontinued,
          lowStock,
          featured: featuredCount
        },
        inventory: {
          totalValue: inventoryValue[0]?.totalValue || 0,
          totalCost: inventoryValue[0]?.totalCost || 0,
          profit: (inventoryValue[0]?.totalValue || 0) - (inventoryValue[0]?.totalCost || 0)
        },
        byCategory,
        byBrand
      }
    });
  } catch (error) {
    next(error);
  }
};

// Get categories
exports.getCategories = async (req, res, next) => {
  try {
    const { companyId } = req.query;
    
    let query = {};
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global') {
      query.companyId = req.user.companyId;
    } else if (companyId) {
      query.companyId = companyId;
    }
    
    const categories = await Product.aggregate([
      { $match: query },
      {
        $group: {
          _id: '$category',
          count: { $sum: 1 },
          subCategories: { $addToSet: '$subCategory' }
        }
      },
      { $sort: { count: -1 } }
    ]);
    
    res.status(200).json({
      success: true,
      count: categories.length,
      data: categories
    });
  } catch (error) {
    next(error);
  }
};

// Get brands
exports.getBrands = async (req, res, next) => {
  try {
    const { companyId } = req.query;
    
    let query = {};
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global') {
      query.companyId = req.user.companyId;
    } else if (companyId) {
      query.companyId = companyId;
    }
    
    const brands = await Product.distinct('brand', query);
    
    res.status(200).json({
      success: true,
      count: brands.length,
      data: brands.filter(brand => brand) // Remove null/empty values
    });
  } catch (error) {
    next(error);
  }
};

// Bulk update products
exports.bulkUpdateProducts = async (req, res, next) => {
  try {
    const { productIds, updates } = req.body;
    
    if (!Array.isArray(productIds) || productIds.length === 0) {
      return res.status(400).json({
        success: false,
        message: 'Product IDs must be a non-empty array'
      });
    }
    
    // Don't allow updating certain fields in bulk
    delete updates.productCode;
    delete updates.companyId;
    delete updates.createdBy;
    
    const result = await Product.updateMany(
      { _id: { $in: productIds } },
      updates
    );
    
    // Log activity
    await ActivityLog.create({
      userId: req.user._id,
      action: 'update',
      module: 'products',
      description: `Bulk updated ${result.modifiedCount} products`,
      changes: { productIds, updates },
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    });
    
    res.status(200).json({
      success: true,
      message: `${result.modifiedCount} products updated successfully`,
      data: {
        matched: result.matchedCount,
        modified: result.modifiedCount
      }
    });
  } catch (error) {
    next(error);
  }
};

// Get low stock products
exports.getLowStockProducts = async (req, res, next) => {
  try {
    const { companyId } = req.query;
    
    let query = {};
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global') {
      query.companyId = req.user.companyId;
    } else if (companyId) {
      query.companyId = companyId;
    }
    
    const products = await Product.find({
      ...query,
      $expr: { $lte: ['$inventory.quantity', '$inventory.minStockLevel'] }
    })
      .populate('companyId', 'name code')
      .sort({ 'inventory.quantity': 1 });
    
    res.status(200).json({
      success: true,
      count: products.length,
      data: products
    });
  } catch (error) {
    next(error);
  }
};