/**
 * Label Printing Service
 * High-level service for printing labels for inventory items and products
 */

// Import InventoryItem and Product models
const { InventoryItem, Product } = require('../../../models');
// Import custom error classes
const { NotFoundError, ValidationError } = require('../../../utils/errors');
// Import logger
const logger = require('../../../utils/logger');
// Import ZPL generator
const { generateZPLTemplate } = require('./zplGenerator');
// Import printer service
const { printLabel } = require('./printer');
// Import QR code generator
const { generateQRCodeDataURL } = require('../../../utils/barcodeGenerator');

/**
 * Print label for inventory item
 * Generates and prints a label for a specific inventory item (FG product with UUID)
 * @param {number} inventoryItemId - Inventory item ID
 * @param {Object} printerConfig - Printer configuration
 * @returns {Promise<Object>} Print result
 */
const printInventoryItemLabel = async (inventoryItemId, printerConfig) => {
  // Validate inventory item ID
  if (!inventoryItemId) {
    throw new ValidationError('Inventory item ID is required');
  }
  
  // Find inventory item with product
  const inventoryItem = await InventoryItem.findByPk(inventoryItemId, {
    include: [
      {
        model: Product,
        as: 'product',
        attributes: ['id', 'name', 'sku', 'product_type'],
      },
    ],
  });
  
  if (!inventoryItem) {
    throw new NotFoundError('Inventory item not found');
  }
  
  // Verify product is FG (Finished Goods)
  if (inventoryItem.product.product_type !== 'FG') {
    throw new ValidationError('Labels can only be printed for FG (Finished Goods) products');
  }
  
  // Get product name
  const productName = inventoryItem.product.name || inventoryItem.product.sku || 'Product';
  
  // Get UID
  const uid = inventoryItem.uid;
  
  if (!uid) {
    throw new ValidationError('Inventory item does not have a UID');
  }
  
  // Generate ZPL template with DPI and label size from printer config
  const zplData = await generateZPLTemplate({
    productName,
    uid,
    qrData: uid, // Use UID for QR code
  }, {
    dpi: printerConfig?.dpi || 203, // Use DPI from printer config or default to 203
    // Label size is inches (2.5" x 5")
    labelLength: printerConfig?.labelLength || 2.5,
    labelWidth: printerConfig?.labelWidth || 5,
  });
  
  // Print label
  const printResult = await printLabel(zplData, printerConfig);
  
  logger.info('Inventory item label printed', {
    inventoryItemId,
    productName,
    uid,
    printResult,
  });
  
  return {
    success: true,
    inventoryItemId,
    productName,
    uid,
    printResult,
  };
};

/**
 * Print label for product (generates preview/example)
 * Generates a label template for a product (for preview/testing)
 * Note: This doesn't require an inventory item, but the product must be FG type
 * @param {number} productId - Product ID
 * @param {Object} printerConfig - Printer configuration (optional, for actual printing)
 * @param {string} sampleUID - Sample UID to use (optional)
 * @returns {Promise<Object>} Label data and optionally print result
 */
const printProductLabel = async (productId, printerConfig = null, sampleUID = null) => {
  // Validate product ID
  if (!productId) {
    throw new ValidationError('Product ID is required');
  }
  
  // Find product
  const product = await Product.findByPk(productId, {
    attributes: ['id', 'name', 'sku', 'product_type'],
  });
  
  if (!product) {
    throw new NotFoundError('Product not found');
  }
  
  // Verify product is FG (Finished Goods)
  if (product.product_type !== 'FG') {
    throw new ValidationError('Labels can only be printed for FG (Finished Goods) products');
  }
  
  // Get product name
  const productName = product.name || product.sku || 'Product';
  
  // Generate sample UID if not provided
  const uid = sampleUID || `SAMPLE-${Date.now()}-${Math.random().toString(36).substring(2, 10).toUpperCase()}`;
  
  // Generate ZPL template with DPI and label size from printer config
  const zplData = await generateZPLTemplate({
    productName,
    uid,
    qrData: uid,
  }, {
    dpi: printerConfig?.dpi || 203, // Use DPI from printer config or default to 203
    // Label size is inches (2.5" x 5")
    labelLength: printerConfig?.labelLength || 2.5,
    labelWidth: printerConfig?.labelWidth || 5,
  });
  
  // Print if printer config provided
  let printResult = null;
  if (printerConfig) {
    printResult = await printLabel(zplData, printerConfig);
  }
  
  logger.info('Product label generated', {
    productId,
    productName,
    uid,
    printed: !!printResult,
  });
  
  return {
    success: true,
    productId,
    productName,
    uid,
    zplData,
    printResult,
  };
};

