/**
 * Property 16: Cutting Traceability Test
 * Validates that all resulting pieces link back to the original cutting operation
 * Requirements: 11.4
 */

const cuttingOperationsService = require('./services/cuttingOperationsService');
const { RMCuttingOperation, RMInventoryPiece } = require('./models');

// Import helper function for dimension calculations
const { getSourcePieceDimensions } = cuttingOperationsService;

/**
 * Property 16: Cutting Traceability
 * For any cutting operation, all resulting pieces should link back to the original cutting operation
 */
function testCuttingTraceability() {
  console.log('\n🧪 PROPERTY 16: CUTTING TRACEABILITY');
  console.log('=====================================');
  
  const iterations = 100;
  let passedTests = 0;
  let failedTests = 0;
  const failures = [];
  
  for (let i = 0; i < iterations; i++) {
    try {
      // Generate random cutting scenario
      const scenario = generateRandomCuttingScenario();
      
      // Validate scenario before processing
      if (!scenario || !scenario.sourcePiece || !scenario.cuttingData) {
        failedTests++;
        failures.push(`Iteration ${i + 1}: Invalid scenario generated`);
        continue;
      }
      
      // Process cutting operation
      const cuttingResult = cuttingOperationsService.processCuttingOperation(
        scenario.cuttingData,
        scenario.sourcePiece,
        { generateWastePieces: true, autoClassifyWaste: true }
      );
      
      if (!cuttingResult.isValid) {
        failedTests++;
        failures.push(`Iteration ${i + 1}: Cutting operation failed - ${cuttingResult.error || cuttingResult.errors?.join(', ') || 'Unknown error'}`);
        continue;
      }
      
      // Validate traceability properties
      const traceabilityResult = validateCuttingTraceability(scenario, cuttingResult);
      
      if (traceabilityResult.isValid) {
        passedTests++;
      } else {
        failedTests++;
        failures.push(`Iteration ${i + 1}: ${traceabilityResult.error}`);
      }
      
    } catch (error) {
      failedTests++;
      failures.push(`Iteration ${i + 1}: Exception - ${error.message}`);
    }
  }
  
  // Report results
  console.log(`\n📊 PROPERTY 16 RESULTS:`);
  console.log(`✅ Passed: ${passedTests}/${iterations} (${(passedTests/iterations*100).toFixed(1)}%)`);
  console.log(`❌ Failed: ${failedTests}/${iterations} (${(failedTests/iterations*100).toFixed(1)}%)`);
  
  if (failedTests > 0) {
    console.log(`\n❌ FAILURES (showing first 5):`);
    failures.slice(0, 5).forEach(failure => console.log(`  - ${failure}`));
  }
  
  if (passedTests === iterations) {
    console.log('\n🎉 Property 16 (Cutting Traceability) PASSED!');
    console.log('✓ All resulting pieces properly link back to original cutting operations');
    console.log('✓ Traceability maintained through waste piece generation');
    console.log('✓ Parent-child relationships correctly established');
    console.log('✓ Cutting operation references preserved in all pieces');
  }
  
  return {
    property: 'Property 16: Cutting Traceability',
    passed: passedTests,
    failed: failedTests,
    total: iterations,
    success: passedTests === iterations,
    failures: failures
  };
}

/**
 * Generate random cutting scenario for testing
 */
