// =============================================================================
// Conversion Broker Service
// Traffic controller managing the conversion queue and worker routing
// =============================================================================

import type {
  QueueItem,
  SupportedFormat,
  ConversionOptions,
} from '../core/types';
import {
  detectFormat,
  generateId,
  generateOutputFilename,
  getCompatibleOutputFormats,
} from '../core/format-utils';
import { getAdapter, registerAdapter } from '../core/converter-factory';

// Register adapters on module load
import { heicAdapter } from '../adapters/heic-adapter';
import { imageMagickAdapter } from '../adapters/image-magic-adapter';
import { ffmpegAdapter } from '../adapters/ffmpeg-adapter';

registerAdapter(heicAdapter);
registerAdapter(imageMagickAdapter);
registerAdapter(ffmpegAdapter);

export type QueueUpdateCallback = (queue: QueueItem[]) => void;

export class ConversionBroker {
  private queue: QueueItem[] = [];
  private listeners: Set<QueueUpdateCallback> = new Set();
  private isProcessing = false;

  /**
   * Subscribe to queue updates
   */
  subscribe(callback: QueueUpdateCallback): () => void {
    this.listeners.add(callback);
    // Immediately notify with current state
    callback([...this.queue]);
    return () => this.listeners.delete(callback);
  }

  private notifyListeners(): void {
    const snapshot = [...this.queue];
    this.listeners.forEach(cb => cb(snapshot));
  }

  /**
   * Add file(s) to the conversion queue
   */
  addToQueue(
    file: File,
    targetFormat: SupportedFormat,
    options?: ConversionOptions
  ): QueueItem | null {
    const inputFormat = detectFormat(file);
    if (!inputFormat) {
      console.warn(`Unsupported file type: ${file.type}`);
      return null;
    }

    // Validate that conversion is possible (same category)
    const compatibleFormats = getCompatibleOutputFormats(inputFormat);
    if (!compatibleFormats.includes(targetFormat)) {
      console.warn(`Cannot convert ${inputFormat} to ${targetFormat}`);
      return null;
    }

    const item: QueueItem = {
      id: generateId(),
      file,
      inputFormat,
      targetFormat,
      status: 'pending',
      progress: 0,
      options,
    };

    this.queue.push(item);
    this.notifyListeners();
    return item;
  }

  /**
   * Add multiple files at once with the same target format
   */
  addFilesToQueue(
    files: File[],
    targetFormat: SupportedFormat,
    options?: ConversionOptions
  ): QueueItem[] {
    return files
      .map(file => this.addToQueue(file, targetFormat, options))
      .filter((item): item is QueueItem => item !== null);
  }

  /**
   * Remove an item from the queue
   */
  removeFromQueue(id: string): void {
    this.queue = this.queue.filter(item => item.id !== id);
    this.notifyListeners();
  }

  /**
   * Clear all completed/errored items
   */
  clearCompleted(): void {
    this.queue = this.queue.filter(
      item => item.status !== 'complete' && item.status !== 'error'
    );
    this.notifyListeners();
  }

  /**
   * Clear entire queue
   */
  clearQueue(): void {
    this.queue = [];
    this.notifyListeners();
  }

  /**
   * Process all pending items in the queue
   */
  async processQueue(): Promise<void> {
    if (this.isProcessing) return;
    this.isProcessing = true;

    try {
      const pendingItems = this.queue.filter(item => item.status === 'pending');

      for (const item of pendingItems) {
        await this.processItem(item);
      }
    } finally {
      this.isProcessing = false;
    }
  }

  /**
   * Process a single queue item
   */
  private async processItem(item: QueueItem): Promise<void> {
    // Update status to processing
    item.status = 'processing';
    item.progress = 0;
    this.notifyListeners();

    try {
      const adapter = getAdapter(item.inputFormat, item.targetFormat);

      const blob = await adapter.convert(
        {
          file: item.file,
          targetFormat: item.targetFormat,
          options: item.options,
        },
        (progress) => {
          item.progress = progress;
          this.notifyListeners();
        }
      );

      item.result = blob;
      item.status = 'complete';
      item.progress = 100;
    } catch (error) {
      item.status = 'error';
      item.error = error instanceof Error ? error.message : 'Unknown error';
      console.error(`Conversion failed for ${item.file.name}:`, error);
    }

    this.notifyListeners();
  }

  /**
   * Get current queue state
   */
  getQueue(): QueueItem[] {
    return [...this.queue];
  }

  /**
   * Download a converted file
   */
  downloadItem(item: QueueItem): void {
    if (!item.result || item.status !== 'complete') return;

    const filename = generateOutputFilename(item.file.name, item.targetFormat);
    const url = URL.createObjectURL(item.result);
    
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    
    URL.revokeObjectURL(url);
  }
}

// Export singleton instance
export const conversionBroker = new ConversionBroker();
