/**
 * Integration Test for BOM-Material Allocation Integration
 * Tests BOM integration with material allocation service
 * Validates: Requirements 2.4, 2.5
 */

const bomDimensionService = require('./services/bomDimensionService');
const materialAllocationService = require('./services/materialAllocationService');

/**
 * Test BOM feasibility checking with material allocation
 */
async function testBOMFeasibilityWithAllocation() {
  console.log('🧪 Testing BOM Feasibility with Material Allocation');
  
  let passCount = 0;
  let failCount = 0;
  const iterations = 20;
  
  for (let i = 0; i < iterations; i++) {
    try {
      // Create mock inventory pieces
      const inventoryPieces = [
        {
          id: 1,
          product_id: 1,
          length: 300,
          width: 200,
          unit: 'cm',
          status: 'FULL'
        },
        {
          id: 2,
          product_id: 1,
          length: 250,
          width: 150,
          unit: 'cm',
          status: 'FULL'
        },
        {
          id: 3,
          product_id: 1,
          length: 100,
          width: 80,
          unit: 'cm',
          status: 'USABLE'
        }
      ];
      
      // Create mock BOM
      const bom = {
        id: i + 1,
        items: [
          {
            id: 1,
            use_dimensions: true,
            required_length: 150,
            required_width: 100,
            dimension_unit: 'cm',
            rawMaterial: {
              id: 1,
              name: 'Cotton Fabric',
              product_type: 'RM',
              track_by_dimensions: true,
              unit_of_measure: 'cm'
            }
          }
        ]
      };
      
      const productionQuantity = Math.floor(Math.random() * 5) + 1; // 1-5 units
      
      // Mock the material allocation service
      const originalCheckFeasibility = materialAllocationService.checkAllocationFeasibility;
      materialAllocationService.checkAllocationFeasibility = async (productId, requirements) => {
        // Simple feasibility check based on available pieces
        const requirement = requirements[0];
        let availableArea = 0;
        let feasiblePieces = 0;
        
        for (const piece of inventoryPieces) {
          if (piece.product_id === productId &&
              piece.length >= requirement.length &&
              piece.width >= requirement.width) {
            availableArea += piece.length * piece.width;
            feasiblePieces++;
          }
        }
        
        const requiredArea = requirement.length * requirement.width * requirement.quantity;
        const feasible = availableArea >= requiredArea && feasiblePieces >= requirement.quantity;
        
        return {
          feasible: feasible,
          availableArea: availableArea,
          requiredArea: requiredArea,
          shortfall: feasible ? null : requiredArea - availableArea,
          details: {
            availablePieces: feasiblePieces,
            requiredPieces: requirement.quantity
          }
        };
      };
      
      // Test BOM feasibility
      const feasibilityResult = await bomDimensionService.validateBOMFeasibility(bom, productionQuantity);
      
      // Restore original function
      materialAllocationService.checkAllocationFeasibility = originalCheckFeasibility;
      
      if (!feasibilityResult.isValid) {
        throw new Error(`Feasibility validation failed: ${feasibilityResult.error}`);
      }
      
      // Verify feasibility result structure
      if (feasibilityResult.bomId !== bom.id) {
        throw new Error(`BOM ID mismatch: expected ${bom.id}, got ${feasibilityResult.bomId}`);
      }
      
      if (feasibilityResult.productionQuantity !== productionQuantity) {
        throw new Error(`Production quantity mismatch: expected ${productionQuantity}, got ${feasibilityResult.productionQuantity}`);
      }
      
      if (!Array.isArray(feasibilityResult.itemResults)) {
        throw new Error('Feasibility result should include itemResults array');
      }
      
      if (feasibilityResult.itemResults.length !== bom.items.length) {
        throw new Error(`Expected ${bom.items.length} item results, got ${feasibilityResult.itemResults.length}`);
      }
      
      // Verify item result structure
      const itemResult = feasibilityResult.itemResults[0];
      
      if (itemResult.bomItemId !== bom.items[0].id) {
        throw new Error('BOM item ID mismatch in result');
      }
      
      if (itemResult.productId !== bom.items[0].rawMaterial.id) {
        throw new Error('Product ID mismatch in result');
      }
      
      if (typeof itemResult.feasible !== 'boolean') {
        throw new Error('Feasible flag should be boolean');
      }
      
      if (itemResult.useDimensions !== true) {
        throw new Error('Use dimensions flag should be true for dimension-based item');
      }
      
      // Verify allocation details are included
      if (itemResult.feasible && typeof itemResult.totalAreaRequired !== 'number') {
        throw new Error('Total area required should be included for feasible dimension-based items');
      }
      
      if (itemResult.feasible && typeof itemResult.availableArea !== 'number') {
        throw new Error('Available area should be included for feasible dimension-based items');
      }
      
      passCount++;
      
    } catch (error) {
      console.log(`❌ Iteration ${i + 1}: ${error.message}`);
      failCount++;
    }
  }
  
  const successRate = (passCount / iterations) * 100;
  console.log(`✅ BOM Feasibility with Allocation: ${passCount}/${iterations} passed (${successRate.toFixed(1)}%)`);
  
  return successRate >= 95;
}

