/**
 * Cutting Operations Service
 * Handles cutting operation processing with waste generation and piece status updates
 */

const dimensionService = require('./dimensionValidationService');

/**
 * Waste piece status types
 */
const WASTE_STATUS = {
  WASTE: 'WASTE',   // Reusable waste piece
  SCRAP: 'SCRAP'    // Non-reusable scrap
};

/**
 * Minimum dimensions for waste pieces to be considered reusable
 */
const MIN_REUSABLE_DIMENSIONS = {
  'm': { length: 0.1, width: 0.1 },      // 10cm x 10cm minimum in meters
  'cm': { length: 10, width: 10 },       // 10cm x 10cm minimum in centimeters
  'inch': { length: 4, width: 4 }        // 4" x 4" minimum in inches
};

/**
 * Processes a cutting operation and generates waste pieces
 * @param {Object} cuttingData - Cutting operation data
 * @param {Object} sourcePiece - Source RM inventory piece being cut
 * @param {Object} options - Processing options
 * @returns {Object} Processing result
 */
function processCuttingOperation(cuttingData, sourcePiece, options = {}) {
  const {
    generateWastePieces: shouldGenerateWastePieces = true,
    autoClassifyWaste = true,
    minWasteThreshold = null
  } = options;

  // Validate cutting data
  const validation = validateCuttingData(cuttingData, sourcePiece);
  if (!validation.isValid) {
    return validation;
  }

  // Calculate cut area and remaining dimensions
  const cutCalculations = calculateCuttingResults(cuttingData, sourcePiece);
  if (!cutCalculations.isValid) {
    return cutCalculations;
  }

  // Generate waste pieces if requested
  let wastePieces = [];
  if (shouldGenerateWastePieces && cutCalculations.hasRemainingMaterial && !cutCalculations.remainingPiece) {
    // Only generate waste pieces if no remaining piece was created
    // (remaining piece and waste pieces represent the same leftover material)
    const wasteResult = generateWastePieces(
      cutCalculations.remainingDimensions,
      sourcePiece,
      { autoClassify: autoClassifyWaste, minThreshold: minWasteThreshold }
    );
    
    if (wasteResult.isValid) {
      wastePieces = wasteResult.wastePieces;
    }
  }

  // Create cutting operation record
  const cuttingOperation = {
    production_order_id: cuttingData.production_order_id,
    rm_piece_id: sourcePiece.id,
    bom_item_id: cuttingData.bom_item_id,
    cut_length: cuttingData.cut_length,
    cut_width: cuttingData.cut_width,
    unit: cuttingData.unit,
    cut_area: cutCalculations.cutArea,
    remaining_piece_id: cutCalculations.remainingPieceId,
    waste_pieces: wastePieces.length > 0 ? wastePieces : null,
    scrap_dimensions: cutCalculations.scrapDimensions,
    cut_at: new Date(),
    operator_id: cuttingData.operator_id || null,
    notes: cuttingData.notes || null
  };

  // Calculate piece status updates
  const statusUpdates = calculatePieceStatusUpdates(sourcePiece, cutCalculations, wastePieces);

  return {
    isValid: true,
    cuttingOperation: cuttingOperation,
    sourcePieceUpdates: statusUpdates.sourcePiece,
    newWastePieces: wastePieces,
    remainingPiece: cutCalculations.remainingPiece,
    cutCalculations: cutCalculations,
    wasteStatistics: {
      totalWastePieces: wastePieces.length,
      reusableWastePieces: wastePieces.filter(w => w.status === WASTE_STATUS.WASTE).length,
      scrapPieces: wastePieces.filter(w => w.status === WASTE_STATUS.SCRAP).length,
      totalWasteArea: wastePieces.reduce((sum, w) => sum + (w.length * w.width), 0),
      wasteUtilizationRatio: cutCalculations.cutArea / (sourcePiece.length * sourcePiece.width)
    }
  };
}

/**
 * Validates cutting operation data
 * @param {Object} cuttingData - Cutting operation data
 * @param {Object} sourcePiece - Source piece being cut
 * @returns {Object} Validation result
 */
