/**
 * Inventory Items Service (UID-Based)
 * Business logic for UID-based inventory item management
 */

// Import InventoryItem and related models
const { InventoryItem, Product, Inventory, InventoryMovement } = require('../../../models'); // ProductVariant removed
// Import UID generator utility
const { generateUID, generateBarcode, isValidUID, isValidBarcode } = require('../../../utils/uidGenerator');
// Import QR code generator utility
const { generateQRCodeDataURL } = require('../../../utils/barcodeGenerator');
// Import custom error classes
const { NotFoundError, ValidationError, ConflictError } = require('../../../utils/errors');
// Import logger for logging
const logger = require('../../../utils/logger');
// Import Sequelize operators
const { Op } = require('sequelize');
const { sequelize } = require('../../../models');

/**
 * Create inventory item with UID
 * Creates a new inventory item with generated UID and optionally barcode
 * **IMPORTANT**: UUID generation is ONLY for FG (Finished Goods) products. RM products do NOT get UUIDs.
 * @param {Object} itemData - Inventory item data (product_id, source, uid, barcode)
 * @param {number} userId - User ID creating the item (for audit)
 * @returns {Promise<Object>} Created inventory item
 */
const createInventoryItem = async (itemData, userId) => {
  // Extract item data
  const { product_id, source, uid = null, barcode = null } = itemData; // Extract data (variant_id removed)
  
  // Validate required fields
  if (!product_id || !source) {
    throw new ValidationError('Product ID and source are required'); // Throw error if missing required fields
  }
  
  // Validate source
  const validSources = ['GRN', 'PRODUCTION', 'RETURN']; // Valid sources
  if (!validSources.includes(source)) {
    throw new ValidationError(`Source must be one of: ${validSources.join(', ')}`); // Throw error if invalid source
  }
  
  // Start database transaction
  const transaction = await sequelize.transaction(); // Start transaction
  
  try {
    // Verify product exists
    const product = await Product.findByPk(product_id, { transaction }); // Find product in transaction
    if (!product) {
      throw new NotFoundError('Product not found'); // Throw error if product doesn't exist
    }
    
    // **UUID generation is ONLY for FG (Finished Goods) products**
    // RM (Raw Materials) products should NOT get UUIDs - they are tracked by quantity/dimensions only
    if (product.product_type !== 'FG') {
      throw new ValidationError(`UUID generation is only allowed for FG (Finished Goods) products. Product ${product.name} is ${product.product_type}.`); // Throw error if trying to create UUID for non-FG product
    }
    
    // Generate UID if not provided
    let itemUID = uid; // Use provided UID
    if (!itemUID) {
      itemUID = generateUID(); // Generate UID if not provided
    } else {
      // Validate provided UID
      if (!isValidUID(itemUID)) {
        throw new ValidationError('Invalid UID format'); // Throw error if invalid UID
      }
      
      // Check if UID already exists
      const existingItem = await InventoryItem.findOne({
        where: { uid: itemUID }, // Match UID
        transaction, // Use transaction
      });
      
      if (existingItem) {
        throw new ConflictError(`UID ${itemUID} already exists`); // Throw error if UID exists
      }
    }
    
    // Generate barcode if not provided
    let itemBarcode = barcode; // Use provided barcode
    if (!itemBarcode) {
      itemBarcode = generateBarcode(); // Generate barcode if not provided
    } else {
      // Validate provided barcode
      if (!isValidBarcode(itemBarcode)) {
        throw new ValidationError('Invalid barcode format'); // Throw error if invalid barcode
      }
      
      // Check if barcode already exists (if provided)
      const existingBarcode = await InventoryItem.findOne({
        where: { barcode: itemBarcode }, // Match barcode
        transaction, // Use transaction
      });
      
      if (existingBarcode) {
        throw new ConflictError(`Barcode ${itemBarcode} already exists`); // Throw error if barcode exists
      }
    }
    
    // Create inventory item
    const inventoryItem = await InventoryItem.create({
      uid: itemUID, // Set UID
      product_id, // Set product ID
      // variant_id removed - variants no longer exist
      source, // Set source
      barcode: itemBarcode, // Set barcode
      status: 'IN_STOCK', // Default status is IN_STOCK
    }, { transaction }); // Create item in transaction
    
    // Get or create quantity-based inventory record
    const inventory = await Inventory.findOrCreate({
      where: {
        product_id, // Match product ID
        // variant_id removed - variants no longer exist
      },
      defaults: {
        quantity: 0, // Default quantity is 0
        reorder_level: 0, // Default reorder level is 0
      },
      transaction, // Use transaction
    });
    
    // Increment quantity in quantity-based inventory
    await inventory[0].increment('quantity', { by: 1, transaction }); // Increment quantity
    
    // Create inventory movement record
    await InventoryMovement.create({
      product_id, // Set product ID
      // variant_id removed - variants no longer exist
      inventory_item_id: inventoryItem.id, // Set inventory item ID
      movement_type: 'IN', // Movement type is IN
      quantity_change: 1, // Quantity change is +1
      quantity_after: inventory[0].quantity, // Quantity after movement
      reason: `Inventory item created (${source})`, // Movement reason
      reference_id: null, // No reference ID for item creation
      user_id: userId, // Set user ID
    }, { transaction }); // Create movement in transaction
    
    // Commit transaction
    await transaction.commit(); // Commit transaction
    
    // Reload inventory item with associations
    const itemWithDetails = await InventoryItem.findByPk(inventoryItem.id, {
      include: [
        {
          model: Product, // Include product details
          as: 'product', // Use product alias
          attributes: ['id', 'name', 'sku'], // Select specific attributes
        },
        // ProductVariant removed - variants no longer exist
      ],
    });
    
    // Log inventory item creation
    logger.info(`Inventory item created: ${itemUID}`, {
      itemId: inventoryItem.id,
      uid: itemUID,
      productId: product_id,
      // variantId removed - variants no longer exist
      source,
      userId, // Log user ID
    });
    
    // Return created inventory item
    return itemWithDetails; // Return inventory item
  } catch (error) {
    // Rollback transaction on error
    await transaction.rollback(); // Rollback transaction
    throw error; // Re-throw error
  }
};

