/**
 * Inventory Service
 * Business logic for quantity-based inventory management
 */

// Import Inventory model
const { Inventory, Product, InventoryMovement, RMInventoryPiece } = require('../../../models'); // ProductVariant removed
// Import sequelize instance from models
const { sequelize } = require('../../../models');
// Import custom error classes
const { NotFoundError, ValidationError, InventoryError } = require('../../../utils/errors');
// Import logger for logging
const logger = require('../../../utils/logger');
// Import Sequelize operators
const { Op } = require('sequelize');

/**
 * Get or create inventory record for a product
 * Finds existing inventory or creates new one with default values
 * @param {number} productId - Product ID
 * @returns {Promise<Object>} Inventory record
 */
const getOrCreateInventory = async (productId) => {
  // Find existing inventory record (variants removed)
  let inventory = await Inventory.findOne({
    where: {
      product_id: productId, // Match product ID
    },
  });
  
  // If inventory doesn't exist, create new one
  if (!inventory) {
    inventory = await Inventory.create({
      product_id: productId, // Set product ID
      quantity: 0, // Default quantity is 0
      reorder_level: 0, // Default reorder level is 0
    });
    
    // Log inventory creation
    logger.info(`Inventory record created: product_id=${productId}`);
  }
  
  // Return inventory record
  return inventory;
};

/**
 * Get inventory quantity for a product
 * Returns current quantity in stock
 * @param {number} productId - Product ID
 * @returns {Promise<number>} Current quantity
 */
const getQuantity = async (productId) => {
  // Verify product exists
  const product = await Product.findByPk(productId); // Find product by ID
  if (!product) {
    throw new NotFoundError('Product not found'); // Throw error if product doesn't exist
  }
  
  // Get or create inventory record (variants removed)
  const inventory = await getOrCreateInventory(productId); // Get inventory record
  
  // Return current quantity
  return parseFloat(inventory.quantity); // Parse decimal to float
};

/**
 * Check if product is available in stock
 * Checks if quantity meets or exceeds required amount
 * @param {number} productId - Product ID
 * @param {number} requiredQuantity - Required quantity
 * @returns {Promise<boolean>} True if available, false otherwise
 */
const checkAvailability = async (productId, requiredQuantity) => {
  // Get current quantity
  const currentQuantity = await getQuantity(productId); // Get current stock quantity
  
  // Check if quantity is sufficient
  return currentQuantity >= requiredQuantity; // Return true if sufficient, false otherwise
};

/**
 * Adjust inventory quantity
 * Increases or decreases inventory quantity and records movement
 * @param {number} productId - Product ID
 * @param {number} quantityChange - Quantity change (positive for increase, negative for decrease)
 * @param {string} reason - Reason for adjustment (GRN, SALE, SALE_RETURN, PRODUCTION_CONSUME, PRODUCTION_OUTPUT, ADJUSTMENT)
 * @param {number|null} referenceId - Reference ID to source transaction (optional)
 * @param {number|null} userId - User ID performing the adjustment (optional)
 * @returns {Promise<Object>} Updated inventory record
 */
const adjustQuantity = async (productId, quantityChange, reason, referenceId = null, userId = null) => {
  // Validate quantity change
  if (quantityChange === 0) {
    throw new ValidationError('Quantity change cannot be zero'); // Throw error if no change
  }
  
  // Validate reason
  const validReasons = ['GRN', 'SALE', 'SALE_RETURN', 'PRODUCTION_CONSUME', 'PRODUCTION_OUTPUT', 'ADJUSTMENT']; // Valid movement reasons
  if (!validReasons.includes(reason)) {
    throw new ValidationError(`Invalid reason. Must be one of: ${validReasons.join(', ')}`); // Throw error if invalid reason
  }
  
  // Get or create inventory record (variants removed)
  const inventory = await getOrCreateInventory(productId); // Get inventory record
  
  // Calculate new quantity
  const newQuantity = parseFloat(inventory.quantity) + parseFloat(quantityChange); // Calculate new quantity
  
  // Check if new quantity would be negative
  if (newQuantity < 0) {
    throw new InventoryError(`Insufficient stock. Current: ${inventory.quantity}, Requested: ${Math.abs(quantityChange)}`); // Throw error if insufficient stock
  }
  
  // Update inventory quantity
  inventory.quantity = newQuantity; // Set new quantity
  await inventory.save(); // Save inventory record
  
  // Record inventory movement for audit trail (variants removed)
  await InventoryMovement.create({
    product_id: productId, // Product ID
    inventory_item_id: null, // No inventory item for quantity-based movements
    quantity_change: quantityChange, // Quantity change amount
    reason: reason, // Movement reason
    reference_id: referenceId || null, // Reference ID (null if not provided)
  });
  
  // Log inventory adjustment
  logger.info(`Inventory adjusted: product_id=${productId}, change=${quantityChange}, new_quantity=${newQuantity}`, {
    productId,
    quantityChange,
    newQuantity,
    reason,
    referenceId,
    userId,
  });
  
  // Return updated inventory record
  return inventory;
};

