/**
 * Integration Tests for GRN Processing with Dimension-Based Inventory
 * Tests complete GRN flow with dimension-based RM products
 * Validates: Requirements 1.1, 1.2, 1.3
 */

const grnProcessingService = require('./services/grnProcessingService');
const dimensionValidationService = require('./services/dimensionValidationService');

/**
 * Mock database transaction for testing
 */
const mockTransaction = {
  commit: async () => {},
  rollback: async () => {}
};

/**
 * Mock RMInventoryPiece model for testing
 */
const mockRMInventoryPiece = {
  create: async (data, options) => {
    return {
      id: Math.floor(Math.random() * 10000) + 1,
      ...data,
      created_at: new Date(),
      updated_at: new Date()
    };
  }
};

/**
 * Test complete GRN flow with dimension-based RM products
 */
function testCompleteGRNFlow() {
  console.log('🧪 Testing Complete GRN Flow with Dimension-Based RM');
  
  let passCount = 0;
  let failCount = 0;
  const iterations = 50;
  
  for (let i = 0; i < iterations; i++) {
    try {
      // Create mock GRN data
      const grnData = {
        id: i + 1,
        vendor_id: Math.floor(Math.random() * 100) + 1,
        received_at: new Date(),
        processed_at: null,
        items: []
      };
      
      // Add RM products with dimensions
      const numItems = Math.floor(Math.random() * 3) + 1; // 1-3 items
      
      for (let j = 0; j < numItems; j++) {
        const product = {
          id: j + 1,
          name: `Cotton Fabric ${j + 1}`,
          product_type: 'RM',
          track_by_dimensions: true,
          unit_of_measure: 'cm'
        };
        
        const grnItem = {
          id: j + 1,
          grn_id: grnData.id,
          product_id: product.id,
          variant_id: null,
          quantity: Math.floor(Math.random() * 5) + 1, // 1-5 pieces
          unit_cost: Math.random() * 100 + 10, // $10-$110
          piece_length: Math.random() * 400 + 100, // 100-500 cm
          piece_width: Math.random() * 200 + 50,   // 50-250 cm
          dimension_unit: 'cm',
          pieces_count: Math.floor(Math.random() * 5) + 1, // 1-5 pieces
          product: product
        };
        
        // Ensure quantity matches pieces_count for RM products
        grnItem.quantity = grnItem.pieces_count;
        
        grnData.items.push(grnItem);
      }
      
      // Test GRN item validation
      for (const item of grnData.items) {
        const validation = grnProcessingService.validateGRNItemDimensions(item, item.product);
        
        if (!validation.isValid) {
          throw new Error(`GRN item validation failed: ${validation.error}`);
        }
        
        if (!validation.requiresDimensions) {
          throw new Error('RM product should require dimensions');
        }
        
        // Verify dimension validation
        if (!validation.dimensions) {
          throw new Error('Validated RM item should have dimensions');
        }
        
        if (validation.piecesCount !== item.pieces_count) {
          throw new Error(`Pieces count mismatch: expected ${item.pieces_count}, got ${validation.piecesCount}`);
        }
      }
      
      // Test dimension validation for each item
      for (const item of grnData.items) {
        const dimensionValidation = dimensionValidationService.validateDimensions(
          item.piece_length,
          item.piece_width,
          item.dimension_unit
        );
        
        if (!dimensionValidation.isValid) {
          throw new Error(`Dimension validation failed: ${dimensionValidation.errors.join(', ')}`);
        }
        
        // Test area calculation
        const areaResult = dimensionValidationService.calculateArea(
          item.piece_length,
          item.piece_width,
          item.dimension_unit
        );
        
        if (!areaResult.isValid) {
          throw new Error(`Area calculation failed: ${areaResult.errors.join(', ')}`);
        }
        
        const expectedArea = item.piece_length * item.piece_width;
        if (Math.abs(areaResult.area - expectedArea) > 0.001) {
          throw new Error(`Area calculation mismatch: expected ${expectedArea}, got ${areaResult.area}`);
        }
      }
      
      // Test unit conversion scenarios
      const testItem = grnData.items[0];
      if (testItem) {
        // Test conversion from cm to m
        const conversionResult = dimensionValidationService.convertDimensions(
          {
            length: testItem.piece_length,
            width: testItem.piece_width,
            unit: 'cm'
          },
          'm'
        );
        
        if (!conversionResult.isValid) {
          throw new Error(`Unit conversion failed: ${conversionResult.error}`);
        }
        
        // Verify conversion accuracy
        const expectedLengthM = testItem.piece_length / 100; // cm to m
        const expectedWidthM = testItem.piece_width / 100;   // cm to m
        
        const lengthDiff = Math.abs(conversionResult.dimensions.length - expectedLengthM);
        const widthDiff = Math.abs(conversionResult.dimensions.width - expectedWidthM);
        
        if (lengthDiff > 0.0001 || widthDiff > 0.0001) {
          throw new Error(`Conversion accuracy error: length diff ${lengthDiff}, width diff ${widthDiff}`);
        }
      }
      
      passCount++;
      
    } catch (error) {
      console.log(`❌ Iteration ${i + 1}: ${error.message}`);
      failCount++;
    }
  }
  
  const successRate = (passCount / iterations) * 100;
  console.log(`✅ Complete GRN Flow: ${passCount}/${iterations} passed (${successRate.toFixed(1)}%)`);
  
  return successRate >= 95;
}

