import { EventEmitter } from 'events';
import { logger } from '../utils/logger';

export interface ProcessingProgress {
  documentId: string;
  jobId: string;
  status: 'uploading' | 'processing' | 'completed' | 'error';
  step: 'validation' | 'text_extraction' | 'analysis' | 'summary_generation' | 'storage';
  progress: number; // 0-100
  message: string;
  startTime: Date;
  estimatedTimeRemaining?: number;
  currentChunk?: number;
  totalChunks?: number;
  error?: string;
}

class UploadProgressService extends EventEmitter {
  private progressMap = new Map<string, ProcessingProgress>();

  /**
   * Initialize progress tracking for a document
   */
  initializeProgress(documentId: string, jobId: string): ProcessingProgress {
    const progress: ProcessingProgress = {
      documentId,
      jobId,
      status: 'processing',
      step: 'validation',
      progress: 0,
      message: 'Initializing document processing...',
      startTime: new Date(),
    };

    this.progressMap.set(documentId, progress);
    this.emit('progress', progress);
    logger.info('Progress tracking initialized', { documentId, jobId });
    return progress;
  }

  /**
   * Update progress for a specific step
   */
  updateProgress(
    documentId: string,
    step: ProcessingProgress['step'],
    progress: number,
    message: string,
    metadata?: {
      currentChunk?: number;
      totalChunks?: number;
      estimatedTimeRemaining?: number;
    }
  ): void {
    const currentProgress = this.progressMap.get(documentId);
    if (!currentProgress) {
      logger.warn('No progress tracking found for document', { documentId });
      return;
    }

    const updatedProgress: ProcessingProgress = {
      ...currentProgress,
      step,
      progress: Math.min(100, Math.max(0, progress)),
      message,
      ...(metadata?.currentChunk !== undefined && { currentChunk: metadata.currentChunk }),
      ...(metadata?.totalChunks !== undefined && { totalChunks: metadata.totalChunks }),
      ...(metadata?.estimatedTimeRemaining !== undefined && { estimatedTimeRemaining: metadata.estimatedTimeRemaining }),
    };

    this.progressMap.set(documentId, updatedProgress);
    this.emit('progress', updatedProgress);
    
    logger.info('Progress updated', {
      documentId,
      step,
      progress: updatedProgress.progress,
      message,
      currentChunk: metadata?.currentChunk,
      totalChunks: metadata?.totalChunks,
    });
  }

  /**
   * Mark processing as completed
   */
  markCompleted(documentId: string, message: string = 'Processing completed successfully'): void {
    const currentProgress = this.progressMap.get(documentId);
    if (!currentProgress) {
      logger.warn('No progress tracking found for document', { documentId });
      return;
    }

    const completedProgress: ProcessingProgress = {
      ...currentProgress,
      status: 'completed',
      step: 'storage',
      progress: 100,
      message,
    };

    this.progressMap.set(documentId, completedProgress);
    this.emit('progress', completedProgress);
    this.emit('completed', completedProgress);
    
    logger.info('Processing completed', { documentId, message });
  }

  /**
   * Mark processing as failed
   */
  markError(documentId: string, error: string): void {
    const currentProgress = this.progressMap.get(documentId);
    if (!currentProgress) {
      logger.warn('No progress tracking found for document', { documentId });
      return;
    }

    const errorProgress: ProcessingProgress = {
      ...currentProgress,
      status: 'error',
      progress: 0,
      message: `Error: ${error}`,
      error,
    };

    this.progressMap.set(documentId, errorProgress);
    this.emit('progress', errorProgress);
    this.emit('error', errorProgress);
    
    logger.error('Processing failed', { documentId, error });
  }

  /**
   * Get current progress for a document
   */
  getProgress(documentId: string): ProcessingProgress | null {
    return this.progressMap.get(documentId) || null;
  }

  /**
   * Get all active progress
   */
  getAllProgress(): ProcessingProgress[] {
    return Array.from(this.progressMap.values());
  }

  /**
   * Clean up completed progress (older than 1 hour)
   */
  cleanupOldProgress(): void {
    const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
    const toDelete: string[] = [];

    this.progressMap.forEach((progress, documentId) => {
      if (progress.status === 'completed' && progress.startTime < oneHourAgo) {
        toDelete.push(documentId);
      }
    });

    toDelete.forEach(documentId => {
      this.progressMap.delete(documentId);
    });

    if (toDelete.length > 0) {
      logger.info('Cleaned up old progress entries', { count: toDelete.length });
    }
  }

  /**
   * Calculate estimated time remaining based on current progress
   */
  calculateEstimatedTimeRemaining(documentId: string): number | undefined {
    const progress = this.progressMap.get(documentId);
    if (!progress || progress.progress === 0) {
      return undefined;
    }

    const elapsed = Date.now() - progress.startTime.getTime();
    const estimatedTotal = (elapsed / progress.progress) * 100;
    return Math.max(0, estimatedTotal - elapsed);
  }

  /**
   * Stop the service and clean up resources
   */
  stop(): void {
    this.progressMap.clear();
    this.removeAllListeners();
    logger.info('Upload progress service stopped');
  }
}

export const uploadProgressService = new UploadProgressService();

// Clean up old progress every 30 minutes
setInterval(() => {
  uploadProgressService.cleanupOldProgress();
}, 30 * 60 * 1000); 