/**
 * Dispatch Service
 * Handles dispatching sales - removing items from inventory after sale
 */

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

/**
 * Dispatch a sale - mark items as dispatched and optionally remove from inventory
 * @param {number} saleId - Sale ID
 * @param {number} userId - User ID who is dispatching
 * @param {Object} transaction - Sequelize transaction (optional)
 * @returns {Promise<Object>} Dispatch result
 */
const dispatchSale = async (saleId, userId, transaction = null) => {
  try {
    // Get sale
    const sale = await Sale.findByPk(saleId, {
      include: [
        {
          model: SaleItem,
          as: 'items',
          include: [
            {
              model: Product,
              as: 'product',
              attributes: ['id', 'name', 'track_inventory'],
            },
          ],
        },
      ],
      transaction,
    });

    if (!sale) {
      throw new NotFoundError(`Sale with ID ${saleId} not found`);
    }

    // Check if sale is paid
    if (sale.status !== 'PAID') {
      throw new ValidationError(`Sale ${saleId} must be PAID before dispatching. Current status: ${sale.status}`);
    }

    // Check if already dispatched
    if (sale.dispatched) {
      throw new ValidationError(`Sale ${saleId} has already been dispatched`);
    }

    // Update sale dispatch status
    sale.dispatched = true;
    sale.dispatched_at = new Date();
    sale.dispatched_by = userId;
    await sale.save({ transaction });

    // Get all inventory items that were sold (SOLD status) for this sale
    const soldItems = await InventoryItem.findAll({
      where: {
        status: 'SOLD',
        id: {
          [Op.in]: sale.items
            .filter(item => item.inventory_item_id)
            .map(item => item.inventory_item_id)
            .filter(Boolean),
        },
      },
      transaction,
    });

    // For items without inventory_item_id (random sales), find items marked as SOLD for this sale via movements
    const movements = await InventoryMovement.findAll({
      where: {
        reason: {
          [Op.in]: ['SALE_FINALIZED', 'SALE_FINALIZED_RANDOM'],
        },
        reference_id: saleId,
      },
      transaction,
    });

    const allSoldItemIds = [
      ...soldItems.map(item => item.id),
      ...movements.map(m => m.inventory_item_id).filter(Boolean),
    ];

    // Remove duplicate IDs
    const uniqueSoldItemIds = [...new Set(allSoldItemIds)];

    logger.info(`Dispatching sale ${saleId}: ${uniqueSoldItemIds.length} inventory items to be removed`, {
      saleId,
      userId,
      itemCount: uniqueSoldItemIds.length,
    });

    // Optionally: You could mark items as DISPATCHED status or delete them
    // For now, we'll just log that they've been dispatched
    // Items remain as SOLD status, which indicates they're no longer in inventory

    // Update quantity-based inventory to ensure sync
    // Group by product/variant to update quantities
    const inventoryUpdates = {};
    for (const saleItem of sale.items) {
      const { product_id, variant_id, product } = saleItem;
      
      if (!product || !product.track_inventory) {
        continue;
      }

      const key = `${product_id}_${variant_id || 'null'}`;
      if (!inventoryUpdates[key]) {
        inventoryUpdates[key] = {
          product_id,
          variant_id: variant_id || null,
          quantity_sold: 0,
        };
      }

      // Count items for this product/variant that were sold
      const itemMovements = movements.filter(m => 
        m.product_id === product_id && 
        (m.variant_id === variant_id || (variant_id === null && m.variant_id === null))
      );
      
      inventoryUpdates[key].quantity_sold += itemMovements.length;
    }

    // Update quantity-based inventory (it should already be updated, but verify)
    for (const key in inventoryUpdates) {
      const update = inventoryUpdates[key];
      const inventory = await Inventory.findOne({
        where: {
          product_id: update.product_id,
          variant_id: update.variant_id,
        },
        transaction,
      });

      if (inventory) {
        // Verify quantity matches UID-based count
        const uidBasedCount = await InventoryItem.count({
          where: {
            product_id: update.product_id,
            variant_id: update.variant_id,
            status: 'IN_STOCK',
          },
          transaction,
        });

        if (parseFloat(inventory.quantity) !== uidBasedCount) {
          logger.warn(
            `Inventory quantity mismatch for product ${update.product_id}, variant ${update.variant_id}: ` +
            `quantity-based=${inventory.quantity}, uid-based=${uidBasedCount}`
          );
          
          // Auto-sync: update quantity to match UID-based count
          inventory.quantity = uidBasedCount;
          await inventory.save({ transaction });
          
          logger.info(
            `Auto-synced inventory quantity for product ${update.product_id}, variant ${update.variant_id}: ${uidBasedCount}`
          );
        }
      }
    }

    logger.info(`Sale ${saleId} dispatched successfully by user ${userId}`, {
      saleId,
      userId,
      dispatchedAt: sale.dispatched_at,
      itemsProcessed: uniqueSoldItemIds.length,
    });

    return {
      success: true,
      saleId,
      dispatched: true,
      dispatchedAt: sale.dispatched_at,
      dispatchedBy: userId,
      itemsProcessed: uniqueSoldItemIds.length,
    };
  } catch (error) {
    logger.error(`Error dispatching sale ${saleId}:`, error);
    throw error;
  }
};

module.exports = {
  dispatchSale,
};

