/**
 * BOM Dimension Service
 * Handles dimension requirements and validation for BOMs with RM products
 */

// Import required models and services
const { BOM, BOMItem, Product, RMInventoryPiece } = require('../models');
const dimensionValidationService = require('./dimensionValidationService');
const materialAllocationService = require('./materialAllocationService');
// Import custom error classes
const { ValidationError, NotFoundError } = require('../utils/errors');
// Import logger
const logger = require('../utils/logger');

/**
 * Check if a BOM item is dimension-based
 * @param {Object} bomItem - BOM item to check
 * @returns {boolean} True if dimension-based, false otherwise
 */
function isDimensionBasedItem(bomItem) {
  if (!bomItem) return false;
  
  // Check if dimension fields are present
  return !!(bomItem.required_length && bomItem.required_width && bomItem.dimension_unit);
}

/**
 * Validate BOM item dimension requirements
 * @param {Object} bomItem - BOM item data
 * @param {Object} rmProduct - Raw material product
 * @returns {Object} Validation result
 */
function validateBOMItemDimensions(bomItem, rmProduct) {
  // Check if product requires dimensions
  const productValidation = dimensionValidationService.validateProductDimensionRequirement(rmProduct);
  
  if (!productValidation.isValid) {
    return {
      isValid: false,
      error: productValidation.error
    };
  }

  // Handle non-RM products (FG products in BOM - not typical, but handle it)
  if (rmProduct.product_type !== 'RM') {
    if (isDimensionBasedItem(bomItem)) {
      return {
        isValid: false,
        error: `Non-RM product ${rmProduct.name} cannot use dimension-based BOM`
      };
    }
    
    return {
      isValid: true,
      requiresDimensions: false,
      useDimensions: false
    };
  }

  // RM products: Match BOM item's use_dimensions with product's track_by_dimensions
  // Dimension-based RM (track_by_dimensions = true) MUST use dimensions in BOM
  // Special items RM (track_by_dimensions = false) MUST use quantity in BOM
  
  if (productValidation.requiresDimensions) {
    // Dimension-based RM product - MUST use dimensions in BOM
    if (!isDimensionBasedItem(bomItem)) {
      return {
        isValid: false,
        error: `Dimension-based RM product ${rmProduct.name} requires dimension specifications (required_length, required_width, dimension_unit)`
      };
    }
  } else {
    // Special items RM product (track_by_dimensions = false) - MUST use quantity in BOM
    if (isDimensionBasedItem(bomItem)) {
      return {
        isValid: false,
        error: `Special items RM product ${rmProduct.name} (zippers, buttons, etc.) requires quantity_per_unit (not dimensions)`
      };
    }
    
    // Validate quantity_per_unit is provided for special items RM
    if (!bomItem.quantity_per_unit || parseFloat(bomItem.quantity_per_unit) <= 0) {
      return {
        isValid: false,
        error: `Special items RM product ${rmProduct.name} requires quantity_per_unit (not dimensions)`
      };
    }
    
    return {
      isValid: true,
      requiresDimensions: false,
      useDimensions: false,
      message: 'Special items RM using quantity-based BOM'
    };
  }

  // Dimension-based BOM item - validate dimension fields
  if (!bomItem.required_length || !bomItem.required_width || !bomItem.dimension_unit) {
    return {
      isValid: false,
      error: `Dimension-based BOM item for ${rmProduct.name} requires required_length, required_width, and dimension_unit`
    };
  }

  // Validate dimension values
  const dimensionValidation = dimensionValidationService.validateDimensions(
    bomItem.required_length,
    bomItem.required_width,
    bomItem.dimension_unit
  );

  if (!dimensionValidation.isValid) {
    return {
      isValid: false,
      error: `Invalid dimensions for ${rmProduct.name}: ${dimensionValidation.errors.join(', ')}`
    };
  }

  // Validate unit compatibility with product
  if (bomItem.dimension_unit !== rmProduct.unit_of_measure) {
    // Test unit conversion
    const conversionTest = dimensionValidationService.convertDimensions(
      {
        length: bomItem.required_length,
        width: bomItem.required_width,
        unit: bomItem.dimension_unit
      },
      rmProduct.unit_of_measure
    );

    if (!conversionTest.isValid) {
      return {
        isValid: false,
        error: `Unit conversion failed for ${rmProduct.name}: ${conversionTest.error}`
      };
    }
  }

  return {
    isValid: true,
    requiresDimensions: true,
    useDimensions: true,
    dimensions: {
      length: bomItem.required_length,
      width: bomItem.required_width,
      unit: bomItem.dimension_unit
    }
  };
}