/**
 * Generate label preview (QR code image)
 * Generates a preview of the label with QR code (for UI display)
 * @param {number} inventoryItemId - Inventory item ID (optional)
 * @param {number} productId - Product ID (optional, if inventoryItemId not provided)
 * @param {string} uid - UID to use (optional, if inventoryItemId not provided)
 * @param {Object} options - Label generation options (dpi, labelLength, labelWidth)
 * @returns {Promise<Object>} Label preview data
 */
const generateLabelPreview = async (inventoryItemId = null, productId = null, uid = null, options = {}) => {
  let productName = 'Product';
  let itemUID = uid;
  
  if (inventoryItemId) {
    // Get from inventory item
    const inventoryItem = await InventoryItem.findByPk(inventoryItemId, {
      include: [
        {
          model: Product,
          as: 'product',
          attributes: ['id', 'name', 'sku', 'product_type'],
        },
      ],
    });
    
    if (!inventoryItem) {
      throw new NotFoundError('Inventory item not found');
    }
    
    productName = inventoryItem.product.name || inventoryItem.product.sku || 'Product';
    itemUID = inventoryItem.uid || itemUID;
  } else if (productId) {
    // Get from product
    const product = await Product.findByPk(productId, {
      attributes: ['id', 'name', 'sku', 'product_type'],
    });
    
    if (!product) {
      throw new NotFoundError('Product not found');
    }
    
    productName = product.name || product.sku || 'Product';
    
    // Generate sample UID if not provided
    if (!itemUID) {
      itemUID = `SAMPLE-${Date.now()}-${Math.random().toString(36).substring(2, 10).toUpperCase()}`;
    }
  } else {
    throw new ValidationError('Either inventoryItemId or productId is required');
  }
  
  if (!itemUID) {
    throw new ValidationError('UID is required for label preview');
  }
  
  // Generate QR code data URL
  const qrCodeDataURL = await generateQRCodeDataURL(itemUID, {
    width: 300,
    errorCorrectionLevel: 'M',
  });
  
  // Generate ZPL template with provided options or defaults
  const zplData = await generateZPLTemplate({
    productName,
    uid: itemUID,
    qrData: itemUID,
  }, {
    dpi: options.dpi || 203, // Use provided DPI or default to 203
    // Label size is inches (2.5" x 5")
    labelLength: options.labelLength || 2.5,
    labelWidth: options.labelWidth || 5,
  });
  
  logger.info('Label preview generated', {
    inventoryItemId,
    productId,
    productName,
    uid: itemUID,
  });
  
  return {
    success: true,
    productName,
    uid: itemUID,
    qrCodeDataURL,
    zplData,
  };
};

/**
 * Generate ZPL for a batch of inventory items (for client-side/local printing)
 * Does NOT print; only returns ZPL payloads for each item.
 * @param {Array<number>} inventoryItemIds - List of inventory item IDs
 * @param {Object} options - Label options (dpi, labelLength, labelWidth)
 * @returns {Promise<Array<Object>>} Array of { inventoryItemId, productName, uid, zplData }
 */
const generateBatchInventoryItemZPL = async (inventoryItemIds = [], options = {}) => {
  if (!Array.isArray(inventoryItemIds) || inventoryItemIds.length === 0) {
    throw new ValidationError('At least one inventory item ID is required for batch ZPL generation');
  }

  const results = [];

  for (const id of inventoryItemIds) {
    // Reuse existing preview/ZPL generation logic per item
    // eslint-disable-next-line no-await-in-loop
    const preview = await generateLabelPreview(id, null, null, options);

    if (!preview.zplData) {
      // Fall back to direct ZPL generation if preview did not include ZPL
      // eslint-disable-next-line no-await-in-loop
      const zplData = await generateZPLTemplate(
        {
          productName: preview.productName || 'Product',
          uid: preview.uid,
          qrData: preview.uid,
        },
        options,
      );

      results.push({
        inventoryItemId: id,
        productName: preview.productName || 'Product',
        uid: preview.uid,
        zplData,
      });
    } else {
      results.push({
        inventoryItemId: id,
        productName: preview.productName || 'Product',
        uid: preview.uid,
        zplData: preview.zplData,
      });
    }
  }

  logger.info('Batch ZPL generated for inventory items', {
    count: results.length,
  });

  return results;
};

// Export functions
module.exports = {
  printInventoryItemLabel, // Print label for inventory item
  printProductLabel, // Print label for product
  generateLabelPreview, // Generate label preview
  generateBatchInventoryItemZPL, // Generate ZPL for a batch of inventory items
};