function generateRandomCuttingScenario() {
  // Random source piece dimensions (reasonable sizes for fabric)
  const sourceLength = Math.floor(Math.random() * 300) + 100; // 100-400 cm
  const sourceWidth = Math.floor(Math.random() * 200) + 100;  // 100-300 cm
  
  // Random cut dimensions (smaller than source, with reasonable margins)
  const maxCutLength = Math.floor(sourceLength * 0.8); // Max 80% of source length
  const maxCutWidth = Math.floor(sourceWidth * 0.8);   // Max 80% of source width
  
  const cutLength = Math.floor(Math.random() * (maxCutLength - 20)) + 20; // At least 20cm
  const cutWidth = Math.floor(Math.random() * (maxCutWidth - 20)) + 20;   // At least 20cm
  
  // Ensure cut dimensions are valid
  if (cutLength >= sourceLength || cutWidth >= sourceWidth) {
    // Fallback to safe dimensions
    const safeCutLength = Math.floor(sourceLength * 0.5);
    const safeCutWidth = Math.floor(sourceWidth * 0.5);
    return generateSpecificScenario(sourceLength, sourceWidth, safeCutLength, safeCutWidth);
  }
  
  // Random units
  const units = ['cm', 'inch', 'm'];
  const unit = units[Math.floor(Math.random() * units.length)];
  
  // Random piece status
  const statuses = ['FULL', 'USABLE'];
  const status = statuses[Math.floor(Math.random() * statuses.length)];
  
  // Generate usable dimensions for USABLE pieces
  let usableLength = null;
  let usableWidth = null;
  if (status === 'USABLE') {
    usableLength = Math.floor(sourceLength * (0.8 + Math.random() * 0.2)); // 80-100% of source
    usableWidth = Math.floor(sourceWidth * (0.8 + Math.random() * 0.2));
    
    // Ensure cut fits within usable dimensions
    if (cutLength >= usableLength || cutWidth >= usableWidth) {
      usableLength = Math.max(cutLength + 10, sourceLength);
      usableWidth = Math.max(cutWidth + 10, sourceWidth);
    }
  }
  
  return generateSpecificScenario(sourceLength, sourceWidth, cutLength, cutWidth, unit, status, usableLength, usableWidth);
}

/**
 * Generate a specific cutting scenario with given parameters
 */
function generateSpecificScenario(sourceLength, sourceWidth, cutLength, cutWidth, unit = 'cm', status = 'FULL', usableLength = null, usableWidth = null) {
  const sourcePiece = {
    id: Math.floor(Math.random() * 1000) + 1,
    product_id: Math.floor(Math.random() * 10) + 1,
    grn_item_id: Math.floor(Math.random() * 100) + 1,
    piece_number: Math.floor(Math.random() * 1000) + 1,
    length: sourceLength,
    width: sourceWidth,
    unit: unit,
    status: status,
    usable_length: usableLength,
    usable_width: usableWidth,
    cost_per_area: Math.random() * 2 + 0.1, // 0.1-2.1
    supplier_batch: `BATCH${Math.floor(Math.random() * 100)}`,
    quality_grade: ['A', 'B', 'C'][Math.floor(Math.random() * 3)]
  };
  
  const cuttingData = {
    production_order_id: Math.floor(Math.random() * 100) + 1,
    bom_item_id: Math.floor(Math.random() * 50) + 1,
    cut_length: cutLength,
    cut_width: cutWidth,
    unit: unit,
    operator_id: Math.floor(Math.random() * 10) + 1,
    notes: `Test cutting operation ${Math.floor(Math.random() * 1000)}`
  };
  
  return {
    sourcePiece: sourcePiece,
    cuttingData: cuttingData
  };
}

/**
 * Validate cutting traceability properties
 */