/**
 * Set inventory quantity (absolute value)
 * Sets inventory to a specific quantity and records movement
 * @param {number} productId - Product ID
 * @param {number} quantity - New quantity value
 * @param {number|null} userId - User ID performing the adjustment (optional)
 * @param {string} notes - Additional notes for the adjustment (optional)
 * @returns {Promise<Object>} Updated inventory record
 */
const setQuantity = async (productId, quantity, userId = null, notes = null) => {
  // Validate quantity
  if (quantity < 0) {
    throw new ValidationError('Quantity cannot be negative'); // Throw error if negative quantity
  }
  
  // Get or create inventory record (variants removed)
  const inventory = await getOrCreateInventory(productId); // Get inventory record
  
  // Calculate quantity change
  const quantityChange = parseFloat(quantity) - parseFloat(inventory.quantity); // Calculate difference
  
  // If quantity hasn't changed, return inventory without creating movement
  if (quantityChange === 0) {
    return inventory; // Return existing inventory record
  }
  
  // Update inventory quantity
  inventory.quantity = quantity; // Set new quantity
  await inventory.save(); // Save inventory record
  
  // Record inventory movement for audit trail (variants removed)
  await InventoryMovement.create({
    product_id: productId, // Product ID
    inventory_item_id: null, // No inventory item for quantity-based movements
    quantity_change: quantityChange, // Quantity change amount
    reason: 'ADJUSTMENT', // Movement reason (manual adjustment)
    reference_id: null, // No reference ID for manual adjustments
  });
  
  // Log inventory quantity set
  logger.info(`Inventory quantity set: product_id=${productId}, quantity=${quantity}`, {
    productId,
    quantity,
    quantityChange,
    userId,
    notes,
  });
  
  // Return updated inventory record
  return inventory;
};

/**
 * Set reorder level for a product
 * Updates the reorder level threshold
 * @param {number} productId - Product ID
 * @param {number} reorderLevel - New reorder level
 * @returns {Promise<Object>} Updated inventory record
 */
const setReorderLevel = async (productId, reorderLevel) => {
  // Validate reorder level
  if (reorderLevel < 0) {
    throw new ValidationError('Reorder level cannot be negative'); // Throw error if negative
  }
  
  // Get or create inventory record (variants removed)
  const inventory = await getOrCreateInventory(productId); // Get inventory record
  
  // Update reorder level
  inventory.reorder_level = reorderLevel; // Set new reorder level
  await inventory.save(); // Save inventory record
  
  // Log reorder level update
  logger.info(`Reorder level set: product_id=${productId}, reorder_level=${reorderLevel}`);
  
  // Return updated inventory record
  return inventory;
};

/**
 * Get inventory record with product details
 * Returns inventory with associated product information
 * @param {number} productId - Product ID
 * @returns {Promise<Object>} Inventory record with associations
 */
const getInventory = async (productId) => {
  // Get or create inventory record (variants removed)
  const inventory = await getOrCreateInventory(productId); // Get inventory record
  
  // Reload inventory with associations
  const inventoryWithDetails = await Inventory.findByPk(inventory.id, {
    include: [
      {
        model: Product, // Include product details
        as: 'product', // Use product alias
        attributes: ['id', 'name', 'sku', 'product_type', 'track_inventory'], // Select specific product attributes
      },
      // ProductVariant removed - variants not used
    ],
  });
  
  // Return inventory with details
  return inventoryWithDetails; // Return inventory record
};

/**
 * List inventory with filters and pagination
 * Returns paginated list of inventory records
 * @param {Object} options - Query options
 * @param {number} options.page - Page number (default: 1)
 * @param {number} options.limit - Items per page (default: 10)
 * @param {number|null} options.productId - Filter by product ID (optional)
 * @param {boolean|null} options.lowStock - Filter by low stock status (optional)
 * @param {boolean|null} options.outOfStock - Filter by out of stock status (optional)
 * @returns {Promise<Object>} Paginated inventory list
 */