/**
 * Test multiple dimension scenarios in GRN processing
 */
function testMultipleDimensionScenarios() {
  console.log('🧪 Testing Multiple Dimension Scenarios in GRN');
  
  let passCount = 0;
  let failCount = 0;
  const iterations = 30;
  
  for (let i = 0; i < iterations; i++) {
    try {
      const product = {
        id: 1,
        name: 'Multi-Dimension Cotton Fabric',
        product_type: 'RM',
        track_by_dimensions: true,
        unit_of_measure: 'cm'
      };
      
      // Create multiple dimension sets for the same product
      const dimensionSets = [
        { length: 200, width: 150, unit: 'cm', quantity: 2 },
        { length: 300, width: 200, unit: 'cm', quantity: 1 },
        { length: 150, width: 100, unit: 'cm', quantity: 3 }
      ];
      
      // Test validation of multiple dimension sets
      const validation = dimensionValidationService.validateMultipleDimensionSets(
        dimensionSets,
        product.unit_of_measure
      );
      
      if (!validation.isValid) {
        throw new Error(`Multiple dimension validation failed: ${validation.error}`);
      }
      
      // Verify each dimension set is processed independently
      let totalExpectedPieces = 0;
      let totalExpectedArea = 0;
      
      for (const dimSet of validation.dimensionSets) {
        totalExpectedPieces += dimSet.quantity;
        totalExpectedArea += dimSet.totalArea;
        
        // Verify independence - each set should maintain its properties
        if (dimSet.area !== dimSet.normalizedDimensions.length * dimSet.normalizedDimensions.width) {
          throw new Error(`Area calculation error for dimension set ${dimSet.index}`);
        }
        
        if (dimSet.normalizedDimensions.unit !== product.unit_of_measure) {
          throw new Error(`Unit conversion error for dimension set ${dimSet.index}`);
        }
      }
      
      // Verify totals
      if (Math.abs(validation.totalArea - totalExpectedArea) > 0.001) {
        throw new Error(`Total area mismatch: expected ${totalExpectedArea}, got ${validation.totalArea}`);
      }
      
      // Test that processing one set doesn't affect others
      const originalSets = JSON.parse(JSON.stringify(validation.dimensionSets));
      
      // Simulate processing each set
      for (let j = 0; j < validation.dimensionSets.length; j++) {
        const currentSet = validation.dimensionSets[j];
        const originalSet = originalSets[j];
        
        // Verify no cross-contamination
        if (currentSet.area !== originalSet.area ||
            currentSet.quantity !== originalSet.quantity ||
            currentSet.normalizedDimensions.length !== originalSet.normalizedDimensions.length ||
            currentSet.normalizedDimensions.width !== originalSet.normalizedDimensions.width) {
          throw new Error(`Dimension set ${j} was affected by processing other sets`);
        }
      }
      
      passCount++;
      
    } catch (error) {
      console.log(`❌ Iteration ${i + 1}: ${error.message}`);
      failCount++;
    }
  }
  
  const successRate = (passCount / iterations) * 100;
  console.log(`✅ Multiple Dimension Scenarios: ${passCount}/${iterations} passed (${successRate.toFixed(1)}%)`);
  
  return successRate >= 95;
}