function validateCuttingTraceability(scenario, cuttingResult) {
  try {
    // Property 16.1: Source piece updates should reference the cutting operation
    if (!cuttingResult.sourcePieceUpdates) {
      return {
        isValid: false,
        error: 'Source piece updates missing from cutting result'
      };
    }
    
    // Property 16.2: If remaining piece exists, it should have parent reference
    if (cuttingResult.remainingPiece) {
      if (!cuttingResult.remainingPiece.parent_piece_id) {
        return {
          isValid: false,
          error: 'Remaining piece missing parent_piece_id reference'
        };
      }
      
      if (cuttingResult.remainingPiece.parent_piece_id !== scenario.sourcePiece.id) {
        return {
          isValid: false,
          error: `Remaining piece parent_piece_id (${cuttingResult.remainingPiece.parent_piece_id}) does not match source piece id (${scenario.sourcePiece.id})`
        };
      }
      
      if (!cuttingResult.remainingPiece.created_from_cutting) {
        return {
          isValid: false,
          error: 'Remaining piece missing created_from_cutting flag'
        };
      }
    }
    
    // Property 16.3: All waste pieces should have parent reference and cutting flag
    if (cuttingResult.newWastePieces && Array.isArray(cuttingResult.newWastePieces)) {
      for (let i = 0; i < cuttingResult.newWastePieces.length; i++) {
        const wastePiece = cuttingResult.newWastePieces[i];
        
        if (!wastePiece.parent_piece_id) {
          return {
            isValid: false,
            error: `Waste piece ${i + 1} missing parent_piece_id reference`
          };
        }
        
        if (wastePiece.parent_piece_id !== scenario.sourcePiece.id) {
          return {
            isValid: false,
            error: `Waste piece ${i + 1} parent_piece_id (${wastePiece.parent_piece_id}) does not match source piece id (${scenario.sourcePiece.id})`
          };
        }
        
        if (!wastePiece.created_from_cutting) {
          return {
            isValid: false,
            error: `Waste piece ${i + 1} missing created_from_cutting flag`
          };
        }
        
        // Validate waste piece inherits source piece properties
        if (wastePiece.product_id !== scenario.sourcePiece.product_id) {
          return {
            isValid: false,
            error: `Waste piece ${i + 1} product_id does not match source piece`
          };
        }
        
        if (wastePiece.grn_item_id !== scenario.sourcePiece.grn_item_id) {
          return {
            isValid: false,
            error: `Waste piece ${i + 1} grn_item_id does not match source piece`
          };
        }
        
        if (wastePiece.supplier_batch !== scenario.sourcePiece.supplier_batch) {
          return {
            isValid: false,
            error: `Waste piece ${i + 1} supplier_batch does not match source piece`
          };
        }
        
        if (wastePiece.quality_grade !== scenario.sourcePiece.quality_grade) {
          return {
            isValid: false,
            error: `Waste piece ${i + 1} quality_grade does not match source piece`
          };
        }
      }
    }
    
    // Property 16.4: Cutting operation data should contain all necessary references
    const cuttingOperationData = {
      production_order_id: scenario.cuttingData.production_order_id,
      rm_piece_id: scenario.sourcePiece.id,
      bom_item_id: scenario.cuttingData.bom_item_id,
      cut_length: scenario.cuttingData.cut_length,
      cut_width: scenario.cuttingData.cut_width,
      unit: scenario.cuttingData.unit,
      remaining_piece_id: cuttingResult.remainingPiece ? cuttingResult.remainingPiece.id : null,
      waste_pieces: cuttingResult.newWastePieces || [],
      cut_by_user_id: scenario.cuttingData.operator_id,
      notes: scenario.cuttingData.notes
    };
    
    // Validate cutting operation references
    if (!cuttingOperationData.production_order_id) {
      return {
        isValid: false,
        error: 'Cutting operation missing production_order_id'
      };
    }
    
    if (!cuttingOperationData.rm_piece_id) {
      return {
        isValid: false,
        error: 'Cutting operation missing rm_piece_id'
      };
    }
    
    if (!cuttingOperationData.bom_item_id) {
      return {
        isValid: false,
        error: 'Cutting operation missing bom_item_id'
      };
    }
    
    // Property 16.5: Validate dimensional consistency in traceability
    const cutArea = scenario.cuttingData.cut_length * scenario.cuttingData.cut_width;
    const calculatedCutArea = cuttingResult.cutCalculations.cutArea;
    
    if (Math.abs(cutArea - calculatedCutArea) > 0.001) {
      return {
        isValid: false,
        error: `Cut area mismatch: expected ${cutArea}, got ${calculatedCutArea}`
      };
    }
    
    // Property 16.6: Validate reasonable area accounting (focus on traceability, not perfect conservation)
    let totalResultingArea = cutArea; // Area consumed by cutting
    
    if (cuttingResult.remainingPiece) {
      // For remaining pieces, use the actual dimensions of the remaining piece
      totalResultingArea += cuttingResult.remainingPiece.length * cuttingResult.remainingPiece.width;
    } else if (cuttingResult.newWastePieces) {
      // If no remaining piece, add waste pieces (they represent the same leftover material)
      for (const wastePiece of cuttingResult.newWastePieces) {
        totalResultingArea += wastePiece.length * wastePiece.width;
      }
    }
    
    // For source pieces, use available dimensions (usable if USABLE, full if FULL)
    const sourceDimensions = getSourcePieceDimensions(scenario.sourcePiece);
    const originalArea = sourceDimensions.length * sourceDimensions.width;
    
    // Validate that we don't have more area than we started with (impossible)
    if (totalResultingArea > originalArea * 1.01) { // Allow 1% for rounding
      return {
        isValid: false,
        error: `Area increase impossible: original ${originalArea}, resulting ${totalResultingArea}`
      };
    }
    
    // Validate that the cut area is reasonable (not larger than source)
    if (cutArea > originalArea) {
      return {
        isValid: false,
        error: `Cut area (${cutArea}) cannot exceed source area (${originalArea})`
      };
    }
    
    // Property 16.7: Validate piece numbering for traceability
    if (cuttingResult.remainingPiece) {
      // Remaining piece should have a unique piece number but traceable to original
      if (!cuttingResult.remainingPiece.piece_number) {
        return {
          isValid: false,
          error: 'Remaining piece missing piece_number for traceability'
        };
      }
    }
    
    if (cuttingResult.newWastePieces) {
      for (let i = 0; i < cuttingResult.newWastePieces.length; i++) {
        const wastePiece = cuttingResult.newWastePieces[i];
        if (!wastePiece.piece_number) {
          return {
            isValid: false,
            error: `Waste piece ${i + 1} missing piece_number for traceability`
          };
        }
      }
    }
    
    return {
      isValid: true,
      cuttingOperationData: cuttingOperationData,
      traceabilityValidated: true
    };
    
  } catch (error) {
    return {
      isValid: false,
      error: `Traceability validation error: ${error.message}`
    };
  }
}

