/**
 * Inventory Finalization Service
 * Handles finalizing inventory when sales are paid (marking items as SOLD)
 * Also handles auto-marking of inventory items for random sales
 */

const { InventoryItem, InventoryMovement, Inventory, SaleItem, Product } = require('../../../models');
const { Op } = require('sequelize');
const logger = require('../../../utils/logger');

/**
 * Finalize inventory when sale is paid
 * Marks scanned items (RESERVED) as SOLD
 * Auto-marks random inventory items as SOLD for items sold without scanning
 * Ensures quantity and UID-based inventory stay in sync
 * @param {number} saleId - Sale ID
 * @param {Object} transaction - Sequelize transaction (optional)
 * @returns {Promise<Object>} Finalization result
 */
const finalizeInventoryForSale = async (saleId, transaction = null) => {
  try {
    // Get sale items
    const saleItems = await SaleItem.findAll({
      where: { sale_id: saleId },
      include: [
        {
          model: Product,
          as: 'product',
          attributes: ['id', 'name', 'track_inventory'],
        },
      ],
      transaction,
    });

    if (!saleItems || saleItems.length === 0) {
      logger.warn(`No sale items found for sale ${saleId}`);
      return { markedAsSold: 0, errors: [] };
    }

    const results = {
      markedAsSold: 0, // Count of items marked as SOLD
      errors: [], // Errors encountered
    };

    // Group sale items by product_id to handle multiple sale items for same product
    const groupedItems = {};
    for (const saleItem of saleItems) {
      const { product_id, quantity, inventory_item_id, product } = saleItem;

      // Skip if product doesn't track inventory
      if (!product || !product.track_inventory) {
        continue;
      }

      const key = `${product_id}`;
      if (!groupedItems[key]) {
        groupedItems[key] = {
          product_id,
          product,
          totalQuantity: 0,
          scannedItems: [], // Array of inventory_item_ids that were scanned
        };
      }

      const itemQuantity = parseFloat(quantity || 1);
      groupedItems[key].totalQuantity += itemQuantity;

      // Collect scanned items (items with inventory_item_id)
      // If inventory_item_id is present, 1 item was scanned, and (quantity - 1) were random
      if (inventory_item_id) {
        groupedItems[key].scannedItems.push(inventory_item_id);
        // The rest of the quantity (if > 1) is random, which will be handled in the randomCount calculation
      }
    }

    // Process each grouped product
    for (const key in groupedItems) {
      const group = groupedItems[key];
      const { product_id, product, totalQuantity, scannedItems } = group;

      // 1. Mark scanned items (RESERVED) as SOLD
      for (const inventoryItemId of scannedItems) {
        try {
          const inventoryItem = await InventoryItem.findByPk(inventoryItemId, { transaction });
          if (inventoryItem) {
            // Validate inventory item belongs to the product
            if (inventoryItem.product_id !== product_id) {
              logger.warn(`Inventory item ${inventoryItemId} does not match product for sale ${saleId}`);
              continue;
            }

            if (inventoryItem.status === 'RESERVED') {
              inventoryItem.status = 'SOLD';
              await inventoryItem.save({ transaction });

              // Create movement record for status change
              await InventoryMovement.create({
                product_id,
                inventory_item_id: inventoryItemId,
                movement_type: 'OUT',
                quantity_change: -1,
                quantity_after: null,
                reason: 'SALE_FINALIZED',
                reference_id: saleId,
                user_id: null,
              }, { transaction });

              results.markedAsSold++;
              logger.info(`Marked scanned inventory item ${inventoryItemId} as SOLD for sale ${saleId}`);
            } else if (inventoryItem.status === 'SOLD') {
              logger.debug(`Inventory item ${inventoryItemId} already marked as SOLD`);
              results.markedAsSold++; // Count as already marked
            } else {
              logger.warn(`Inventory item ${inventoryItemId} has unexpected status: ${inventoryItem.status}`);
            }
          }
        } catch (error) {
          logger.error(`Error marking scanned item ${inventoryItemId} as SOLD:`, error);
          results.errors.push({ item_id: inventoryItemId, error: error.message });
        }
      }

      // 2. Auto-mark random inventory items as SOLD (for items sold without scanning)
      const scannedCount = scannedItems.length;
      const randomCount = totalQuantity - scannedCount;

      if (randomCount > 0) {
        try {
          // Find available inventory items (IN_STOCK) for this product
          // Exclude already scanned items to avoid double-counting
          const availableItems = await InventoryItem.findAll({
            where: {
              product_id,
              status: 'IN_STOCK',
              id: {
                [Op.notIn]: scannedItems, // Exclude already scanned items
              },
            },
            limit: Math.floor(randomCount), // Only mark as many as were sold randomly
            order: [['created_at', 'ASC']], // FIFO - oldest items first
            transaction,
          });

          // Mark found items as SOLD
          for (const item of availableItems) {
            item.status = 'SOLD';
            await item.save({ transaction });

            // Create movement record
            await InventoryMovement.create({
              product_id,
              inventory_item_id: item.id,
              movement_type: 'OUT',
              quantity_change: -1,
              quantity_after: null,
              reason: 'SALE_FINALIZED_RANDOM',
              reference_id: saleId,
              user_id: null,
            }, { transaction });

            results.markedAsSold++;
          }

          if (availableItems.length < randomCount) {
            logger.warn(
              `Sale ${saleId}: Only ${availableItems.length} items available to mark as SOLD, but ${randomCount} items were sold randomly for product ${product_id}. ` +
              `Inventory may be out of sync. Quantity: ${totalQuantity}, Scanned: ${scannedCount}, Random: ${randomCount}, Available: ${availableItems.length}`
            );
            results.errors.push({
              product_id,
              message: `Only ${availableItems.length} items marked as SOLD out of ${randomCount} random items. Total quantity: ${totalQuantity}, Scanned: ${scannedCount}`,
            });
          } else {
            logger.info(
              `Marked ${availableItems.length} random inventory items as SOLD for sale ${saleId}, product ${product_id} (Total: ${totalQuantity}, Scanned: ${scannedCount}, Random: ${randomCount})`
            );
          }
        } catch (error) {
          logger.error(`Error auto-marking random items as SOLD for sale ${saleId}:`, error);
          results.errors.push({ product_id, error: error.message });
        }
      } else {
        logger.debug(`No random items to mark for sale ${saleId}, product ${product_id} (all ${totalQuantity} items were scanned)`);
      }
    }

    logger.info(`Inventory finalized for sale ${saleId}: ${results.markedAsSold} items marked as SOLD`);
    return results;
  } catch (error) {
    logger.error(`Error finalizing inventory for sale ${saleId}:`, error);
    throw error;
  }
};

module.exports = {
  finalizeInventoryForSale,
};