/**
 * Test GRN validation edge cases
 */
function testGRNValidationEdgeCases() {
  console.log('🧪 Testing GRN Validation Edge Cases');
  
  const product = {
    id: 1,
    name: 'Test Fabric',
    product_type: 'RM',
    track_by_dimensions: true,
    unit_of_measure: 'cm'
  };
  
  // Test 1: Missing dimensions for RM product
  try {
    const invalidItem = {
      id: 1,
      product_id: 1,
      quantity: 2,
      unit_cost: 50,
      pieces_count: 2,
      // Missing piece_length, piece_width, dimension_unit
      product: product
    };
    
    const validation = grnProcessingService.validateGRNItemDimensions(invalidItem, product);
    if (validation.isValid) {
      console.log('❌ Missing dimensions should be invalid');
      return false;
    }
  } catch (error) {
    // Expected to fail
  }
  
  // Test 2: Quantity mismatch with pieces_count
  try {
    const mismatchItem = {
      id: 1,
      product_id: 1,
      quantity: 3, // Different from pieces_count
      unit_cost: 50,
      piece_length: 100,
      piece_width: 200,
      dimension_unit: 'cm',
      pieces_count: 2, // Different from quantity
      product: product
    };
    
    const validation = grnProcessingService.validateGRNItemDimensions(mismatchItem, product);
    if (validation.isValid) {
      // This should be valid at the item level, but would fail at GRN creation level
    }
  } catch (error) {
    // May fail depending on validation logic
  }
  
  // Test 3: Zero or negative dimensions
  try {
    const negativeItem = {
      id: 1,
      product_id: 1,
      quantity: 1,
      unit_cost: 50,
      piece_length: -100, // Negative
      piece_width: 200,
      dimension_unit: 'cm',
      pieces_count: 1,
      product: product
    };
    
    const validation = grnProcessingService.validateGRNItemDimensions(negativeItem, product);
    if (validation.isValid) {
      console.log('❌ Negative dimensions should be invalid');
      return false;
    }
  } catch (error) {
    // Expected to fail
  }
  
  // Test 4: Invalid unit
  try {
    const invalidUnitItem = {
      id: 1,
      product_id: 1,
      quantity: 1,
      unit_cost: 50,
      piece_length: 100,
      piece_width: 200,
      dimension_unit: 'invalid', // Invalid unit
      pieces_count: 1,
      product: product
    };
    
    const validation = grnProcessingService.validateGRNItemDimensions(invalidUnitItem, product);
    if (validation.isValid) {
      console.log('❌ Invalid unit should be invalid');
      return false;
    }
  } catch (error) {
    // Expected to fail
  }
  
  // Test 5: Non-RM product (should not require dimensions)
  try {
    const fgProduct = {
      id: 2,
      name: 'Finished Shirt',
      product_type: 'FG',
      track_by_dimensions: false,
      unit_of_measure: null
    };
    
    const fgItem = {
      id: 1,
      product_id: 2,
      quantity: 10,
      unit_cost: 25,
      pieces_count: 1,
      // No dimension fields
      product: fgProduct
    };
    
    const validation = grnProcessingService.validateGRNItemDimensions(fgItem, fgProduct);
    if (!validation.isValid) {
      console.log('❌ Non-RM product should be valid without dimensions');
      return false;
    }
    
    if (validation.requiresDimensions) {
      console.log('❌ Non-RM product should not require dimensions');
      return false;
    }
  } catch (error) {
    console.log(`❌ Non-RM product test failed: ${error.message}`);
    return false;
  }
  
  console.log('✅ All GRN validation edge cases handled correctly');
  return true;
}