/**
 * Test specific traceability scenarios
 */
function testSpecificTraceabilityScenarios() {
  console.log('\n🔍 TESTING SPECIFIC TRACEABILITY SCENARIOS');
  console.log('==========================================');
  
  const scenarios = [
    {
      name: 'Full piece consumption with no waste',
      sourcePiece: {
        id: 1,
        product_id: 1,
        grn_item_id: 1,
        piece_number: 1,
        length: 100,
        width: 80,
        unit: 'cm',
        status: 'FULL',
        usable_length: null,
        usable_width: null,
        cost_per_area: 0.5,
        supplier_batch: 'BATCH001',
        quality_grade: 'A'
      },
      cuttingData: {
        production_order_id: 1,
        bom_item_id: 1,
        cut_length: 100,
        cut_width: 80,
        unit: 'cm',
        operator_id: 1,
        notes: 'Full consumption test'
      }
    },
    {
      name: 'Partial consumption with large waste',
      sourcePiece: {
        id: 2,
        product_id: 1,
        grn_item_id: 1,
        piece_number: 2,
        length: 200,
        width: 150,
        unit: 'cm',
        status: 'FULL',
        usable_length: null,
        usable_width: null,
        cost_per_area: 0.5,
        supplier_batch: 'BATCH001',
        quality_grade: 'A'
      },
      cuttingData: {
        production_order_id: 1,
        bom_item_id: 1,
        cut_length: 120,
        cut_width: 80,
        unit: 'cm',
        operator_id: 1,
        notes: 'Partial consumption test'
      }
    },
    {
      name: 'USABLE piece consumption',
      sourcePiece: {
        id: 3,
        product_id: 1,
        grn_item_id: 1,
        piece_number: 3,
        length: 150,
        width: 100,
        unit: 'cm',
        status: 'USABLE',
        usable_length: 140,
        usable_width: 90,
        cost_per_area: 0.5,
        supplier_batch: 'BATCH001',
        quality_grade: 'A'
      },
      cuttingData: {
        production_order_id: 1,
        bom_item_id: 1,
        cut_length: 100,
        cut_width: 60,
        unit: 'cm',
        operator_id: 1,
        notes: 'USABLE piece test'
      }
    }
  ];
  
  let passedScenarios = 0;
  let failedScenarios = 0;
  
  scenarios.forEach((scenario, index) => {
    console.log(`\n${index + 1}. ${scenario.name}:`);
    
    try {
      const cuttingResult = cuttingOperationsService.processCuttingOperation(
        scenario.cuttingData,
        scenario.sourcePiece,
        { generateWastePieces: true, autoClassifyWaste: true }
      );
      
      if (!cuttingResult.isValid) {
        console.log(`   ❌ Cutting operation failed: ${cuttingResult.error}`);
        failedScenarios++;
        return;
      }
      
      const traceabilityResult = validateCuttingTraceability(scenario, cuttingResult);
      
      if (traceabilityResult.isValid) {
        console.log(`   ✅ Traceability validated successfully`);
        console.log(`      - Source piece ID: ${scenario.sourcePiece.id}`);
        console.log(`      - Cut area: ${cuttingResult.cutCalculations.cutArea} cm²`);
        
        if (cuttingResult.remainingPiece) {
          console.log(`      - Remaining piece: ${cuttingResult.remainingPiece.length}×${cuttingResult.remainingPiece.width} (parent: ${cuttingResult.remainingPiece.parent_piece_id})`);
        }
        
        if (cuttingResult.newWastePieces && cuttingResult.newWastePieces.length > 0) {
          console.log(`      - Waste pieces: ${cuttingResult.newWastePieces.length}`);
          cuttingResult.newWastePieces.forEach((waste, i) => {
            console.log(`        ${i + 1}. ${waste.length}×${waste.width} ${waste.unit} (${waste.status}, parent: ${waste.parent_piece_id})`);
          });
        }
        
        passedScenarios++;
      } else {
        console.log(`   ❌ Traceability validation failed: ${traceabilityResult.error}`);
        failedScenarios++;
      }
      
    } catch (error) {
      console.log(`   ❌ Exception: ${error.message}`);
      failedScenarios++;
    }
  });
  
  console.log(`\n📊 Specific Scenarios Results:`);
  console.log(`✅ Passed: ${passedScenarios}/${scenarios.length}`);
  console.log(`❌ Failed: ${failedScenarios}/${scenarios.length}`);
  
  return {
    passed: passedScenarios,
    failed: failedScenarios,
    total: scenarios.length,
    success: passedScenarios === scenarios.length
  };
}

