/**
 * Client-Side Printer Utility
 * Handles printing ZPL labels from the browser to local USB/network printers
 * Supports Windows printers (Zebra, Honeywell, TSC)
 * 
 * Architecture:
 * - Frontend queries Print Service (http://localhost:9101) in REAL-TIME
 * - No localStorage caching - always fresh printer detection
 * - Print Service handles Windows API communication with printers
 */

/**
 * Get print service URL from env or use default
 * @returns {string} Print service URL
 */
const getPrintServiceUrl = () => {
  return process.env.NEXT_PUBLIC_PRINT_SERVICE_URL || 'http://127.0.0.1:9101';
};

const getPrintServiceKey = () => {
  // Use default key if not configured (matches PrintService default)
  return process.env.NEXT_PUBLIC_PRINT_SERVICE_KEY || 'xyz-pos-local-token';
};

const buildPrintServiceHeaders = (includeKey = true) => {
  const headers = {
    'Content-Type': 'application/json',
  };

  if (includeKey) {
    const key = getPrintServiceKey();
    headers['X-Print-Agent-Key'] = key;
  }

  return headers;
};

/**
 * Check if print service is healthy and available
 * @returns {Promise<boolean>} True if service is available, false otherwise
 */
export const checkPrintServiceHealth = async () => {
  const serviceUrl = getPrintServiceUrl();
  try {
    const response = await fetch(`${serviceUrl}/health`, {
      method: 'GET',
      headers: buildPrintServiceHeaders(false),
      signal: AbortSignal.timeout(2000),
    });
    return response.ok;
  } catch (error) {
    // Health check is best-effort; avoid noisy console errors
    if (error.name !== 'AbortError') {
      console.warn('Print service health check failed:', error.message);
    }
    return false;
  }
};

/**
 * Get available printers from print service (REAL-TIME, no caching)
 * Always queries the print service directly - no localStorage fallbacks
 * @returns {Promise<Array>} Array of available printers
 * @throws {Error} If print service is not available or no printers found
 */
export const getAvailablePrinters = async () => {
  const serviceUrl = getPrintServiceUrl();
  
  try {
    // Query print service directly
    const response = await fetch(`${serviceUrl}/printers`, {
      method: 'GET',
      headers: buildPrintServiceHeaders(true),
      signal: AbortSignal.timeout(2000),
    });

    if (!response.ok) {
      throw new Error(`Print service returned status ${response.status}. Please ensure the XYZ POS Print Service is running.`);
    }

    const data = await response.json();
    
    if (!data.printers || data.printers.length === 0) {
      throw new Error('No label printers found. Please connect a label printer and ensure it is properly installed.');
    }

    // Map service printers to our format
    const printers = data.printers.map((printer) => ({
      id: printer.id || `printer-${Date.now()}-${Math.random()}`,
      name: printer.name,
      type: printer.type || 'label',
      brand: printer.brand || detectPrinterBrand(printer.name),
      description: printer.description || '',
      dpi: printer.dpi || 203, // Include DPI from service
      port: printer.port || '', // Include port from service
      source: 'local-service',
    }));

    return printers;
  } catch (error) {
    // Fail fast with clear error messages
    if (error.name === 'AbortError') {
      throw new Error('Print service timeout. Please ensure the XYZ POS Print Service is running on localhost:9101');
    }
    
    if (error.message.includes('fetch')) {
      throw new Error(`Cannot connect to print service at ${serviceUrl}. Please ensure the XYZ POS Print Service is running.`);
    }
    
    // Re-throw our custom errors
    throw error;
  }
};

/**
 * Detect printer brand from printer name
 * @param {string} name - Printer name
 * @returns {string} Brand name
 */
const detectPrinterBrand = (name) => {
  const lowerName = (name || '').toLowerCase();
  if (lowerName.includes('zebra')) return 'Zebra';
  if (lowerName.includes('honeywell')) return 'Honeywell';
  if (lowerName.includes('tsc') || lowerName.includes('tspl')) return 'TSC';
  if (lowerName.includes('intermec')) return 'Intermec';
  if (lowerName.includes('datamax')) return 'Datamax';
  return 'Unknown';
};

/**
 * Request USB printer access (requires user interaction)
 * Shows browser permission dialog to access USB devices
 * @returns {Promise<Array>} Array of detected USB printers
 */