/**
 * Calculate dimension requirements for production quantity
 * @param {Object} bomItem - BOM item with dimension requirements
 * @param {number} productionQuantity - Quantity to produce
 * @returns {Object} Dimension requirement calculation
 */
function calculateDimensionRequirements(bomItem, productionQuantity) {
  if (!isDimensionBasedItem(bomItem)) {
    return {
      isValid: false,
      error: 'BOM item does not use dimensions'
    };
  }

  if (!bomItem.required_length || !bomItem.required_width) {
    return {
      isValid: false,
      error: 'BOM item missing dimension requirements'
    };
  }

  if (productionQuantity <= 0) {
    return {
      isValid: false,
      error: 'Production quantity must be positive'
    };
  }

  const areaPerUnit = bomItem.required_length * bomItem.required_width;
  const totalAreaRequired = areaPerUnit * productionQuantity;

  return {
    isValid: true,
    areaPerUnit: areaPerUnit,
    totalAreaRequired: totalAreaRequired,
    dimensions: {
      length: bomItem.required_length,
      width: bomItem.required_width,
      unit: bomItem.dimension_unit
    },
    productionQuantity: productionQuantity,
    unit: bomItem.dimension_unit
  };
}

/**
 * Validate BOM feasibility with available inventory
 * @param {Object} bom - BOM with items
 * @param {number} productionQuantity - Quantity to produce
 * @returns {Promise<Object>} Feasibility analysis
 */
