/**
 * ZPL Template Generator
 * Generates ZPL (Zebra Programming Language) templates for label printing.
 *
 * IMPORTANT:
 * - Our production label size is **2.5" x 4"** (height x width) for safe printing.
 * - We generate dot-based ZPL using the target printer DPI (203/300/600...).
 *
 * Backwards compatibility:
 * - Older callers may still pass `labelWidthCm/labelHeightCm`.
 */

// Import QR code generator
const { generateQRCodeBuffer } = require('../../../utils/barcodeGenerator');
// Import logger
const logger = require('../../../utils/logger');

// Conversion constants
const CM_PER_INCH = 2.54;

/**
 * Generate ZPL template for a label.
 * Default: **2.5" x 4"** (height x width).
 *
 * @param {Object} labelData - Label data (productName, uid, qrData)
 * @param {Object} options - Label options
 * @param {number} [options.dpi=203] - Printer DPI
 * @param {number} [options.labelWidth=5] - Label width in inches
 * @param {number} [options.labelLength=2.5] - Label length/height in inches
 * @param {number} [options.labelWidthCm] - (legacy) width in cm
 * @param {number} [options.labelHeightCm] - (legacy) height in cm
 * @returns {Promise<string>} ZPL template string
 */
/**
 * Validate ZPL syntax (basic validation)
 * @param {string} zpl - ZPL string to validate
 * @throws {Error} If ZPL is invalid
 */
const validateZPL = (zpl) => {
  if (!zpl || typeof zpl !== 'string') {
    throw new Error('ZPL data must be a non-empty string');
  }

  // Check for basic ZPL structure
  if (!zpl.includes('^XA')) {
    throw new Error('Invalid ZPL: Missing ^XA (start label) command');
  }

  if (!zpl.includes('^XZ')) {
    throw new Error('Invalid ZPL: Missing ^XZ (end label) command');
  }

  // Check that ^XA comes before ^XZ
  const xaIndex = zpl.indexOf('^XA');
  const xzIndex = zpl.indexOf('^XZ');
  if (xaIndex >= xzIndex) {
    throw new Error('Invalid ZPL: ^XA must come before ^XZ');
  }

  // Check for common ZPL commands (basic sanity check)
  const validCommands = ['^FO', '^FD', '^FS', '^A0', '^BQ', '^PW', '^LL'];
  const hasValidCommand = validCommands.some(cmd => zpl.includes(cmd));
  if (!hasValidCommand && zpl.length < 20) {
    throw new Error('Invalid ZPL: ZPL appears to be too short or missing required commands');
  }
};

const generateZPLTemplate = async (labelData, options = {}) => {
  const { productName, uid, qrData } = labelData;
  
  // Validate required fields
  if (!productName || !uid) {
    throw new Error('Product name and UID are required');
  }
  
  // Use UID for QR code if qrData not provided
  const qrCodeData = qrData || uid;
  
  // DPI (most common label printers are 203/300)
  const DPI = Number(options.dpi) > 0 ? Number(options.dpi) : 203;

  // Use your hand-tuned layouts from Labelary for 203dpi and 300dpi.
  // For other DPIs, we fall back to the 203dpi template (most common).

  let zpl;

  if (DPI <= 220) {
    // 203 dpi template
    // ^PW399  ^LL200
    zpl = `
^XA
^PW399
^LL200
^CI28

^LH0,0

^FX ================= QR CODE =================
^FO20,15
^BQN,2,4
^FDLA,${qrCodeData}^FS

^FX ================= PRODUCT NAME =================
^FO140,25
^A0N,28,28
^FB260,2,0,L,0
^FD${productName}^FS

^FX ================= QR DATA =================
^FO40,160
^A0N,20,30
^FD${uid}^FS

^XZ
`.trim();
  } else {
    // 300 dpi template
    // ^PW590  ^LL295
    zpl = `
^XA
^PW590
^LL295
^CI28

^LH0,0

^FO25,20
^BQN,2,8
^FDLA,${qrCodeData}^FS

^FO210,70
^A0N,40,50
^FB380,2,0,L,0
^FD${productName}^FS

^FO30,250
^A0N,30,70
^FD${uid}^FS

^XZ
`.trim();
  }
  
  // Validate generated ZPL before returning
  try {
    validateZPL(zpl);
  } catch (error) {
    logger.error('Generated ZPL failed validation', {
      productName,
      uid,
      error: error.message,
      zplPreview: zpl.substring(0, 100),
    });
    throw new Error(`Generated ZPL is invalid: ${error.message}`);
  }
  
  logger.info('ZPL template generated', {
    productName,
    uid,
    qrCodeData,
    dpi: DPI,
  });
  
  return zpl;
};