function validateCuttingData(cuttingData, sourcePiece) {
  const errors = [];

  // Validate required fields
  if (!cuttingData.production_order_id) {
    errors.push('Production order ID is required');
  }
  if (!cuttingData.bom_item_id) {
    errors.push('BOM item ID is required');
  }
  if (!cuttingData.cut_length || !cuttingData.cut_width) {
    errors.push('Cut dimensions are required');
  }
  if (!cuttingData.unit) {
    errors.push('Unit is required');
  }

  // Validate source piece
  if (!sourcePiece || !sourcePiece.id) {
    errors.push('Valid source piece is required');
  }

  if (errors.length > 0) {
    return {
      isValid: false,
      errors: errors
    };
  }

  // Validate cut dimensions
  const dimValidation = dimensionService.validateDimensions(
    cuttingData.cut_length,
    cuttingData.cut_width,
    cuttingData.unit
  );
  if (!dimValidation.isValid) {
    return {
      isValid: false,
      errors: dimValidation.errors
    };
  }

  // Validate that cut fits within source piece
  const sourceDimensions = getSourcePieceDimensions(sourcePiece);
  if (!sourceDimensions) {
    return {
      isValid: false,
      errors: ['Cannot determine source piece dimensions']
    };
  }

  const sufficiencyResult = dimensionService.checkDimensionSufficiency(
    sourceDimensions,
    {
      length: cuttingData.cut_length,
      width: cuttingData.cut_width,
      unit: cuttingData.unit
    }
  );

  if (!sufficiencyResult.isValid || !sufficiencyResult.canFit) {
    return {
      isValid: false,
      errors: ['Cut dimensions exceed available source piece dimensions']
    };
  }

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

/**
 * Gets available dimensions from source piece based on its status
 * @param {Object} sourcePiece - Source RM inventory piece
 * @returns {Object|null} Available dimensions
 */
function getSourcePieceDimensions(sourcePiece) {
  if (!sourcePiece) return null;

  switch (sourcePiece.status) {
    case 'FULL':
      return {
        length: sourcePiece.length,
        width: sourcePiece.width,
        unit: sourcePiece.unit
      };
    
    case 'USABLE':
    case 'WASTE':
      if (sourcePiece.usable_length && sourcePiece.usable_width) {
        return {
          length: sourcePiece.usable_length,
          width: sourcePiece.usable_width,
          unit: sourcePiece.unit
        };
      }
      return null;
    
    default:
      return null;
  }
}

/**
 * Calculates cutting operation results including remaining material
 * @param {Object} cuttingData - Cutting operation data
 * @param {Object} sourcePiece - Source piece being cut
 * @returns {Object} Calculation results
 */
function calculateCuttingResults(cuttingData, sourcePiece) {
  const sourceDimensions = getSourcePieceDimensions(sourcePiece);
  if (!sourceDimensions) {
    return {
      isValid: false,
      error: 'Cannot determine source piece dimensions'
    };
  }

  // Convert cut dimensions to source piece unit if needed
  const cutDimensions = {
    length: cuttingData.cut_length,
    width: cuttingData.cut_width,
    unit: cuttingData.unit
  };

  let normalizedCutDimensions = cutDimensions;
  if (cutDimensions.unit !== sourceDimensions.unit) {
    const conversionResult = dimensionService.convertDimensions(cutDimensions, sourceDimensions.unit);
    if (!conversionResult.isValid) {
      return {
        isValid: false,
        error: 'Unit conversion failed'
      };
    }
    normalizedCutDimensions = conversionResult.dimensions;
  }

  // Calculate areas
  const sourceArea = sourceDimensions.length * sourceDimensions.width;
  const cutArea = normalizedCutDimensions.length * normalizedCutDimensions.width;
  const remainingArea = sourceArea - cutArea;

  // Calculate remaining dimensions (simplified rectangular cutting)
  // In a typical cutting operation, we cut a rectangle from one corner/edge
  // This leaves us with remaining material that can be in different shapes
  // For simplicity, we'll assume we cut from one corner, leaving an L-shaped remainder
  // We'll represent this as the largest rectangular piece that can fit in the remainder
  
  let remainingLength = 0;
  let remainingWidth = 0;
  
  // Option 1: Cut from length, leaving width intact
  const option1Length = sourceDimensions.length - normalizedCutDimensions.length;
  const option1Width = sourceDimensions.width;
  const option1Area = Math.max(0, option1Length * option1Width);
  
  // Option 2: Cut from width, leaving length intact  
  const option2Length = sourceDimensions.length;
  const option2Width = sourceDimensions.width - normalizedCutDimensions.width;
  const option2Area = Math.max(0, option2Length * option2Width);
  
  // Choose the option that gives us the larger remaining piece
  if (option1Area >= option2Area && option1Length > 0) {
    remainingLength = option1Length;
    remainingWidth = option1Width;
  } else if (option2Area > 0 && option2Width > 0) {
    remainingLength = option2Length;
    remainingWidth = option2Width;
  }
  
  const calculatedRemainingArea = remainingLength * remainingWidth;

  // Check if there's significant remaining material
  const hasRemainingMaterial = calculatedRemainingArea > 0.001; // Minimum threshold
  
  let remainingPiece = null;
  let remainingPieceId = null;
  let scrapDimensions = null;

  if (hasRemainingMaterial) {
    // Determine if remaining material is large enough to be a new piece
    const minDimensions = MIN_REUSABLE_DIMENSIONS[sourceDimensions.unit];
    
    if (remainingLength >= minDimensions.length && remainingWidth >= minDimensions.width) {
      // Create remaining piece - this represents the leftover material after cutting
      remainingPieceId = generatePieceId(); // Would be generated by database
      remainingPiece = {
        id: remainingPieceId,
        product_id: sourcePiece.product_id,
        grn_item_id: sourcePiece.grn_item_id,
        piece_number: generatePieceNumber(sourcePiece),
        length: remainingLength,
        width: remainingWidth,
        unit: sourceDimensions.unit,
        status: 'USABLE',
        usable_length: remainingLength,
        usable_width: remainingWidth,
        parent_piece_id: sourcePiece.id,
        created_from_cutting: true,
        // Inherit properties from source piece for traceability
        supplier_batch: sourcePiece.supplier_batch,
        quality_grade: sourcePiece.quality_grade,
        cost_per_area: sourcePiece.cost_per_area
      };
    } else {
      // Too small for reuse - mark as scrap
      scrapDimensions = {
        length: remainingLength,
        width: remainingWidth,
        unit: sourceDimensions.unit,
        area: remainingLength * remainingWidth
      };
    }
  }

  return {
    isValid: true,
    cutArea: cutArea,
    remainingArea: calculatedRemainingArea,
    hasRemainingMaterial: hasRemainingMaterial,
    remainingDimensions: hasRemainingMaterial ? {
      length: remainingLength,
      width: remainingWidth,
      unit: sourceDimensions.unit
    } : null,
    remainingPiece: remainingPiece,
    remainingPieceId: remainingPieceId,
    scrapDimensions: scrapDimensions,
    utilizationRatio: cutArea / sourceArea,
    wasteRatio: calculatedRemainingArea / sourceArea
  };
}

/**
 * Generates waste pieces from remaining material
 * @param {Object} remainingDimensions - Remaining material dimensions
 * @param {Object} sourcePiece - Original source piece
 * @param {Object} options - Generation options
 * @returns {Object} Waste generation result
 */
function generateWastePieces(remainingDimensions, sourcePiece, options = {}) {
  const { autoClassify = true, minThreshold = null } = options;

  if (!remainingDimensions || remainingDimensions.length <= 0 || remainingDimensions.width <= 0) {
    return {
      isValid: true,
      wastePieces: []
    };
  }

  const wastePieces = [];
  const unit = remainingDimensions.unit;
  const minDimensions = minThreshold || MIN_REUSABLE_DIMENSIONS[unit];

  // Simple waste piece generation (could be more sophisticated)
  // For now, create one waste piece from the remaining material
  
  let wasteStatus = WASTE_STATUS.WASTE;
  
  if (autoClassify) {
    // Classify as SCRAP if too small to be useful
    if (remainingDimensions.length < minDimensions.length || 
        remainingDimensions.width < minDimensions.width) {
      wasteStatus = WASTE_STATUS.SCRAP;
    }
  }

  const wastePiece = {
    length: remainingDimensions.length,
    width: remainingDimensions.width,
    unit: unit,
    status: wasteStatus,
    area: remainingDimensions.length * remainingDimensions.width,
    parent_piece_id: sourcePiece.id,
    created_from_cutting: true,
    created_at: new Date(),
    // Inherit properties from source piece for traceability
    product_id: sourcePiece.product_id,
    grn_item_id: sourcePiece.grn_item_id,
    supplier_batch: sourcePiece.supplier_batch,
    quality_grade: sourcePiece.quality_grade,
    cost_per_area: sourcePiece.cost_per_area,
    piece_number: generatePieceNumber(sourcePiece)
  };

  wastePieces.push(wastePiece);

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

/**
 * Calculates piece status updates after cutting
 * @param {Object} sourcePiece - Original source piece
 * @param {Object} cutCalculations - Cutting calculation results
 * @param {Array} wastePieces - Generated waste pieces
 * @returns {Object} Status update information
 */
function calculatePieceStatusUpdates(sourcePiece, cutCalculations, wastePieces) {
  // Source piece updates
  let newSourceStatus = sourcePiece.status;
  let newUsableLength = sourcePiece.usable_length;
  let newUsableWidth = sourcePiece.usable_width;

  // If the entire usable area was consumed, mark as SCRAP or update usable dimensions
  if (cutCalculations.remainingArea < 0.001) {
    newSourceStatus = 'SCRAP';
    newUsableLength = 0;
    newUsableWidth = 0;
  } else if (cutCalculations.remainingPiece) {
    // If a remaining piece was created, the source piece is now fully utilized
    newSourceStatus = 'SCRAP';
    newUsableLength = 0;
    newUsableWidth = 0;
  } else if (cutCalculations.scrapDimensions) {
    // Update usable dimensions to reflect the remaining unusable area
    const sourceDimensions = getSourcePieceDimensions(sourcePiece);
    newUsableLength = Math.max(0, sourceDimensions.length - cutCalculations.scrapDimensions.length);
    newUsableWidth = Math.max(0, sourceDimensions.width - cutCalculations.scrapDimensions.width);
    
    if (newUsableLength < 0.001 || newUsableWidth < 0.001) {
      newSourceStatus = 'SCRAP';
      newUsableLength = 0;
      newUsableWidth = 0;
    } else {
      newSourceStatus = 'USABLE';
    }
  }

  return {
    sourcePiece: {
      id: sourcePiece.id,
      newStatus: newSourceStatus,
      newUsableLength: newUsableLength,
      newUsableWidth: newUsableWidth,
      lastCutAt: new Date()
    }
  };
}

/**
 * Validates waste piece classification
 * @param {Object} wastePiece - Waste piece to validate
 * @param {Object} options - Validation options
 * @returns {Object} Validation result
 */
function validateWastePieceClassification(wastePiece, options = {}) {
  const { strictClassification = false } = options;

  if (!wastePiece || !wastePiece.length || !wastePiece.width || !wastePiece.unit) {
    return {
      isValid: false,
      error: 'Waste piece must have dimensions and unit'
    };
  }

  const minDimensions = MIN_REUSABLE_DIMENSIONS[wastePiece.unit];
  if (!minDimensions) {
    return {
      isValid: false,
      error: `Unsupported unit: ${wastePiece.unit}`
    };
  }

  const isReusableSize = wastePiece.length >= minDimensions.length && 
                        wastePiece.width >= minDimensions.width;

  // Validate status classification
  if (strictClassification) {
    if (wastePiece.status === WASTE_STATUS.WASTE && !isReusableSize) {
      return {
        isValid: false,
        error: 'Piece too small to be classified as reusable WASTE'
      };
    }
    
    if (wastePiece.status === WASTE_STATUS.SCRAP && isReusableSize) {
      return {
        isValid: false,
        error: 'Piece large enough to be reusable but classified as SCRAP'
      };
    }
  }

  return {
    isValid: true,
    isReusableSize: isReusableSize,
    recommendedStatus: isReusableSize ? WASTE_STATUS.WASTE : WASTE_STATUS.SCRAP,
    area: wastePiece.length * wastePiece.width
  };
}

/**
 * Calculates cutting operation statistics
 * @param {Array} cuttingOperations - Array of cutting operations
 * @param {Object} options - Calculation options
 * @returns {Object} Statistics result
 */
function calculateCuttingStatistics(cuttingOperations, options = {}) {
  const { groupBy = null, timeRange = null } = options;

  if (!Array.isArray(cuttingOperations)) {
    return {
      isValid: false,
      error: 'Cutting operations must be an array'
    };
  }

  let filteredOperations = cuttingOperations;

  // Apply time range filter if specified
  if (timeRange && timeRange.start && timeRange.end) {
    filteredOperations = cuttingOperations.filter(op => {
      const cutDate = new Date(op.cut_at);
      return cutDate >= new Date(timeRange.start) && cutDate <= new Date(timeRange.end);
    });
  }

  // Calculate overall statistics
  const totalOperations = filteredOperations.length;
  const totalCutArea = filteredOperations.reduce((sum, op) => sum + (op.cut_area || 0), 0);
  const totalWasteArea = filteredOperations.reduce((sum, op) => {
    if (op.waste_pieces && Array.isArray(op.waste_pieces)) {
      return sum + op.waste_pieces.reduce((wasteSum, waste) => wasteSum + (waste.length * waste.width), 0);
    }
    return sum;
  }, 0);

  const averageUtilization = filteredOperations.length > 0 ? 
    filteredOperations.reduce((sum, op) => {
      const wasteArea = op.waste_pieces ? 
        op.waste_pieces.reduce((wasteSum, waste) => wasteSum + (waste.length * waste.width), 0) : 0;
      const totalArea = (op.cut_area || 0) + wasteArea;
      return sum + (totalArea > 0 ? (op.cut_area || 0) / totalArea : 0);
    }, 0) / filteredOperations.length : 0;

  const wasteReusabilityRate = filteredOperations.length > 0 ?
    filteredOperations.reduce((sum, op) => {
      if (op.waste_pieces && Array.isArray(op.waste_pieces)) {
        const reusableWaste = op.waste_pieces.filter(w => w.status === WASTE_STATUS.WASTE).length;
        const totalWaste = op.waste_pieces.length;
        return sum + (totalWaste > 0 ? reusableWaste / totalWaste : 0);
      }
      return sum;
    }, 0) / filteredOperations.length : 0;

  return {
    isValid: true,
    totalOperations: totalOperations,
    totalCutArea: totalCutArea,
    totalWasteArea: totalWasteArea,
    averageUtilization: averageUtilization,
    wasteReusabilityRate: wasteReusabilityRate,
    wasteRatio: totalCutArea > 0 ? totalWasteArea / (totalCutArea + totalWasteArea) : 0,
    operationsPerDay: timeRange ? totalOperations / Math.max(1, 
      Math.ceil((new Date(timeRange.end) - new Date(timeRange.start)) / (1000 * 60 * 60 * 24))) : null
  };
}

// Helper functions
function generatePieceId() {
  return Math.floor(Math.random() * 1000000); // Would be replaced with actual ID generation
}

function generatePieceNumber(sourcePiece) {
  return (sourcePiece.piece_number || 0) + 1; // Simple increment
}

module.exports = {
  processCuttingOperation,
  validateCuttingData,
  calculateCuttingResults,
  generateWastePieces,
  validateWastePieceClassification,
  calculateCuttingStatistics,
  getSourcePieceDimensions,
  WASTE_STATUS,
  MIN_REUSABLE_DIMENSIONS
};