async function validateBOMFeasibility(bom, productionQuantity) {
  if (!bom || !bom.items) {
    return {
      isValid: false,
      error: 'BOM or BOM items not provided'
    };
  }

  if (productionQuantity <= 0) {
    return {
      isValid: false,
      error: 'Production quantity must be positive'
    };
  }

  const feasibilityResults = [];
  let overallFeasible = true;

  for (const bomItem of bom.items) {
    const rmProduct = bomItem.rawMaterial;
    
    if (!rmProduct) {
      return {
        isValid: false,
        error: `Raw material product not found for BOM item ${bomItem.id}`
      };
    }

    const isDimensionBased = isDimensionBasedItem(bomItem);
    
    let itemResult = {
      bomItemId: bomItem.id,
      productId: rmProduct.id,
      productName: rmProduct.name,
      useDimensions: isDimensionBased
    };

    if (isDimensionBased) {
      // Dimension-based feasibility check
      const dimensionReq = calculateDimensionRequirements(bomItem, productionQuantity);
      
      if (!dimensionReq.isValid) {
        itemResult.feasible = false;
        itemResult.error = dimensionReq.error;
        overallFeasible = false;
      } else {
        try {
          // Fetch available inventory pieces for this product
          const { Op } = require('sequelize');
          const availablePieces = await RMInventoryPiece.findAll({
            where: {
              product_id: rmProduct.id,
              status: { [Op.in]: ['FULL', 'USABLE', 'WASTE'] } // FULL, USABLE, and WASTE pieces are allocatable (WASTE for reuse)
            },
            order: [
              ['status', 'DESC'], // FULL pieces first, then USABLE, then WASTE
              ['created_at', 'ASC'] // Oldest first
            ]
          });

          // Create a BOM item-like object for allocation check
          const bomItemForAllocation = {
            rm_product_id: bomItem.rm_product_id,
            required_length: dimensionReq.dimensions.length,
            required_width: dimensionReq.dimensions.width,
            dimension_unit: dimensionReq.dimensions.unit,
            isDimensionBased: () => true // Helper method for allocation service
          };

          // Check allocation feasibility
          const allocationResult = materialAllocationService.allocateMaterialForBOMItem(
            availablePieces,
            bomItemForAllocation,
            productionQuantity,
            { strategy: materialAllocationService.ALLOCATION_STRATEGIES.BEST_FIT }
          );

          if (!allocationResult.isValid) {
            itemResult.feasible = false;
            itemResult.error = allocationResult.error || 'Allocation check failed';
            overallFeasible = false;
          } else {
            // Calculate available area from pieces - convert all to required unit for accurate calculation
            const requiredUnit = dimensionReq.dimensions.unit;
            let availableArea = 0;
            
            for (const piece of availablePieces) {
              const dims = materialAllocationService.getAvailableDimensions(piece);
              if (dims) {
                // Convert piece dimensions to required unit if needed
                let pieceLength = parseFloat(dims.length);
                let pieceWidth = parseFloat(dims.width);
                
                if (dims.unit !== requiredUnit) {
                  const converted = dimensionValidationService.convertDimensions(
                    { length: pieceLength, width: pieceWidth, unit: dims.unit },
                    requiredUnit
                  );
                  if (converted.isValid) {
                    pieceLength = converted.dimensions.length;
                    pieceWidth = converted.dimensions.width;
                  } else {
                    // Skip this piece if conversion fails
                    logger.warn('Unit conversion failed for piece', { pieceId: piece.id, fromUnit: dims.unit, toUnit: requiredUnit });
                    continue;
                  }
                }
                
                availableArea += (pieceLength * pieceWidth);
              }
            }

            itemResult.feasible = allocationResult.isFullyAllocated;
            itemResult.totalAreaRequired = dimensionReq.totalAreaRequired;
            itemResult.availableArea = parseFloat(availableArea.toFixed(3));
            itemResult.shortfall = allocationResult.isFullyAllocated ? 0 : Math.max(0, dimensionReq.totalAreaRequired - availableArea);
            itemResult.allocatedUnits = allocationResult.totalAllocatedUnits || 0;
            itemResult.remainingQuantity = allocationResult.remainingQuantity || productionQuantity;
            
            if (!allocationResult.isFullyAllocated) {
              overallFeasible = false;
            }
          }
        } catch (error) {
          // If allocation service fails, mark as not feasible
          itemResult.feasible = false;
          itemResult.error = `Allocation check failed: ${error.message}`;
          overallFeasible = false;
          logger.error('Allocation check error', { error: error.message, stack: error.stack });
        }
      }
    } else {
      // Quantity-based feasibility check
      const totalQuantityRequired = bomItem.quantity_per_unit * productionQuantity;
      
      // For quantity-based items, we assume feasibility (would integrate with existing inventory system)
      itemResult.feasible = true;
      itemResult.quantityRequired = totalQuantityRequired;
      itemResult.message = 'Quantity-based feasibility assumed (integration with existing inventory system needed)';
    }

    feasibilityResults.push(itemResult);
  }

  return {
    isValid: true,
    overallFeasible: overallFeasible,
    productionQuantity: productionQuantity,
    bomId: bom.id,
    itemResults: feasibilityResults
  };
}

/**
 * Convert BOM dimension requirements to different unit
 * @param {Object} bomItem - BOM item with dimensions
 * @param {string} targetUnit - Target unit for conversion
 * @returns {Object} Conversion result
 */
function convertBOMDimensions(bomItem, targetUnit) {
  if (!isDimensionBasedItem(bomItem)) {
    return {
      isValid: false,
      error: 'BOM item does not use dimensions'
    };
  }

  if (!bomItem.required_length || !bomItem.required_width || !bomItem.dimension_unit) {
    return {
      isValid: false,
      error: 'BOM item missing dimension requirements'
    };
  }

  const conversionResult = dimensionValidationService.convertDimensions(
    {
      length: bomItem.required_length,
      width: bomItem.required_width,
      unit: bomItem.dimension_unit
    },
    targetUnit
  );

  if (!conversionResult.isValid) {
    return conversionResult;
  }

  return {
    isValid: true,
    originalDimensions: {
      length: bomItem.required_length,
      width: bomItem.required_width,
      unit: bomItem.dimension_unit
    },
    convertedDimensions: conversionResult.dimensions,
    areaOriginal: bomItem.required_length * bomItem.required_width,
    areaConverted: conversionResult.dimensions.length * conversionResult.dimensions.width
  };
}