/**
 * Print ZPL to printer via local print service
 * Uses Windows Print Spooler API through the print service
 * @param {string} zplData - ZPL data to print
 * @param {string} printerName - Windows printer name
 * @param {string} printerPort - Printer port (optional, e.g., USB001) - speeds up printing
 * @returns {Promise<Object>} Print result
 * @throws {Error} If print service is not available or printing fails
 */
export const printZPLToPrinter = async (zplData, printerName, printerPort = null, retries = 2) => {
  console.log(`Printing ZPL to: ${printerName}, port: ${printerPort}`);
  
  if (!zplData) {
    throw new Error('ZPL data is required');
  }
  
  if (!printerName && !printerPort) {
    throw new Error('Either printer name or port is required');
  }

  const serviceUrl = `${getPrintServiceUrl()}/print`;
  
  // Build request body for .NET print service
  const requestBody = {
    printerName,
    zpl: zplData,
  };
  
  if (printerPort) {
    requestBody.printerPort = printerPort;
  }
  
  let lastError = null;
  
  // Retry logic for transient failures
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      if (attempt > 0) {
        console.log(`Retry attempt ${attempt} of ${retries} for printer ${printerName}`);
        // Wait before retry (exponential backoff: 1s, 2s)
        await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
      }
      
      console.log(`Calling print service at: ${serviceUrl} (attempt ${attempt + 1})`);
      
      // Increased timeout to 60 seconds for slow USB printers and network latency
      const response = await fetch(serviceUrl, {
        method: 'POST',
        headers: {
          ...buildPrintServiceHeaders(true),
        },
        body: JSON.stringify(requestBody),
        signal: AbortSignal.timeout(60000), // 60 second timeout for USB printing
      });

      console.log(`Print service response status: ${response.status}`);
      
      if (!response.ok) {
        // Try to get error message from response
        const errorData = await response.json().catch(() => ({}));
        const errorMessage = errorData.error || errorData.detail || `Print service returned status ${response.status}`;
        
        // Don't retry on client errors (4xx) - these are permanent failures
        if (response.status >= 400 && response.status < 500) {
          throw new Error(errorMessage);
        }
        
        // Retry on server errors (5xx) or network issues
        lastError = new Error(errorMessage);
        continue;
      }
      
      const result = await response.json();
      console.log(`Print service response:`, result);
      
      // Check if the service returned an error
      if (result.success === false) {
        const errorMsg = result.error || result.detail || 'Print service returned an error';
        // Don't retry on validation errors
        if (response.status === 400) {
          throw new Error(errorMsg);
        }
        lastError = new Error(errorMsg);
        continue;
      }
      
      // Verify success
      if (!result.success) {
        lastError = new Error('Print operation did not succeed');
        continue;
      }
      
      // Success!
      return {
        success: true,
        method: 'local-service',
        printer: printerName,
        port: printerPort,
        message: result.message || 'Label printed successfully',
      };
    } catch (error) {
      // Don't retry on AbortError (timeout) - these are likely permanent
      if (error.name === 'AbortError') {
        throw new Error('Print request timeout. The print service may be busy or the printer is not responding.');
      }
      
      // Don't retry on connection errors if it's the last attempt
      if (error.message.includes('fetch') || error.message.includes('Failed to fetch')) {
        if (attempt === retries) {
          throw new Error(`Cannot connect to print service at ${getPrintServiceUrl()}. Please ensure the XYZ POS Print Service is running.`);
        }
        lastError = error;
        continue;
      }
      
      // For other errors, retry if we have attempts left
      if (attempt < retries) {
        lastError = error;
        continue;
      }
      
      // Last attempt failed, throw the error
      throw error;
    }
  }
  
  // All retries exhausted
  throw lastError || new Error('Print operation failed after multiple retry attempts');
};

/**
 * Create a batch print job on the local print service.
 * The service will queue, retry, and process each item sequentially.
 *
 * @param {string} printerName - Target printer name
 * @param {Array<{ zpl: string, itemId?: string }>} items - Batch items to print
 * @returns {Promise<{ success: boolean, jobId: string, printerName: string, totalItems: number, status: string, createdAt: string }>}
 */
