/**
 * Printer Communication Service
 * Handles Network (TCP/IP) and USB printing for label printers
 * Supports Honeywell and Zebra printers
 */

// Import Node.js net module for network printing
const net = require('net');
// Import Node.js fs module for file operations
const fs = require('fs');
// Import Node.js path module
const path = require('path');
// Import logger
const logger = require('../../../utils/logger');
// Import custom error classes
const { ValidationError } = require('../../../utils/errors');

/**
 * Print to network printer (TCP/IP)
 * Sends ZPL data to a network printer via TCP/IP
 * @param {string} zplData - ZPL template string
 * @param {Object} printerConfig - Printer configuration (host, port)
 * @returns {Promise<Object>} Print result
 */
const printToNetwork = async (zplData, printerConfig) => {
  const { host, port = 9100 } = printerConfig;
  
  // Validate configuration
  if (!host) {
    throw new ValidationError('Printer host is required for network printing');
  }
  
  // Validate port
  if (!port || port < 1 || port > 65535) {
    throw new ValidationError('Valid printer port (1-65535) is required');
  }
  
  return new Promise((resolve, reject) => {
    // Create TCP socket connection
    const socket = new net.Socket();
    
    // Set timeout (10 seconds)
    socket.setTimeout(10000);
    
    // Handle connection
    socket.connect(port, host, () => {
      logger.info('Connected to network printer', { host, port });
      
      // Send ZPL data
      socket.write(zplData, 'utf8', (err) => {
        if (err) {
          logger.error('Error sending data to printer', { error: err.message, host, port });
          socket.destroy();
          reject(new Error(`Failed to send data to printer: ${err.message}`));
          return;
        }
        
        logger.info('ZPL data sent to network printer', { 
          host, 
          port, 
          dataLength: zplData.length 
        });
        
        // Close connection after sending
        socket.end();
      });
    });
    
    // Handle data received (acknowledgment)
    socket.on('data', (data) => {
      logger.debug('Received data from printer', { 
        host, 
        port, 
        data: data.toString() 
      });
    });
    
    // Handle connection close
    socket.on('close', () => {
      logger.info('Connection to network printer closed', { host, port });
      resolve({
        success: true,
        method: 'network',
        host,
        port,
        message: 'Label printed successfully',
      });
    });
    
    // Handle errors
    socket.on('error', (err) => {
      logger.error('Network printer connection error', { 
        error: err.message, 
        host, 
        port 
      });
      reject(new Error(`Network printer connection failed: ${err.message}`));
    });
    
    // Handle timeout
    socket.on('timeout', () => {
      logger.error('Network printer connection timeout', { host, port });
      socket.destroy();
      reject(new Error('Network printer connection timeout'));
    });
  });
};

/**
 * Print to USB printer
 * Sends ZPL data to a USB printer via file system (Linux/Unix)
 * @param {string} zplData - ZPL template string
 * @param {Object} printerConfig - Printer configuration (devicePath)
 * @returns {Promise<Object>} Print result
 */
const printToUSB = async (zplData, printerConfig) => {
  const { devicePath } = printerConfig;
  
  // Validate configuration
  if (!devicePath) {
    throw new ValidationError('Printer device path is required for USB printing');
  }
  
  return new Promise((resolve, reject) => {
    // Check if device exists
    if (!fs.existsSync(devicePath)) {
      reject(new ValidationError(`USB printer device not found: ${devicePath}`));
      return;
    }
    
    // Write ZPL data to device
    fs.writeFile(devicePath, zplData, 'utf8', (err) => {
      if (err) {
        logger.error('Error writing to USB printer', { 
          error: err.message, 
          devicePath 
        });
        reject(new Error(`Failed to write to USB printer: ${err.message}`));
        return;
      }
      
      logger.info('ZPL data written to USB printer', { 
        devicePath, 
        dataLength: zplData.length 
      });
      
      resolve({
        success: true,
        method: 'usb',
        devicePath,
        message: 'Label printed successfully',
      });
    });
  });
};

/**
 * Print label (auto-detect method)
 * Automatically detects and uses network or USB printing based on configuration
 * @param {string} zplData - ZPL template string
 * @param {Object} printerConfig - Printer configuration
 * @returns {Promise<Object>} Print result
 */
const printLabel = async (zplData, printerConfig) => {
  const { method, host, port, devicePath } = printerConfig;
  
  // Auto-detect method if not specified
  if (!method) {
    if (host) {
      // Network printer
      return await printToNetwork(zplData, { host, port });
    } else if (devicePath) {
      // USB printer
      return await printToUSB(zplData, { devicePath });
    } else {
      throw new ValidationError('Printer configuration is required (host for network or devicePath for USB)');
    }
  }
  
  // Use specified method
  if (method === 'network') {
    return await printToNetwork(zplData, { host, port });
  } else if (method === 'usb') {
    return await printToUSB(zplData, { devicePath });
  } else {
    throw new ValidationError(`Invalid printer method: ${method}. Use 'network' or 'usb'`);
  }
};

// Export functions
module.exports = {
  printToNetwork, // Print to network printer
  printToUSB, // Print to USB printer
  printLabel, // Print label (auto-detect)
};