/**
 * Test unit conversion in GRN processing
 */
function testGRNUnitConversion() {
  console.log('🧪 Testing GRN Unit Conversion');
  
  let passCount = 0;
  let failCount = 0;
  const iterations = 20;
  
  for (let i = 0; i < iterations; i++) {
    try {
      // Product uses meters
      const product = {
        id: 1,
        name: 'Meter-Based Fabric',
        product_type: 'RM',
        track_by_dimensions: true,
        unit_of_measure: 'm'
      };
      
      // GRN item uses centimeters
      const grnItem = {
        id: 1,
        product_id: 1,
        quantity: 2,
        unit_cost: 75,
        piece_length: 250, // cm
        piece_width: 180,  // cm
        dimension_unit: 'cm',
        pieces_count: 2,
        product: product
      };
      
      // Validate item
      const validation = grnProcessingService.validateGRNItemDimensions(grnItem, product);
      
      if (!validation.isValid) {
        throw new Error(`Validation failed: ${validation.error}`);
      }
      
      // Test unit conversion
      const conversionResult = dimensionValidationService.convertDimensions(
        {
          length: grnItem.piece_length,
          width: grnItem.piece_width,
          unit: grnItem.dimension_unit
        },
        product.unit_of_measure
      );
      
      if (!conversionResult.isValid) {
        throw new Error(`Unit conversion failed: ${conversionResult.error}`);
      }
      
      // Verify conversion accuracy
      const expectedLengthM = grnItem.piece_length / 100; // cm to m
      const expectedWidthM = grnItem.piece_width / 100;   // cm to m
      
      const lengthDiff = Math.abs(conversionResult.dimensions.length - expectedLengthM);
      const widthDiff = Math.abs(conversionResult.dimensions.width - expectedWidthM);
      
      if (lengthDiff > 0.0001 || widthDiff > 0.0001) {
        throw new Error(`Conversion accuracy error: length diff ${lengthDiff}, width diff ${widthDiff}`);
      }
      
      // Verify unit is correct
      if (conversionResult.dimensions.unit !== product.unit_of_measure) {
        throw new Error(`Unit conversion failed: expected ${product.unit_of_measure}, got ${conversionResult.dimensions.unit}`);
      }
      
      passCount++;
      
    } catch (error) {
      console.log(`❌ Iteration ${i + 1}: ${error.message}`);
      failCount++;
    }
  }
  
  const successRate = (passCount / iterations) * 100;
  console.log(`✅ GRN Unit Conversion: ${passCount}/${iterations} passed (${successRate.toFixed(1)}%)`);
  
  return successRate >= 95;
}

// Run all tests
function runAllTests() {
  console.log('🚀 Starting GRN Integration Tests\n');
  
  const results = [
    testCompleteGRNFlow(),
    testMultipleDimensionScenarios(),
    testGRNValidationEdgeCases(),
    testGRNUnitConversion()
  ];
  
  const allPassed = results.every(result => result === true);
  
  console.log('\n📊 Final Results:');
  console.log(`GRN Integration Tests: ${allPassed ? '✅ PASSED' : '❌ FAILED'}`);
  
  if (allPassed) {
    console.log('\n🎉 All GRN integration tests passed!');
    console.log('✅ Complete GRN flow with dimension-based RM - VALIDATED');
    console.log('✅ Multiple dimension scenarios - VALIDATED');
    console.log('✅ Requirements 1.1, 1.2, 1.3 - SATISFIED');
  } else {
    console.log('\n💥 Some GRN integration tests failed!');
    process.exit(1);
  }
}

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

module.exports = {
  testCompleteGRNFlow,
  testMultipleDimensionScenarios,
  testGRNValidationEdgeCases,
  testGRNUnitConversion,
  runAllTests
};