/**
 * Test BOM material allocation
 */
async function testBOMMaterialAllocation() {
  console.log('🧪 Testing BOM Material Allocation');
  
  let passCount = 0;
  let failCount = 0;
  const iterations = 15;
  
  for (let i = 0; i < iterations; i++) {
    try {
      // Create mock BOM
      const bom = {
        id: i + 1,
        items: [
          {
            id: 1,
            use_dimensions: true,
            required_length: 100,
            required_width: 80,
            dimension_unit: 'cm',
            rawMaterial: {
              id: 1,
              name: 'Cotton Fabric',
              product_type: 'RM',
              track_by_dimensions: true,
              unit_of_measure: 'cm'
            }
          },
          {
            id: 2,
            use_dimensions: false,
            quantity_per_unit: 5,
            rawMaterial: {
              id: 2,
              name: 'Thread',
              product_type: 'RM',
              track_by_dimensions: true,
              unit_of_measure: 'cm'
            }
          }
        ]
      };
      
      const productionQuantity = Math.floor(Math.random() * 3) + 1; // 1-3 units
      const allocationStrategy = ['BEST_FIT', 'FIRST_FIT', 'LARGEST_FIRST'][Math.floor(Math.random() * 3)];
      
      // Mock the material allocation service
      const originalAllocateMaterials = materialAllocationService.allocateMaterials;
      materialAllocationService.allocateMaterials = async (productId, requirements, strategy) => {
        // Simple allocation simulation
        const requirement = requirements[0];
        
        return {
          success: true,
          allocatedPieces: [
            {
              pieceId: 1,
              originalLength: 200,
              originalWidth: 150,
              allocatedLength: requirement.length,
              allocatedWidth: requirement.width,
              remainingLength: 200 - requirement.length,
              remainingWidth: 150 - requirement.width,
              unit: requirement.unit
            }
          ],
          wasteGenerated: [
            {
              pieceId: 1,
              length: 200 - requirement.length,
              width: 150 - requirement.width,
              unit: requirement.unit,
              status: 'USABLE'
            }
          ],
          summary: {
            totalPiecesAllocated: 1,
            totalAreaAllocated: requirement.length * requirement.width * requirement.quantity,
            totalWasteGenerated: (200 - requirement.length) * (150 - requirement.width),
            strategy: strategy
          }
        };
      };
      
      // Test BOM material allocation
      const allocationResult = await bomDimensionService.allocateMaterialsForBOM(bom, productionQuantity, allocationStrategy);
      
      // Restore original function
      materialAllocationService.allocateMaterials = originalAllocateMaterials;
      
      if (!allocationResult.isValid) {
        throw new Error(`Allocation failed: ${allocationResult.error}`);
      }
      
      // Verify allocation result structure
      if (allocationResult.bomId !== bom.id) {
        throw new Error(`BOM ID mismatch: expected ${bom.id}, got ${allocationResult.bomId}`);
      }
      
      if (allocationResult.productionQuantity !== productionQuantity) {
        throw new Error(`Production quantity mismatch: expected ${productionQuantity}, got ${allocationResult.productionQuantity}`);
      }
      
      if (allocationResult.allocationStrategy !== allocationStrategy) {
        throw new Error(`Strategy mismatch: expected ${allocationStrategy}, got ${allocationResult.allocationStrategy}`);
      }
      
      if (!Array.isArray(allocationResult.itemResults)) {
        throw new Error('Allocation result should include itemResults array');
      }
      
      if (allocationResult.itemResults.length !== bom.items.length) {
        throw new Error(`Expected ${bom.items.length} item results, got ${allocationResult.itemResults.length}`);
      }
      
      // Verify summary
      if (!allocationResult.summary) {
        throw new Error('Allocation result should include summary');
      }
      
      const summary = allocationResult.summary;
      if (summary.totalItems !== bom.items.length) {
        throw new Error(`Summary total items mismatch: expected ${bom.items.length}, got ${summary.totalItems}`);
      }
      
      if (summary.dimensionBasedItems !== 1) {
        throw new Error(`Expected 1 dimension-based item, got ${summary.dimensionBasedItems}`);
      }
      
      if (summary.quantityBasedItems !== 1) {
        throw new Error(`Expected 1 quantity-based item, got ${summary.quantityBasedItems}`);
      }
      
      // Verify dimension-based item allocation
      const dimensionItem = allocationResult.itemResults.find(r => r.useDimensions);
      if (!dimensionItem) {
        throw new Error('Should have dimension-based item result');
      }
      
      if (!dimensionItem.success) {
        throw new Error('Dimension-based allocation should succeed in mock');
      }
      
      if (!Array.isArray(dimensionItem.allocatedPieces)) {
        throw new Error('Dimension-based item should have allocated pieces');
      }
      
      if (!Array.isArray(dimensionItem.wasteGenerated)) {
        throw new Error('Dimension-based item should have waste generated');
      }
      
      // Verify quantity-based item handling
      const quantityItem = allocationResult.itemResults.find(r => !r.useDimensions);
      if (!quantityItem) {
        throw new Error('Should have quantity-based item result');
      }
      
      if (!quantityItem.success) {
        throw new Error('Quantity-based allocation should succeed (assumed)');
      }
      
      if (typeof quantityItem.quantityRequired !== 'number') {
        throw new Error('Quantity-based item should have quantity required');
      }
      
      passCount++;
      
    } catch (error) {
      console.log(`❌ Iteration ${i + 1}: ${error.message}`);
      failCount++;
    }
  }
  
  const successRate = (passCount / iterations) * 100;
  console.log(`✅ BOM Material Allocation: ${passCount}/${iterations} passed (${successRate.toFixed(1)}%)`);
  
  return successRate >= 95;
}