const listInventory = async (options = {}) => {
  // Extract options with defaults (variants removed)
  const { page = 1, limit = 10, productId = null, lowStock = null, outOfStock = null, product_type = null } = options; // Extract and set defaults
  
  // Build where clause
  const where = {}; // Initialize where clause
  
  // Add product ID filter if provided
  if (productId) {
    where.product_id = productId; // Filter by product ID
  }
  
  // Variant ID filter removed - variants not used
  
  // Build product where clause for product_type filtering
  const productWhere = {}; // Initialize product where clause
  if (product_type) {
    productWhere.product_type = product_type; // Filter by product type (RM or FG)
  }
  // Exclude dimension-based RM products from quantity-based inventory query
  // They are tracked separately via RMInventoryPiece table
  if (!product_type || product_type === 'RM') {
    productWhere.track_by_dimensions = { [Op.ne]: true }; // Only include non-dimension-based products (false or null)
  }
  
  // Calculate offset for pagination
  const offset = (page - 1) * limit; // Calculate offset
  
  // Build query options
  const queryOptions = {
    where, // Apply where clause
    include: [
      {
        model: Product, // Include product details
        as: 'product', // Use product alias
        attributes: ['id', 'name', 'sku', 'product_type', 'track_inventory', 'track_by_dimensions', 'unit_of_measure'], // Select specific product attributes
        where: Object.keys(productWhere).length > 0 ? productWhere : undefined, // Apply product filter if provided
      },
      // ProductVariant removed - variants not used
    ],
    limit: parseInt(limit), // Set limit
    offset: parseInt(offset), // Set offset
    order: [['product_id', 'ASC']], // Order by product ID (variants removed)
  };
  
  // Execute query to get inventory records and total count
  const { count, rows } = await Inventory.findAndCountAll(queryOptions); // Get inventory records with count
  
  // If filtering by RM products, also include dimension-based RM products
  let dimensionBasedInventories = [];
  if (product_type === 'RM' || !product_type) {
    // Build product where clause for dimension-based RM
    const dimProductWhere = {
      product_type: 'RM',
      track_by_dimensions: true,
    };
    if (productId) {
      dimProductWhere.id = productId;
    }
    
    // Get all dimension-based RM products
    const dimProducts = await Product.findAll({
      where: dimProductWhere,
      attributes: ['id', 'name', 'sku', 'product_type', 'track_inventory', 'track_by_dimensions', 'unit_of_measure'],
    });
    
    // For each dimension-based product, get inventory summary
    for (const product of dimProducts) {
      // Get all pieces for this product
      const pieces = await RMInventoryPiece.findAll({
        where: { product_id: product.id },
        attributes: ['id', 'status', 'length', 'width', 'unit', 'usable_length', 'usable_width'],
      });
      
      // Calculate total available area (FULL and USABLE pieces only)
      let totalArea = 0;
      let totalPieces = 0;
      for (const piece of pieces) {
        if (piece.status === 'FULL' || piece.status === 'USABLE') {
          if (piece.status === 'FULL') {
            totalArea += piece.length * piece.width;
          } else if (piece.usable_length && piece.usable_width) {
            totalArea += piece.usable_length * piece.usable_width;
          }
          totalPieces++;
        }
      }
      
      // Only include if there are pieces or if no productId filter (to show all products)
      if (totalPieces > 0 || !productId) {
        dimensionBasedInventories.push({
          id: null, // No inventory record ID for dimension-based
          product_id: product.id,
          // variant_id removed - variants not used
          quantity: totalPieces, // Use piece count as quantity
          reorder_level: 0, // Dimension-based products don't use reorder levels
          product: product,
          variant: null,
          _isDimensionBased: true, // Flag to identify dimension-based inventory
          _totalArea: totalArea, // Total available area
          _unit: product.unit_of_measure || 'm', // Unit of measure
        });
      }
    }
  }
  
  // Combine regular inventory with dimension-based inventory
  const allInventories = [...rows, ...dimensionBasedInventories];
  
  // Sort by product name
  allInventories.sort((a, b) => {
    const nameA = a.product?.name || '';
    const nameB = b.product?.name || '';
    return nameA.localeCompare(nameB);
  });
  
  // Apply pagination to combined results
  const paginatedInventories = allInventories.slice(offset, offset + parseInt(limit));
  const totalCount = allInventories.length;
  const totalPages = Math.ceil(totalCount / limit);
  
  // Return paginated results
  return {
    inventories: paginatedInventories, // Combined inventory records
    pagination: {
      page: parseInt(page), // Current page
      limit: parseInt(limit), // Items per page
      total: totalCount, // Total items
      totalPages, // Total pages
    },
  };
};

/**
 * Get low stock items
 * Returns inventory items that are below reorder level
 * @param {Object} options - Query options
 * @param {number} options.page - Page number (default: 1)
 * @param {number} options.limit - Items per page (default: 10)
 * @returns {Promise<Object>} Paginated low stock inventory list
 */