/**
 * Validate mixed BOM (dimension + quantity based items)
 * @param {Array} bomItems - Array of BOM items
 * @returns {Object} Validation result
 */
function validateMixedBOM(bomItems) {
  if (!Array.isArray(bomItems)) {
    return {
      isValid: false,
      error: 'BOM items must be an array'
    };
  }

  if (bomItems.length === 0) {
    return {
      isValid: false,
      error: 'BOM must have at least one item'
    };
  }

  const validationResults = [];
  let hasDimensionBased = false;
  let hasQuantityBased = false;

  for (let i = 0; i < bomItems.length; i++) {
    const bomItem = bomItems[i];
    
    const isDimensionBased = isDimensionBasedItem(bomItem);
    
    const itemResult = {
      index: i,
      bomItemId: bomItem.id || null,
      productId: bomItem.rm_product_id,
      useDimensions: isDimensionBased
    };

    if (isDimensionBased) {
      hasDimensionBased = true;
      
      // Validate dimension requirements
      if (!bomItem.required_length || !bomItem.required_width || !bomItem.dimension_unit) {
        itemResult.isValid = false;
        itemResult.error = 'Dimension-based BOM item missing required dimension fields';
      } else {
        const dimensionValidation = dimensionValidationService.validateDimensions(
          bomItem.required_length,
          bomItem.required_width,
          bomItem.dimension_unit
        );
        
        itemResult.isValid = dimensionValidation.isValid;
        if (!dimensionValidation.isValid) {
          itemResult.error = `Invalid dimensions: ${dimensionValidation.errors.join(', ')}`;
        }
      }
    } else {
      hasQuantityBased = true;
      
      // Validate quantity requirements
      if (!bomItem.quantity_per_unit || bomItem.quantity_per_unit <= 0) {
        itemResult.isValid = false;
        itemResult.error = 'Quantity-based BOM item must have positive quantity_per_unit';
      } else {
        itemResult.isValid = true;
      }
    }

    validationResults.push(itemResult);
  }

  const allValid = validationResults.every(result => result.isValid);

  return {
    isValid: allValid,
    hasDimensionBased: hasDimensionBased,
    hasQuantityBased: hasQuantityBased,
    isMixed: hasDimensionBased && hasQuantityBased,
    itemResults: validationResults,
    summary: {
      totalItems: bomItems.length,
      dimensionBasedItems: validationResults.filter(r => r.useDimensions).length,
      quantityBasedItems: validationResults.filter(r => !r.useDimensions).length
    }
  };
}

/**
 * Generate dimension requirements summary for BOM
 * @param {Object} bom - BOM with items
 * @param {number} productionQuantity - Quantity to produce
 * @returns {Object} Requirements summary
 */
function generateDimensionRequirementsSummary(bom, productionQuantity) {
  if (!bom || !bom.items) {
    return {
      isValid: false,
      error: 'BOM or BOM items not provided'
    };
  }

  const summary = {
    bomId: bom.id,
    productionQuantity: productionQuantity,
    dimensionBasedItems: [],
    quantityBasedItems: [],
    totalAreaRequired: 0,
    unitBreakdown: {}
  };

  for (const bomItem of bom.items) {
    const rmProduct = bomItem.rawMaterial;
    
    const isDimensionBased = isDimensionBasedItem(bomItem);
    
    if (isDimensionBased) {
      const dimensionReq = calculateDimensionRequirements(bomItem, productionQuantity);
      
      if (dimensionReq.isValid) {
        const itemSummary = {
          bomItemId: bomItem.id,
          productId: rmProduct.id,
          productName: rmProduct.name,
          dimensions: dimensionReq.dimensions,
          areaPerUnit: dimensionReq.areaPerUnit,
          totalAreaRequired: dimensionReq.totalAreaRequired,
          unit: dimensionReq.unit
        };
        
        summary.dimensionBasedItems.push(itemSummary);
        summary.totalAreaRequired += dimensionReq.totalAreaRequired;
        
        // Track by unit
        if (!summary.unitBreakdown[dimensionReq.unit]) {
          summary.unitBreakdown[dimensionReq.unit] = 0;
        }
        summary.unitBreakdown[dimensionReq.unit] += dimensionReq.totalAreaRequired;
      }
    } else {
      const quantityRequired = bomItem.quantity_per_unit * productionQuantity;
      
      summary.quantityBasedItems.push({
        bomItemId: bomItem.id,
        productId: rmProduct.id,
        productName: rmProduct.name,
        quantityPerUnit: bomItem.quantity_per_unit,
        totalQuantityRequired: quantityRequired
      });
    }
  }

  return {
    isValid: true,
    summary: summary
  };
}