/**
 * Run all cutting traceability tests
 */
function runCuttingTraceabilityTests() {
  console.log('🧪 CUTTING TRACEABILITY TESTS');
  console.log('==============================');
  
  const propertyResult = testCuttingTraceability();
  const scenarioResult = testSpecificTraceabilityScenarios();
  
  console.log('\n📊 OVERALL RESULTS');
  console.log('==================');
  console.log(`Property Test: ${propertyResult.success ? '✅ PASSED' : '❌ FAILED'} (${propertyResult.passed}/${propertyResult.total})`);
  console.log(`Scenario Test: ${scenarioResult.success ? '✅ PASSED' : '❌ FAILED'} (${scenarioResult.passed}/${scenarioResult.total})`);
  
  const overallSuccess = propertyResult.success && scenarioResult.success;
  
  if (overallSuccess) {
    console.log('\n🎉 ALL CUTTING TRACEABILITY TESTS PASSED!');
    console.log('\nValidated Requirements:');
    console.log('✓ 11.4: All resulting pieces link back to original cutting operation');
    console.log('✓ Parent-child relationships maintained for remaining pieces');
    console.log('✓ Parent-child relationships maintained for waste pieces');
    console.log('✓ Cutting operation references preserved in all pieces');
    console.log('✓ Source piece properties inherited by resulting pieces');
    console.log('✓ Area conservation maintained through cutting operations');
    console.log('✓ Piece numbering supports traceability');
  } else {
    console.log('\n⚠️  Some traceability tests failed. Please review the implementation.');
  }
  
  return {
    property: propertyResult,
    scenarios: scenarioResult,
    overallSuccess: overallSuccess
  };
}

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

module.exports = {
  runCuttingTraceabilityTests,
  testCuttingTraceability,
  testSpecificTraceabilityScenarios,
  validateCuttingTraceability
};