const getLowStockItems = async (options = {}) => {
  // Extract options with defaults
  const { page = 1, limit = 10 } = options; // Extract and set defaults
  
  // Calculate offset for pagination
  const offset = (page - 1) * limit; // Calculate offset
  
  // Build query to find low stock items (quantity <= reorder_level)
  // Use Sequelize literal for column comparison
  const queryOptions = {
    where: sequelize.literal('quantity <= reorder_level'), // Quantity is less than or equal to reorder level
    include: [
      {
        model: Product, // Include product details
        as: 'product', // Use product alias
        attributes: ['id', 'name', 'sku', 'product_type', 'track_inventory'], // Select specific product attributes
      },
      // ProductVariant removed - variants not used
    ],
    limit: parseInt(limit), // Set limit
    offset: parseInt(offset), // Set offset
    order: [['quantity', 'ASC']], // Order by quantity ascending (lowest first)
  };
  
  // Execute query to get low stock items and total count
  const { count, rows } = await Inventory.findAndCountAll(queryOptions); // Get inventory records with count
  
  // Calculate total pages
  const totalPages = Math.ceil(count / limit); // Calculate total pages
  
  // Return paginated results
  return {
    inventories: rows, // Low stock inventory records
    pagination: {
      page: parseInt(page), // Current page
      limit: parseInt(limit), // Items per page
      total: count, // Total items
      totalPages, // Total pages
    },
  };
};

/**
 * Get inventory movements for a product/variant
 * Returns history of inventory changes
 * @param {number} productId - Product ID
 * @param {Object} options - Query options
 * @param {number} options.page - Page number (default: 1)
 * @param {number} options.limit - Items per page (default: 20)
 * @returns {Promise<Object>} Paginated inventory movements
 */
const getInventoryMovements = async (productId, options = {}) => {
  // Extract options with defaults
  const { page = 1, limit = 20 } = options; // Extract and set defaults
  
  // Build where clause
  const where = {
    product_id: productId, // Filter by product ID
  };
  
  // Variant ID filter removed - variants not used
  
  // Calculate offset for pagination
  const offset = (page - 1) * limit; // Calculate offset
  
  // Build query options
  const queryOptions = {
    where, // Apply where clause
    limit: parseInt(limit), // Set limit
    offset: parseInt(offset), // Set offset
    order: [['created_at', 'DESC']], // Order by creation date descending (newest first)
  };
  
  // Execute query to get movements and total count
  const { count, rows } = await InventoryMovement.findAndCountAll(queryOptions); // Get movements with count
  
  // Calculate total pages
  const totalPages = Math.ceil(count / limit); // Calculate total pages
  
  // Return paginated results
  return {
    movements: rows, // Movement records
    pagination: {
      page: parseInt(page), // Current page
      limit: parseInt(limit), // Items per page
      total: count, // Total items
      totalPages, // Total pages
    },
  };
};

// Import inventory items service
const inventoryItemsService = require('./items');
// Import reconciliation service
const reconciliationService = require('./reconciliation');

// Export service functions (quantity-based + UID-based)
module.exports = {
  // Quantity-based inventory functions
  getQuantity, // Get inventory quantity
  checkAvailability, // Check stock availability
  adjustQuantity, // Adjust inventory quantity
  setQuantity, // Set inventory quantity (absolute)
  setReorderLevel, // Set reorder level
  getInventory, // Get inventory record with details
  listInventory, // List inventory with filters
  getLowStockItems, // Get low stock items
  getInventoryMovements, // Get inventory movements history
  // UID-based inventory item functions
  createInventoryItem: inventoryItemsService.createInventoryItem, // Create inventory item with UID
  getInventoryItemByUID: inventoryItemsService.getInventoryItemByUID, // Get inventory item by UID/barcode
  updateInventoryItemStatus: inventoryItemsService.updateInventoryItemStatus, // Update inventory item status
  scanInventoryItem: inventoryItemsService.scanInventoryItem, // Scan inventory item
  listInventoryItems: inventoryItemsService.listInventoryItems, // List inventory items with filters
  // Reconciliation functions
  checkDiscrepancies: reconciliationService.checkDiscrepancies, // Check for inventory discrepancies
  reconcileDiscrepancies: reconciliationService.reconcileDiscrepancies, // Reconcile discrepancies
  getReconciliationReport: reconciliationService.getReconciliationReport, // Get reconciliation report
  autoReconcile: reconciliationService.autoReconcile, // Auto-reconcile all discrepancies
};