/**
 * Allocate materials for BOM production
 * @param {Object} bom - BOM with items
 * @param {number} productionQuantity - Quantity to produce
 * @param {string} allocationStrategy - Allocation strategy (BEST_FIT, FIRST_FIT, LARGEST_FIRST)
 * @returns {Promise<Object>} Allocation result
 */
async function allocateMaterialsForBOM(bom, productionQuantity, allocationStrategy = 'BEST_FIT') {
  if (!bom || !bom.items) {
    return {
      isValid: false,
      error: 'BOM or BOM items not provided'
    };
  }

  if (productionQuantity <= 0) {
    return {
      isValid: false,
      error: 'Production quantity must be positive'
    };
  }

  const allocationResults = [];
  let overallSuccess = true;

  for (const bomItem of bom.items) {
    const rmProduct = bomItem.rawMaterial;
    
    if (!rmProduct) {
      return {
        isValid: false,
        error: `Raw material product not found for BOM item ${bomItem.id}`
      };
    }

    let itemResult = {
      bomItemId: bomItem.id,
      productId: rmProduct.id,
      productName: rmProduct.name,
      useDimensions: isDimensionBasedItem(bomItem)
    };

    const isDimensionBased = isDimensionBasedItem(bomItem);
    
    if (isDimensionBased) {
      // Dimension-based allocation
      const dimensionReq = calculateDimensionRequirements(bomItem, productionQuantity);
      
      if (!dimensionReq.isValid) {
        itemResult.success = false;
        itemResult.error = dimensionReq.error;
        overallSuccess = false;
      } else {
        try {
          // Fetch available inventory pieces for this product
          const { Op } = require('sequelize');
          const availablePieces = await RMInventoryPiece.findAll({
            where: {
              product_id: rmProduct.id,
              status: { [Op.in]: ['FULL', 'USABLE', 'WASTE'] }
            },
            order: [['status', 'DESC'], ['created_at', 'ASC']]
          });

          // Create a BOM item-like object for allocation
          const bomItemForAllocation = {
            rm_product_id: bomItem.rm_product_id,
            required_length: dimensionReq.dimensions.length,
            required_width: dimensionReq.dimensions.width,
            dimension_unit: dimensionReq.dimensions.unit,
            isDimensionBased: () => true
          };

          // Perform actual material allocation
          const allocationResult = materialAllocationService.allocateMaterialForBOMItem(
            availablePieces,
            bomItemForAllocation,
            productionQuantity,
            { strategy: allocationStrategy }
          );

          if (!allocationResult.isValid) {
            itemResult.success = false;
            itemResult.error = allocationResult.error || 'Allocation failed';
            overallSuccess = false;
          } else {
            itemResult.success = allocationResult.isFullyAllocated;
            itemResult.totalAreaRequired = dimensionReq.totalAreaRequired;
            itemResult.allocatedPieces = allocationResult.allocations || [];
            itemResult.totalAllocatedUnits = allocationResult.totalAllocatedUnits || 0;
            itemResult.remainingQuantity = allocationResult.remainingQuantity || productionQuantity;
            itemResult.piecesUsed = allocationResult.piecesUsed || 0;
            itemResult.wasteGenerated = allocationResult.totalWasteArea || 0;
            itemResult.allocationSummary = {
              totalPiecesUsed: allocationResult.piecesUsed || 0,
              totalWasteArea: allocationResult.totalWasteArea || 0,
              utilizationEfficiency: allocationResult.utilizationEfficiency || 0
            };
            
            if (!allocationResult.isFullyAllocated) {
              itemResult.error = `Insufficient material: ${allocationResult.remainingQuantity} units remaining`;
              overallSuccess = false;
            }
          }
        } catch (error) {
          itemResult.success = false;
          itemResult.error = `Material allocation failed: ${error.message}`;
          overallSuccess = false;
          logger.error('Material allocation error', { error: error.message, stack: error.stack, bomItemId: bomItem.id });
        }
      }
    } else {
      // Quantity-based allocation (placeholder)
      const totalQuantityRequired = bomItem.quantity_per_unit * productionQuantity;
      
      // For quantity-based items, we assume success (would integrate with existing inventory system)
      itemResult.success = true;
      itemResult.quantityRequired = totalQuantityRequired;
      itemResult.message = 'Quantity-based allocation assumed (integration with existing inventory system needed)';
    }

    allocationResults.push(itemResult);
  }

  return {
    isValid: true,
    overallSuccess: overallSuccess,
    productionQuantity: productionQuantity,
    bomId: bom.id,
    allocationStrategy: allocationStrategy,
    itemResults: allocationResults,
    summary: {
      totalItems: allocationResults.length,
      successfulAllocations: allocationResults.filter(r => r.success).length,
      failedAllocations: allocationResults.filter(r => !r.success).length,
      dimensionBasedItems: allocationResults.filter(r => r.useDimensions).length,
      quantityBasedItems: allocationResults.filter(r => !r.useDimensions).length
    }
  };
}