/**
 * Get inventory item by UID
 * Retrieves an inventory item by its UID or barcode
 * @param {string} identifier - UID or barcode
 * @returns {Promise<Object>} Inventory item with details
 */
const getInventoryItemByUID = async (identifier) => {
  // Find inventory item by UID or barcode
  const inventoryItem = await InventoryItem.findOne({
    where: {
      [Op.or]: [
        { uid: identifier }, // Match UID
        { barcode: identifier }, // Match barcode
      ],
    },
    include: [
      {
        model: Product, // Include product details
        as: 'product', // Use product alias
      },
      // ProductVariant removed - variants no longer exist
    ],
  });
  
  // If inventory item not found, throw error
  if (!inventoryItem) {
    throw new NotFoundError(`Inventory item with UID/barcode ${identifier} not found`); // Throw error
  }
  
  // Return inventory item
  return inventoryItem; // Return inventory item
};

/**
 * Update inventory item status
 * Updates the status of an inventory item
 * @param {string} uid - Inventory item UID
 * @param {string} newStatus - New status
 * @param {number} userId - User ID updating the status (for audit)
 * @param {string} reason - Reason for status change (optional)
 * @returns {Promise<Object>} Updated inventory item
 */
const updateInventoryItemStatus = async (uid, newStatus, userId, reason = null) => {
  // Validate new status
  const validStatuses = ['IN_STOCK', 'RESERVED', 'SOLD', 'RETURNED', 'DAMAGED', 'SCRAPPED']; // Valid statuses
  if (!validStatuses.includes(newStatus)) {
    throw new ValidationError(`Status must be one of: ${validStatuses.join(', ')}`); // Throw error if invalid status
  }
  
  // Start database transaction
  const transaction = await sequelize.transaction(); // Start transaction
  
  try {
    // Find inventory item
    const inventoryItem = await InventoryItem.findOne({
      where: {
        [Op.or]: [
          { uid }, // Match UID
          { barcode: uid }, // Match barcode
        ],
      },
      include: [
        {
          model: Product, // Include product details
          as: 'product', // Use product alias
        },
      ],
      transaction, // Use transaction
    });
    
    // If inventory item not found, throw error
    if (!inventoryItem) {
      throw new NotFoundError(`Inventory item with UID ${uid} not found`); // Throw error
    }
    
    // Store old status
    const oldStatus = inventoryItem.status; // Get old status
    
    // Update status
    inventoryItem.status = newStatus; // Set new status
    await inventoryItem.save({ transaction }); // Save item in transaction
    
    // Create inventory movement record for status change (if status affects quantity)
    // Note: Status changes like SOLD, SCRAPPED may affect quantity-based inventory
    const statusAffectsQuantity = ['SOLD', 'SCRAPPED'].includes(newStatus); // Check if status affects quantity
    
    if (statusAffectsQuantity && oldStatus === 'IN_STOCK') {
      // Decrement quantity if item was IN_STOCK and is now SOLD or SCRAPPED
      const inventory = await Inventory.findOne({
        where: {
          product_id: inventoryItem.product_id, // Match product ID
          // variant_id removed - variants no longer exist
        },
        transaction, // Use transaction
      });
      
      if (inventory) {
        // Decrement quantity
        await inventory.decrement('quantity', { by: 1, transaction }); // Decrement quantity
        
        // Create inventory movement record
        await InventoryMovement.create({
          product_id: inventoryItem.product_id, // Set product ID
          // variant_id removed - variants no longer exist
          inventory_item_id: inventoryItem.id, // Set inventory item ID
          movement_type: 'OUT', // Movement type is OUT
          quantity_change: -1, // Quantity change is -1
          quantity_after: inventory.quantity, // Quantity after movement
          reason: reason || `Status changed from ${oldStatus} to ${newStatus}`, // Movement reason
          reference_id: null, // No reference ID
          user_id: userId, // Set user ID
        }, { transaction }); // Create movement in transaction
      }
    }
    
    // Commit transaction
    await transaction.commit(); // Commit transaction
    
    // Reload inventory item with associations
    const updatedItem = await InventoryItem.findByPk(inventoryItem.id, {
      include: [
        {
          model: Product, // Include product details
          as: 'product', // Use product alias
        },
        // ProductVariant removed - variants no longer exist
      ],
    });
    
    // Log status update
    logger.info(`Inventory item status updated: ${uid}`, {
      itemId: inventoryItem.id,
      uid,
      oldStatus, // Old status
      newStatus, // New status
      reason, // Reason for change
      userId, // Log user ID
    });
    
    // Return updated inventory item
    return updatedItem; // Return inventory item
  } catch (error) {
    // Rollback transaction on error
    await transaction.rollback(); // Rollback transaction
    throw error; // Re-throw error
  }
};