export const createPrintBatchJob = async (printerName, items) => {
  if (!printerName) {
    throw new Error('Printer name is required to create a batch job.');
  }

  if (!items || !Array.isArray(items) || items.length === 0) {
    throw new Error('Batch job must contain at least one item.');
  }

  const serviceUrl = `${getPrintServiceUrl()}/print-batch`;

  try {
    const response = await fetch(serviceUrl, {
      method: 'POST',
      headers: {
        ...buildPrintServiceHeaders(true),
      },
      body: JSON.stringify({
        printerName,
        items,
      }),
      signal: AbortSignal.timeout(60000),
    });

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      const errorMessage =
        errorData.error ||
        errorData.detail ||
        `Print service returned status ${response.status} while creating batch job.`;

      // 4xx are treated as validation/client errors
      if (response.status >= 400 && response.status < 500) {
        throw new Error(errorMessage);
      }

      throw new Error(errorMessage);
    }

    const result = await response.json();

    if (result.success === false) {
      const errorMsg = result.error || result.detail || 'Print service returned an error while creating batch job.';
      throw new Error(errorMsg);
    }

    return result;
  } catch (error) {
    if (error.name === 'AbortError') {
      throw new Error('Batch print request timeout. The print service may be busy or the printer is not responding.');
    }

    if (error.message.includes('fetch') || error.message.includes('Failed to fetch')) {
      throw new Error(`Cannot connect to print service at ${getPrintServiceUrl()}. Please ensure the XYZ POS Print Service is running.`);
    }

    throw error;
  }
};

/**
 * Get the status of a print job from the local print service.
 *
 * @param {string} jobId - Job identifier returned by createPrintBatchJob
 * @returns {Promise<Object>} Job status payload from the print service
 */
export const getPrintJobStatus = async (jobId) => {
  if (!jobId) {
    throw new Error('Job ID is required to query print job status.');
  }

  const serviceUrl = `${getPrintServiceUrl()}/print-jobs/${jobId}`;

  try {
    const response = await fetch(serviceUrl, {
      method: 'GET',
      headers: buildPrintServiceHeaders(true),
      signal: AbortSignal.timeout(10000),
    });

    if (response.status === 404) {
      throw new Error('Print job not found. It may have expired or been cleaned up.');
    }

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      const errorMessage =
        errorData.error ||
        errorData.detail ||
        `Print service returned status ${response.status} while fetching job status.`;
      throw new Error(errorMessage);
    }

    const result = await response.json();

    if (result.success === false) {
      const errorMsg = result.error || result.detail || 'Print service returned an error while fetching job status.';
      throw new Error(errorMsg);
    }

    return result;
  } catch (error) {
    if (error.name === 'AbortError') {
      throw new Error('Print job status request timeout. The print service may be busy or unreachable.');
    }

    if (error.message.includes('fetch') || error.message.includes('Failed to fetch')) {
      throw new Error(`Cannot connect to print service at ${getPrintServiceUrl()}. Please ensure the XYZ POS Print Service is running.`);
    }

    throw error;
  }
};

/**
 * Print ZPL using Web USB API (for direct USB printer access)
 * Requires HTTPS and user permission
 * @param {string} zplData - ZPL data to print
 * @returns {Promise<Object>} Print result
 */
/**
 * Main print function - sends ZPL to print service
 * Uses local print service exclusively - no fallbacks
 * @param {string} zplData - ZPL data to print
 * @param {Object} options - Print options
 * @param {string} options.printerName - Printer name (required)
 * @param {string} options.printerPort - Printer port (optional, e.g., USB001)
 * @param {string} options.method - Print method ('local-service' or 'auto')
 * @returns {Promise<Object>} Print result
 * @throws {Error} If printing fails (no fallback to download)
 */
export const printZPL = async (zplData, options = {}) => {
  const { printerName, printerPort, method = 'auto' } = options;

  // Validate required parameters
  if (!zplData) {
    throw new Error('ZPL data is required');
  }

  if (!printerName && !printerPort) {
    throw new Error('Either printer name or port is required. Please select a printer.');
  }

  // Always use local service - this is the only supported method
  // Browser cannot directly communicate with printers due to security restrictions
  return await printZPLToPrinter(zplData, printerName, printerPort);
};