/**
 * Scale BOM requirements for different production quantities
 * @param {Object} bom - BOM with items
 * @param {number} baseQuantity - Base production quantity
 * @param {number} targetQuantity - Target production quantity
 * @returns {Object} Scaled requirements
 */
function scaleBOMRequirements(bom, baseQuantity, targetQuantity) {
  if (!bom || !bom.items) {
    return {
      isValid: false,
      error: 'BOM or BOM items not provided'
    };
  }

  if (baseQuantity <= 0 || targetQuantity <= 0) {
    return {
      isValid: false,
      error: 'Base and target quantities must be positive'
    };
  }

  const scalingFactor = targetQuantity / baseQuantity;
  const scaledItems = [];

  for (const bomItem of bom.items) {
    const isDimensionBased = isDimensionBasedItem(bomItem);
    
    const scaledItem = {
      bomItemId: bomItem.id,
      productId: bomItem.rm_product_id,
      productName: bomItem.rawMaterial ? bomItem.rawMaterial.name : 'Unknown',
      useDimensions: isDimensionBased,
      scalingFactor: scalingFactor
    };

    if (isDimensionBased) {
      // For dimension-based items, scale the area requirement
      const baseAreaPerUnit = bomItem.required_length * bomItem.required_width;
      const baseTotalArea = baseAreaPerUnit * baseQuantity;
      const targetTotalArea = baseAreaPerUnit * targetQuantity;

      scaledItem.dimensions = {
        length: bomItem.required_length,
        width: bomItem.required_width,
        unit: bomItem.dimension_unit
      };
      scaledItem.baseAreaPerUnit = baseAreaPerUnit;
      scaledItem.baseTotalArea = baseTotalArea;
      scaledItem.targetTotalArea = targetTotalArea;
      scaledItem.areaScalingFactor = targetTotalArea / baseTotalArea;
    } else {
      // For quantity-based items, scale the quantity
      const baseQuantityRequired = bomItem.quantity_per_unit * baseQuantity;
      const targetQuantityRequired = bomItem.quantity_per_unit * targetQuantity;

      scaledItem.baseQuantityRequired = baseQuantityRequired;
      scaledItem.targetQuantityRequired = targetQuantityRequired;
      scaledItem.quantityPerUnit = bomItem.quantity_per_unit;
    }

    scaledItems.push(scaledItem);
  }

  return {
    isValid: true,
    bomId: bom.id,
    baseQuantity: baseQuantity,
    targetQuantity: targetQuantity,
    scalingFactor: scalingFactor,
    scaledItems: scaledItems
  };
}

module.exports = {
  validateBOMItemDimensions,
  calculateDimensionRequirements,
  validateBOMFeasibility,
  convertBOMDimensions,
  validateMixedBOM,
  generateDimensionRequirementsSummary,
  allocateMaterialsForBOM,
  scaleBOMRequirements
};