/**
 * OCR service client
 */

import { createWorker } from 'tesseract.js';
import { getEnv } from '@the-order/shared';
import { StorageClient } from '@the-order/storage';

export interface OCRResult {
  text: string;
  confidence: number;
  words: Array<{
    text: string;
    confidence: number;
    bbox: { x0: number; y0: number; x1: number; y1: number };
  }>;
}

export class OCRClient {
  private storageClient?: StorageClient;

  constructor(storageClient?: StorageClient) {
    this.storageClient = storageClient;
  }

  /**
   * Process document from storage key with retry logic
   */
  async processFromStorage(
    storageKey: string,
    options?: { maxRetries?: number; initialDelay?: number }
  ): Promise<OCRResult> {
    if (!this.storageClient) {
      throw new Error('Storage client required for processing from storage');
    }

    const fileBuffer = await this.storageClient.download(storageKey);
    return this.processBuffer(fileBuffer, options);
  }

  /**
   * Process document from buffer with retry logic
   */
  async processBuffer(
    buffer: Buffer,
    options?: { maxRetries?: number; initialDelay?: number }
  ): Promise<OCRResult> {
    const maxRetries = options?.maxRetries ?? 3;
    const initialDelay = options?.initialDelay ?? 1000;
    let lastError: Error | null = null;

    for (let attempt = 0; attempt < maxRetries; attempt++) {
      try {
        const env = getEnv();
        
        // Use external OCR service if configured
        if (env.OCR_SERVICE_URL) {
          return await this.processWithExternalService(buffer);
        }

        // Fallback to local Tesseract.js
        return await this.processWithTesseract(buffer);
      } catch (error) {
        lastError = error instanceof Error ? error : new Error(String(error));
        
        // Don't retry on the last attempt
        if (attempt === maxRetries - 1) {
          throw lastError;
        }

        // Exponential backoff: delay = initialDelay * 2^attempt
        const delay = initialDelay * Math.pow(2, attempt);
        await new Promise((resolve) => setTimeout(resolve, delay));
      }
    }

    // This should never be reached, but TypeScript needs it
    throw lastError || new Error('OCR processing failed after retries');
  }

  /**
   * Process with external OCR service
   */
  private async processWithExternalService(buffer: Buffer): Promise<OCRResult> {
    const env = getEnv();
    const response = await fetch(`${env.OCR_SERVICE_URL}/process`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/octet-stream',
        Authorization: `Bearer ${env.OCR_SERVICE_API_KEY}`,
      },
      body: buffer,
    });

    if (!response.ok) {
      throw new Error(`OCR service error: ${response.status}`);
    }

    return (await response.json()) as OCRResult;
  }

  /**
   * Process with local Tesseract.js
   */
  private async processWithTesseract(buffer: Buffer): Promise<OCRResult> {
    const worker = await createWorker('eng');
    
    try {
      const { data } = await worker.recognize(buffer);
      
      return {
        text: data.text,
        confidence: data.confidence || 0,
        words: data.words.map((word) => ({
          text: word.text,
          confidence: word.confidence || 0,
          bbox: {
            x0: word.bbox.x0,
            y0: word.bbox.y0,
            x1: word.bbox.x1,
            y1: word.bbox.y1,
          },
        })),
      };
    } finally {
      await worker.terminate();
    }
  }
}

