/**
 * GRN Processing Service for Dimension-Based Inventory
 * Handles GRN processing with dimension-based RM inventory piece creation
 */

// Import required models
const { GRN, GRNItem, Product, RMInventoryPiece, Inventory, InventoryItem, InventoryMovement } = require('../models');
// Import dimension validation service
const dimensionValidationService = require('./dimensionValidationService');
// Import custom error classes
const { ValidationError, NotFoundError } = require('../utils/errors');
// Import logger
const logger = require('../utils/logger');
// Import Sequelize transaction
const { sequelize } = require('../models');
// Import UID/barcode generation utilities
const { generateUID, generateBarcode } = require('../utils/uidGenerator');

/**
 * Process GRN with dimension-based RM inventory creation
 * @param {number} grnId - GRN ID to process
 * @param {number} userId - User ID processing the GRN
 * @returns {Promise<Object>} Processing result with created pieces
 */
async function processGRNWithDimensions(grnId, userId) {
  if (!grnId) {
    throw new ValidationError('GRN ID is required');
  }

  if (!userId) {
    throw new ValidationError('User ID is required');
  }

  // Get GRN with items and product details
  const grn = await GRN.findByPk(grnId, {
    include: [
      {
        model: GRNItem,
        as: 'items',
        include: [
          {
            model: Product,
            as: 'product',
            required: true
          }
        ]
      }
    ]
  });

  if (!grn) {
    throw new NotFoundError(`GRN with ID ${grnId} not found`);
  }

  if (!grn.items || grn.items.length === 0) {
    throw new ValidationError('GRN has no items to process');
  }

  if (grn.processed_at) {
    throw new ValidationError('GRN has already been processed');
  }

  // Start transaction
  const transaction = await sequelize.transaction();
  
  try {
    const processingResults = [];

    // Process each GRN item
    for (const grnItem of grn.items) {
      const product = grnItem.product;
      
      // Validate product dimension requirements
      const productValidation = dimensionValidationService.validateProductDimensionRequirement(product);
      
      if (!productValidation.isValid) {
        throw new ValidationError(`Product validation failed for ${product.name}: ${productValidation.error}`);
      }

      // If product requires dimensions, process as dimension-based
      if (productValidation.requiresDimensions) {
        const result = await processDimensionBasedGRNItem(grnItem, product, userId, transaction);
        processingResults.push(result);
      } else {
        // For quantity-based items (special items RM, FG products), use quantity-based processing
        // Special items RM (track_by_dimensions = false) and FG products
        const result = await processQuantityBasedGRNItem(grnItem, product, userId, transaction, true);
        processingResults.push(result);
      }
    }

    // Mark GRN as processed
    await grn.update(
      { processed_at: new Date() },
      { transaction }
    );

    // Commit transaction
    await transaction.commit();

    logger.info(`GRN ${grnId} processed successfully with ${processingResults.length} items`);

    return {
      grnId: grnId,
      processedAt: new Date(),
      itemsProcessed: processingResults.length,
      results: processingResults
    };

  } catch (error) {
    await transaction.rollback();
    logger.error(`Failed to process GRN ${grnId}: ${error.message}`);
    throw error;
  }
}

/**
 * Process dimension-based GRN item (RM products)
 * @param {Object} grnItem - GRN item to process
 * @param {Object} product - Product details
 * @param {number} userId - User ID
 * @param {Object} transaction - Database transaction
 * @returns {Promise<Object>} Processing result
 */
async function processDimensionBasedGRNItem(grnItem, product, userId, transaction) {
  // Validate dimension fields are provided
  if (!grnItem.piece_length || !grnItem.piece_width || !grnItem.dimension_unit) {
    throw new ValidationError(
      `RM product ${product.name} requires dimensions (piece_length, piece_width, dimension_unit)`
    );
  }

  // Validate dimensions
  const dimensionValidation = dimensionValidationService.validateDimensions(
    grnItem.piece_length,
    grnItem.piece_width,
    grnItem.dimension_unit
  );

  if (!dimensionValidation.isValid) {
    throw new ValidationError(
      `Invalid dimensions for ${product.name}: ${dimensionValidation.errors.join(', ')}`
    );
  }

  // Validate unit compatibility with product
  if (grnItem.dimension_unit !== product.unit_of_measure) {
    // Convert dimensions to product unit
    const convertedDimensions = dimensionValidationService.convertDimensions(
      {
        length: grnItem.piece_length,
        width: grnItem.piece_width,
        unit: grnItem.dimension_unit
      },
      product.unit_of_measure
    );

    if (!convertedDimensions.isValid) {
      throw new ValidationError(
        `Unit conversion failed for ${product.name}: ${convertedDimensions.error}`
      );
    }

    // Use converted dimensions
    grnItem.piece_length = convertedDimensions.dimensions.length;
    grnItem.piece_width = convertedDimensions.dimensions.width;
    grnItem.dimension_unit = convertedDimensions.dimensions.unit;
  }

  const piecesCount = grnItem.pieces_count || 1;
  const createdPieces = [];

  // Create individual RM inventory pieces
  for (let i = 0; i < piecesCount; i++) {
    const piece = await RMInventoryPiece.create({
      product_id: product.id,
      variant_id: grnItem.variant_id || null,
      length: grnItem.piece_length,
      width: grnItem.piece_width,
      unit: grnItem.dimension_unit,
      status: 'FULL',
      usable_length: null, // FULL pieces should not have usable dimensions
      usable_width: null, // FULL pieces should not have usable dimensions
      source: 'GRN',
      source_reference_id: grnItem.grn_id,
      source_item_id: grnItem.id,
      piece_number: i + 1,
      unit_cost: grnItem.unit_cost,
      received_at: new Date(),
      created_by: userId
    }, { transaction });

    createdPieces.push(piece);
  }

  logger.info(
    `Created ${createdPieces.length} RM inventory pieces for product ${product.name} ` +
    `(${grnItem.piece_length}×${grnItem.piece_width} ${grnItem.dimension_unit})`
  );

  return {
    type: 'dimension-based',
    productId: product.id,
    productName: product.name,
    grnItemId: grnItem.id,
    dimensions: {
      length: grnItem.piece_length,
      width: grnItem.piece_width,
      unit: grnItem.dimension_unit
    },
    piecesCreated: createdPieces.length,
    pieceIds: createdPieces.map(p => p.id),
    totalArea: grnItem.piece_length * grnItem.piece_width * piecesCount,
    unitCost: grnItem.unit_cost
  };
}