/**
 * Generate ZPL template with image (alternative method using QR code image)
 * Generates ZPL code with embedded QR code image (base64)
 * @param {Object} labelData - Label data (productName, uid, qrData)
 * @param {Object} options - Label options (optional)
 * @returns {Promise<string>} ZPL template string with embedded image
 */
const generateZPLTemplateWithImage = async (labelData, options = {}) => {
  const { productName, uid, qrData } = labelData;
  
  // Validate required fields
  if (!productName || !uid) {
    throw new Error('Product name and UID are required');
  }
  
  // Use UID for QR code if qrData not provided
  const qrCodeData = qrData || uid;
  
  // Generate QR code as buffer (smaller size for 2.5cm x 5cm label)
  const qrBuffer = await generateQRCodeBuffer(qrCodeData, {
    width: 120,
    errorCorrectionLevel: 'M',
  });
  
  // Convert buffer to base64
  const qrBase64 = qrBuffer.toString('base64');
  
  // Label dimensions: 2.5cm x 5cm (height=2.5cm, width=5cm)
  const DPI = options.dpi || 203;
  const LABEL_HEIGHT_CM = options.labelHeightCm || 2.5; // cm (short side)
  const LABEL_WIDTH_CM = options.labelWidthCm || 5; // cm (long side)
  
  // Convert cm to inches, then to dots
  const LABEL_WIDTH_DOTS = Math.round(LABEL_WIDTH_CM * CM_TO_INCH * DPI);
  const LABEL_HEIGHT_DOTS = Math.round(LABEL_HEIGHT_CM * CM_TO_INCH * DPI);
  
  // Font sizes (scaled by DPI - adjusted for smaller label)
  const FONT_SIZE_LARGE = Math.round(24 * (DPI / 203));
  const FONT_SIZE_SMALL = Math.round(16 * (DPI / 203));
  
  // QR code size (scaled by DPI - smaller for cm label)
  const QR_SIZE = Math.round(120 * (DPI / 203));
  
  // Margins (scaled by DPI - smaller for smaller label)
  const MARGIN_X = Math.round(8 * (DPI / 203));
  const MARGIN_Y = Math.round(8 * (DPI / 203));
  
  // Calculate positions
  const PRODUCT_NAME_Y = MARGIN_Y;
  const QR_X = (LABEL_WIDTH_DOTS - QR_SIZE) / 2;
  const QR_Y = PRODUCT_NAME_Y + FONT_SIZE_LARGE + Math.round(4 * (DPI / 203));
  const UID_Y = QR_Y + QR_SIZE + Math.round(4 * (DPI / 203));
  
  // Truncate product name
  const maxChars = Math.floor((LABEL_WIDTH_DOTS - (MARGIN_X * 2)) / (FONT_SIZE_LARGE * 0.6));
  const truncatedName = productName.length > maxChars
    ? productName.substring(0, maxChars - 3) + '...'
    : productName;
  
  // Build ZPL template with embedded image
  // Note: ZPL ^GF command is used for graphic fields (base64 encoded)
  const zpl = `
^XA
^PW${LABEL_WIDTH_DOTS}
^LL${LABEL_HEIGHT_DOTS}
^FO${MARGIN_X},${MARGIN_Y}^A0N,${FONT_SIZE_LARGE},${FONT_SIZE_LARGE}^FD${truncatedName}^FS
^FO${QR_X},${QR_Y}^GF${qrBase64.length},${qrBase64.length},${qrBase64.length / 8},${qrBase64}^FS
^FO${MARGIN_X},${UID_Y}^A0N,${FONT_SIZE_SMALL},${FONT_SIZE_SMALL}^FD${uid}^FS
^XZ
`.trim();
  
  logger.info('ZPL template with image generated', {
    productName: truncatedName,
    uid,
    qrCodeData,
    imageSize: qrBase64.length,
    labelSize: `${LABEL_HEIGHT_CM}cm x ${LABEL_WIDTH_CM}cm`,
    dpi: DPI,
  });
  
  return zpl;
};

// Export functions
module.exports = {
  generateZPLTemplate, // Generate ZPL template (native QR code)
  generateZPLTemplateWithImage, // Generate ZPL template (embedded image)
  validateZPL, // Validate ZPL syntax
};