/**
 * Scan inventory item (get item by UID/barcode)
 * Scans an inventory item by UID or barcode and returns details with QR code
 * @param {string} identifier - UID or barcode
 * @returns {Promise<Object>} Inventory item with QR code data URL
 */
const scanInventoryItem = async (identifier) => {
  // Get inventory item by UID or barcode
  const inventoryItem = await getInventoryItemByUID(identifier); // Get inventory item
  
  // Generate QR code data URL for the UID
  const qrCodeDataURL = await generateQRCodeDataURL(inventoryItem.uid); // Generate QR code
  
  // Return inventory item with QR code
  return {
    ...inventoryItem.toJSON(), // Convert to JSON
    qr_code: qrCodeDataURL, // Add QR code data URL
  };
};

/**
 * List inventory items with filters
 * Retrieves paginated list of inventory items
 * @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
 * @param {number|null} options.variantId - Filter by variant ID (DEPRECATED - variants removed)
 * @param {string|null} options.status - Filter by status
 * @param {string|null} options.source - Filter by source
 * @returns {Promise<Object>} Paginated inventory items list
 */
const listInventoryItems = async (options = {}) => {
  // Extract options with defaults
  const {
    page = 1,
    limit = 10,
    productId = null,
    variantId = null, // DEPRECATED - variants removed, kept for API compatibility
    status = null,
    source = null,
    sourceReferenceId = null, // Filter by source_reference_id (GRN ID or Production Order ID)
    uid = null,
    barcode = 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
  }
  
  // variantId filter removed - variants no longer exist in the system
  
  // Add status filter if provided
  if (status) {
    where.status = status; // Filter by status
  }
  
  // Add source filter if provided
  if (source) {
    where.source = source; // Filter by source
  }
  
  // Add source_reference_id filter if provided
  if (sourceReferenceId) {
    where.source_reference_id = sourceReferenceId; // Filter by source reference ID
  }
  
  // Add UID filter if provided (using LIKE for partial matching)
  if (uid) {
    where.uid = { [Op.like]: `%${uid}%` }; // Filter by UID (partial match)
  }
  
  // Add barcode filter if provided (using LIKE for partial matching)
  if (barcode) {
    where.barcode = { [Op.like]: `%${barcode}%` }; // Filter by barcode (partial match)
  }
  
  // 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'], // Select specific attributes
      },
      // ProductVariant removed - variants no longer exist
    ],
    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 items and total count
  const { count, rows } = await InventoryItem.findAndCountAll(queryOptions); // Get items with count
  
  // Calculate total pages
  const totalPages = Math.ceil(count / limit); // Calculate total pages
  
  // Get status counts for summary (only if product_id is provided, not for general queries)
  // This provides accurate counts across ALL items matching filters, not just the current page
  let statusCounts = null;
  if (productId) {
    // Build base where clause for status counts (same filters but no status filter itself)
    // Build where clause for status counts (variants removed - no variant_id filtering)
    const statusWhere = {
      product_id: productId,
    };
    
    // Apply other filters (source, uid, barcode) but NOT status
    if (source) {
      statusWhere.source = source;
    }
    if (uid) {
      statusWhere.uid = { [Op.like]: `%${uid}%` };
    }
    if (barcode) {
      statusWhere.barcode = { [Op.like]: `%${barcode}%` };
    }
    
    // Get counts grouped by status
    const statusGroups = await InventoryItem.findAll({
      where: statusWhere,
      attributes: [
        'status',
        [sequelize.fn('COUNT', sequelize.col('InventoryItem.id')), 'count'],
      ],
      group: ['status'],
      raw: true,
    });
    
    // Transform to object format
    statusCounts = {
      IN_STOCK: 0,
      RESERVED: 0,
      SOLD: 0,
      RETURNED: 0,
      DAMAGED: 0,
      SCRAPPED: 0,
    };
    
    statusGroups.forEach((group) => {
      const status = group.status;
      const count = parseInt(group.count) || 0;
      if (status && statusCounts.hasOwnProperty(status)) {
        statusCounts[status] = count;
      }
    });
  }
  
  // Return paginated results with status counts
  return {
    items: rows, // Inventory item records
    pagination: {
      page: parseInt(page), // Current page
      limit: parseInt(limit), // Items per page
      total: count, // Total items
      totalPages, // Total pages
    },
    ...(statusCounts && { statusCounts }), // Include status counts if available
  };
};

// Export service functions
module.exports = {
  createInventoryItem, // Create inventory item function
  getInventoryItemByUID, // Get inventory item by UID function
  updateInventoryItemStatus, // Update inventory item status function
  scanInventoryItem, // Scan inventory item function
  listInventoryItems, // List inventory items function
};