/**
 * Process quantity-based GRN item (non-RM products)
 * @param {Object} grnItem - GRN item to process
 * @param {Object} product - Product details
 * @param {number} userId - User ID
 * @param {Object} transaction - Database transaction
 * @returns {Promise<Object>} Processing result
 */
async function processQuantityBasedGRNItem(grnItem, product, userId, transaction) {
  // For non-RM products, this would integrate with existing inventory system
  // For now, we'll just return a placeholder result
  logger.info(
    `Processed quantity-based item: ${product.name}, quantity: ${grnItem.quantity}`
  );

  return {
    type: 'quantity-based',
    productId: product.id,
    productName: product.name,
    grnItemId: grnItem.id,
    quantity: grnItem.quantity,
    unitCost: grnItem.unit_cost,
    totalCost: grnItem.quantity * grnItem.unit_cost
  };
}

/**
 * Validate GRN item dimensions before processing
 * @param {Object} grnItem - GRN item to validate
 * @param {Object} product - Product details
 * @returns {Object} Validation result
 */
function validateGRNItemDimensions(grnItem, product) {
  // Check if product requires dimensions
  const productValidation = dimensionValidationService.validateProductDimensionRequirement(product);
  
  if (!productValidation.isValid) {
    return {
      isValid: false,
      error: productValidation.error
    };
  }

  // If product doesn't require dimensions, skip dimension validation
  if (!productValidation.requiresDimensions) {
    return {
      isValid: true,
      requiresDimensions: false
    };
  }

  // Validate dimension fields are provided
  if (!grnItem.piece_length || !grnItem.piece_width || !grnItem.dimension_unit) {
    return {
      isValid: false,
      error: `RM product ${product.name} requires dimensions (piece_length, piece_width, dimension_unit)`
    };
  }

  // Validate dimension values
  const dimensionValidation = dimensionValidationService.validateDimensions(
    grnItem.piece_length,
    grnItem.piece_width,
    grnItem.dimension_unit
  );

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

  // Validate pieces count
  const piecesCount = grnItem.pieces_count || 1;
  if (piecesCount < 1) {
    return {
      isValid: false,
      error: 'Pieces count must be at least 1'
    };
  }

  return {
    isValid: true,
    requiresDimensions: true,
    dimensions: {
      length: grnItem.piece_length,
      width: grnItem.piece_width,
      unit: grnItem.dimension_unit
    },
    piecesCount: piecesCount
  };
}

/**
 * Handle multiple dimension scenarios in a single GRN item
 * @param {Array} dimensionSets - Array of dimension sets
 * @param {Object} product - Product details
 * @param {Object} grnItem - Base GRN item data
 * @param {number} userId - User ID
 * @param {Object} transaction - Database transaction
 * @returns {Promise<Array>} Array of created pieces
 */
async function processMultipleDimensionSets(dimensionSets, product, grnItem, userId, transaction) {
  // Validate multiple dimension sets
  const validation = dimensionValidationService.validateMultipleDimensionSets(
    dimensionSets,
    product.unit_of_measure
  );

  if (!validation.isValid) {
    throw new ValidationError(`Multiple dimension validation failed: ${validation.error}`);
  }

  const allCreatedPieces = [];

  // Process each dimension set
  for (const dimSet of validation.dimensionSets) {
    const piecesCount = dimSet.quantity || 1;

    // Create pieces for this dimension set
    for (let i = 0; i < piecesCount; i++) {
      const piece = await RMInventoryPiece.create({
        product_id: product.id,
        variant_id: grnItem.variant_id || null,
        length: dimSet.normalizedDimensions.length,
        width: dimSet.normalizedDimensions.width,
        unit: dimSet.normalizedDimensions.unit,
        status: 'FULL',
        usable_length: null, // FULL pieces should not have usable dimensions
        usable_width: null, // FULL pieces should not have usable dimensions
        source: 'GRN',
        source_reference_id: grnItem.grn_id,
        source_item_id: grnItem.id,
        piece_number: allCreatedPieces.length + i + 1,
        unit_cost: grnItem.unit_cost,
        received_at: new Date(),
        created_by: userId
      }, { transaction });

      allCreatedPieces.push(piece);
    }
  }

  logger.info(
    `Created ${allCreatedPieces.length} RM inventory pieces for product ${product.name} ` +
    `with ${dimensionSets.length} different dimension sets`
  );

  return allCreatedPieces;
}

module.exports = {
  processGRNWithDimensions,
  validateGRNItemDimensions,
  processMultipleDimensionSets
};