/**
 * Test BOM requirements scaling
 */
function testBOMRequirementsScaling() {
  console.log('🧪 Testing BOM Requirements Scaling');
  
  let passCount = 0;
  let failCount = 0;
  const iterations = 20;
  
  for (let i = 0; i < iterations; i++) {
    try {
      // Create mock BOM
      const bom = {
        id: i + 1,
        items: [
          {
            id: 1,
            rm_product_id: 1,
            use_dimensions: true,
            required_length: 100,
            required_width: 80,
            dimension_unit: 'cm',
            rawMaterial: {
              id: 1,
              name: 'Cotton Fabric'
            }
          },
          {
            id: 2,
            rm_product_id: 2,
            use_dimensions: false,
            quantity_per_unit: 5,
            rawMaterial: {
              id: 2,
              name: 'Thread'
            }
          }
        ]
      };
      
      const baseQuantity = Math.floor(Math.random() * 10) + 5;  // 5-14 units
      const targetQuantity = Math.floor(Math.random() * 20) + 10; // 10-29 units
      
      // Test requirements scaling
      const scalingResult = bomDimensionService.scaleBOMRequirements(bom, baseQuantity, targetQuantity);
      
      if (!scalingResult.isValid) {
        throw new Error(`Scaling failed: ${scalingResult.error}`);
      }
      
      // Verify scaling result structure
      if (scalingResult.bomId !== bom.id) {
        throw new Error(`BOM ID mismatch: expected ${bom.id}, got ${scalingResult.bomId}`);
      }
      
      if (scalingResult.baseQuantity !== baseQuantity) {
        throw new Error(`Base quantity mismatch: expected ${baseQuantity}, got ${scalingResult.baseQuantity}`);
      }
      
      if (scalingResult.targetQuantity !== targetQuantity) {
        throw new Error(`Target quantity mismatch: expected ${targetQuantity}, got ${scalingResult.targetQuantity}`);
      }
      
      const expectedScalingFactor = targetQuantity / baseQuantity;
      if (Math.abs(scalingResult.scalingFactor - expectedScalingFactor) > 0.0001) {
        throw new Error(`Scaling factor mismatch: expected ${expectedScalingFactor}, got ${scalingResult.scalingFactor}`);
      }
      
      if (!Array.isArray(scalingResult.scaledItems)) {
        throw new Error('Scaling result should include scaledItems array');
      }
      
      if (scalingResult.scaledItems.length !== bom.items.length) {
        throw new Error(`Expected ${bom.items.length} scaled items, got ${scalingResult.scaledItems.length}`);
      }
      
      // Verify dimension-based item scaling
      const dimensionItem = scalingResult.scaledItems.find(item => item.useDimensions);
      if (!dimensionItem) {
        throw new Error('Should have dimension-based scaled item');
      }
      
      const expectedBaseArea = 100 * 80; // length * width
      const expectedBaseTotalArea = expectedBaseArea * baseQuantity;
      const expectedTargetTotalArea = expectedBaseArea * targetQuantity;
      
      if (Math.abs(dimensionItem.baseAreaPerUnit - expectedBaseArea) > 0.001) {
        throw new Error(`Base area per unit mismatch: expected ${expectedBaseArea}, got ${dimensionItem.baseAreaPerUnit}`);
      }
      
      if (Math.abs(dimensionItem.baseTotalArea - expectedBaseTotalArea) > 0.001) {
        throw new Error(`Base total area mismatch: expected ${expectedBaseTotalArea}, got ${dimensionItem.baseTotalArea}`);
      }
      
      if (Math.abs(dimensionItem.targetTotalArea - expectedTargetTotalArea) > 0.001) {
        throw new Error(`Target total area mismatch: expected ${expectedTargetTotalArea}, got ${dimensionItem.targetTotalArea}`);
      }
      
      // Verify quantity-based item scaling
      const quantityItem = scalingResult.scaledItems.find(item => !item.useDimensions);
      if (!quantityItem) {
        throw new Error('Should have quantity-based scaled item');
      }
      
      const expectedBaseQuantityRequired = 5 * baseQuantity; // quantity_per_unit * baseQuantity
      const expectedTargetQuantityRequired = 5 * targetQuantity; // quantity_per_unit * targetQuantity
      
      if (Math.abs(quantityItem.baseQuantityRequired - expectedBaseQuantityRequired) > 0.001) {
        throw new Error(`Base quantity required mismatch: expected ${expectedBaseQuantityRequired}, got ${quantityItem.baseQuantityRequired}`);
      }
      
      if (Math.abs(quantityItem.targetQuantityRequired - expectedTargetQuantityRequired) > 0.001) {
        throw new Error(`Target quantity required mismatch: expected ${expectedTargetQuantityRequired}, got ${quantityItem.targetQuantityRequired}`);
      }
      
      passCount++;
      
    } catch (error) {
      console.log(`❌ Iteration ${i + 1}: ${error.message}`);
      failCount++;
    }
  }
  
  const successRate = (passCount / iterations) * 100;
  console.log(`✅ BOM Requirements Scaling: ${passCount}/${iterations} passed (${successRate.toFixed(1)}%)`);
  
  return successRate >= 95;
}

// Run all tests
async function runAllTests() {
  console.log('🚀 Starting BOM-Material Allocation Integration Tests\n');
  
  const results = [
    await testBOMFeasibilityWithAllocation(),
    await testBOMMaterialAllocation(),
    testBOMRequirementsScaling()
  ];
  
  const allPassed = results.every(result => result === true);
  
  console.log('\n📊 Final Results:');
  console.log(`BOM-Material Allocation Integration: ${allPassed ? '✅ PASSED' : '❌ FAILED'}`);
  
  if (allPassed) {
    console.log('\n🎉 All BOM-material allocation integration tests passed!');
    console.log('✅ BOM feasibility with material allocation - VALIDATED');
    console.log('✅ BOM material allocation integration - VALIDATED');
    console.log('✅ BOM requirements scaling - VALIDATED');
    console.log('✅ Requirements 2.4, 2.5 - SATISFIED');
  } else {
    console.log('\n💥 Some BOM-material allocation integration tests failed!');
    process.exit(1);
  }
}

// Run tests if this file is executed directly
if (require.main === module) {
  runAllTests();
}

module.exports = {
  testBOMFeasibilityWithAllocation,
  testBOMMaterialAllocation,
  testBOMRequirementsScaling,
  runAllTests
};