const db = require('../models');
const { Op } = require('sequelize');

/**
 * Check if all required materials have been issued for a Production Order
 */
const areAllMaterialsIssued = async (productionOrderId, transaction = null) => {
  const options = transaction ? { transaction } : {};
  
  const components = await db.ProductionOrderComponent.findAll({
    where: {
      production_order_id: productionOrderId
    },
    ...options
  });

  if (!components || components.length === 0) {
    return false; // No components means can't determine
  }

  // Check if all components have been fully issued
  for (const component of components) {
    const requiredQty = parseFloat(component.required_quantity || 0);
    const issuedQty = parseFloat(component.issued_quantity || 0);
    
    if (issuedQty < requiredQty) {
      return false; // At least one component is not fully issued
    }
  }

  return true; // All components are fully issued
};

/**
 * Check if finished goods have been received for a Production Order
 */
const areFinishedGoodsReceived = async (productionOrder, transaction = null) => {
  const options = transaction ? { transaction } : {};
  
  // Check if InventoryGoodsReceipt exists for this Production Order
  // The reference_number stores the production_order_number (not ID)
  const grn = await db.InventoryGoodsReceipt.findOne({
    where: {
      reference_type: 'PRODUCTION_ORDER',
      reference_number: productionOrder.production_order_number,
      status: 'POSTED'
    },
    ...options
  });

  return !!grn;
};

/**
 * Update Production Order status based on workflow events
 * 
 * @param {Object} productionOrder - Production Order instance
 * @param {String} triggerEvent - Event that triggered status check: 'MATERIAL_RECEIVED', 'PRODUCTION_CONFIRMED', 'COMPLETION_CHECK'
 * @param {Integer} userId - User ID who triggered the event
 * @param {Object} transaction - Sequelize transaction (optional)
 * @returns {Object} Updated Production Order with new status
 */
exports.updateProductionOrderStatus = async (productionOrder, triggerEvent, userId, transaction = null) => {
  const options = transaction ? { transaction } : {};
  const currentStatus = productionOrder.status;

  // Don't update if already CLOSED or CANCELLED
  if (currentStatus === 'CLOSED' || currentStatus === 'CANCELLED') {
    return productionOrder;
  }

  let newStatus = currentStatus;
  let updateData = {};

  try {
    switch (triggerEvent) {
      case 'MATERIAL_RECEIVED':
        // Auto-change to IN_PROGRESS when materials are received
        // Only if current status is RELEASED
        if (currentStatus === 'RELEASED') {
          newStatus = 'IN_PROGRESS';
          updateData = {
            status: newStatus,
            updated_by: userId
          };
          console.log(`Production Order ${productionOrder.production_order_number}: Status changed from ${currentStatus} to ${newStatus} (Materials received)`);
        }
        break;

      case 'PRODUCTION_CONFIRMED':
        // Status is already updated in productionConfirmationController
        // This is just for logging/completeness
        // Status changes: PARTIALLY_CONFIRMED or CONFIRMED based on confirmed_quantity
        break;

      case 'COMPLETION_CHECK':
        // Auto-change to CLOSED when:
        // 1. All materials are issued
        // 2. Finished goods are received
        // 3. Status is CONFIRMED
        if (currentStatus === 'CONFIRMED') {
          const allMaterialsIssued = await areAllMaterialsIssued(productionOrder.id, transaction);
          const finishedGoodsReceived = await areFinishedGoodsReceived(productionOrder, transaction);

          if (allMaterialsIssued && finishedGoodsReceived) {
            newStatus = 'CLOSED';
            updateData = {
              status: newStatus,
              closed_at: new Date(),
              closed_by: userId,
              updated_by: userId
            };
            console.log(`Production Order ${productionOrder.production_order_number}: Status changed from ${currentStatus} to ${newStatus} (All materials issued and finished goods received)`);
          }
        }
        break;

      default:
        console.warn(`Unknown trigger event: ${triggerEvent}`);
        return productionOrder;
    }

    // Update Production Order if status changed
    if (newStatus !== currentStatus && Object.keys(updateData).length > 0) {
      await productionOrder.update(updateData, options);
      
      // Reload to get updated data
      const updatedPO = await db.ProductionOrder.findByPk(productionOrder.id, {
        include: [
          { model: db.Company, as: 'company', attributes: ['id', 'code', 'name'] },
          { model: db.Plant, as: 'plant', attributes: ['id', 'code', 'name'] },
          { model: db.Material, as: 'finishedMaterial', attributes: ['id', 'material_number', 'description'] }
        ],
        ...options
      });

      return updatedPO;
    }

    return productionOrder;
  } catch (error) {
    console.error('Error updating Production Order status:', error);
    throw error;
  }
};

/**
 * Check and update Production Order status when materials are received
 * Called from materialIssueController.receive
 */
exports.onMaterialReceived = async (materialIssue, userId, transaction = null) => {
  if (!materialIssue.production_order_id) {
    return null; // Not linked to a Production Order
  }

  const productionOrder = await db.ProductionOrder.findByPk(materialIssue.production_order_id, {
    transaction
  });

  if (!productionOrder) {
    return null;
  }

  return await exports.updateProductionOrderStatus(
    productionOrder,
    'MATERIAL_RECEIVED',
    userId,
    transaction
  );
};

/**
 * Check and update Production Order status when production is confirmed
 * Called from productionConfirmationController (after status update)
 */
exports.onProductionConfirmed = async (productionOrder, userId, transaction = null) => {
  // First, trigger completion check to see if it should be CLOSED
  return await exports.updateProductionOrderStatus(
    productionOrder,
    'COMPLETION_CHECK',
    userId,
    transaction
  );
};

/**
 * Manually trigger completion check
 * Can be called from any workflow point to check if Production Order should be CLOSED
 */
exports.checkAndCloseIfComplete = async (productionOrderId, userId, transaction = null) => {
  const options = transaction ? { transaction } : {};
  
  const productionOrder = await db.ProductionOrder.findByPk(productionOrderId, options);

  if (!productionOrder) {
    throw new Error('Production Order not found');
  }

  return await exports.updateProductionOrderStatus(
    productionOrder,
    'COMPLETION_CHECK',
    userId,
    transaction
  );
};

/**
 * Cancel stock reservations when Production Order is cancelled
 * Called when Production Order status changes to CANCELLED
 */
exports.onProductionOrderCancelled = async (productionOrderId, userId, transaction = null) => {
  try {
    const stockReservationService = require('./stockReservationService');
    const cancelledCount = await stockReservationService.cancelReservationsForProductionOrder(
      productionOrderId,
      userId,
      transaction
    );
    console.log(`Cancelled ${cancelledCount} stock reservations for Production Order ${productionOrderId}`);
    return cancelledCount;
  } catch (error) {
    console.error('Error cancelling stock reservations:', error);
    // Don't throw - allow cancellation to proceed even if reservation cancellation fails
    return 0;
  }
};

