/**
 * Bulk Tracking Worker for Redirect Intelligence v2
 * 
 * Processes bulk URL tracking jobs using BullMQ
 */

import { Worker, Job } from 'bullmq';
import IORedis from 'ioredis';
import { logger } from '../lib/logger';
import { prisma } from '../lib/prisma';
import { RedirectTrackerService } from '../services/redirect-tracker.service';

interface BulkTrackingJobData {
  jobId: string;
  userId: string;
  organizationId?: string;
  urls: Array<{
    url: string;
    label?: string;
    metadata?: Record<string, any>;
  }>;
  options: {
    method: 'GET' | 'POST' | 'HEAD';
    userAgent?: string;
    maxHops: number;
    timeout: number;
    enableSSLAnalysis: boolean;
    enableSEOAnalysis: boolean;
    enableSecurityAnalysis: boolean;
    headers?: Record<string, string>;
  };
}

interface JobResult {
  url: string;
  label?: string;
  checkId?: string;
  status: 'success' | 'failed';
  error?: string;
  timing: {
    startedAt: Date;
    finishedAt?: Date;
    durationMs?: number;
  };
}

export class BulkTrackingWorker {
  private worker: Worker;
  private redis: IORedis;
  private redirectTracker: RedirectTrackerService;

  constructor() {
    this.redis = new IORedis({
      host: process.env.REDIS_HOST || 'localhost',
      port: parseInt(process.env.REDIS_PORT || '6379'),
      retryDelayOnFailover: 100,
      enableReadyCheck: false,
      maxRetriesPerRequest: null,
    });

    this.redirectTracker = new RedirectTrackerService();

    this.worker = new Worker(
      'bulk-tracking',
      this.processJob.bind(this),
      {
        connection: this.redis,
        concurrency: parseInt(process.env.BULK_WORKER_CONCURRENCY || '3'),
        removeOnComplete: 100,
        removeOnFail: 50,
      }
    );

    this.setupEventHandlers();
  }

  /**
   * Setup worker event handlers
   */
  private setupEventHandlers(): void {
    this.worker.on('ready', () => {
      logger.info('Bulk tracking worker is ready');
    });

    this.worker.on('active', (job: Job) => {
      logger.info(`Processing bulk job: ${job.id}`, {
        jobId: job.data.jobId,
        userId: job.data.userId,
        urlCount: job.data.urls.length,
      });
    });

    this.worker.on('completed', (job: Job, result: any) => {
      logger.info(`Bulk job completed: ${job.id}`, {
        jobId: job.data.jobId,
        userId: job.data.userId,
        successful: result.successful,
        failed: result.failed,
        total: result.total,
      });
    });

    this.worker.on('failed', (job: Job | undefined, error: Error) => {
      logger.error(`Bulk job failed: ${job?.id}`, {
        jobId: job?.data?.jobId,
        userId: job?.data?.userId,
        error: error.message,
      });
    });

    this.worker.on('error', (error: Error) => {
      logger.error('Bulk tracking worker error:', error);
    });

    this.worker.on('stalled', (jobId: string) => {
      logger.warn(`Bulk job stalled: ${jobId}`);
    });
  }

  /**
   * Process a bulk tracking job
   */
  private async processJob(job: Job<BulkTrackingJobData>): Promise<any> {
    const { jobId, userId, organizationId, urls, options } = job.data;
    
    try {
      logger.info(`Starting bulk job processing: ${jobId}`, {
        userId,
        urlCount: urls.length,
      });

      // Update job status to processing
      await this.updateJobStatus(jobId, 'processing', { startedAt: new Date() });

      const results: JobResult[] = [];
      let processed = 0;
      let successful = 0;
      let failed = 0;

      // Process URLs one by one to avoid overwhelming the system
      for (const urlData of urls) {
        const startTime = new Date();
        
        try {
          // Update progress
          await job.updateProgress(Math.round((processed / urls.length) * 100));
          
          logger.debug(`Processing URL: ${urlData.url}`, { jobId, userId });

          // Track the URL using our existing service
          const trackRequest = {
            url: urlData.url,
            method: options.method,
            userAgent: options.userAgent,
            headers: options.headers,
            maxHops: options.maxHops,
            timeout: options.timeout,
            enableSSLAnalysis: options.enableSSLAnalysis,
            enableSEOAnalysis: options.enableSEOAnalysis,
            enableSecurityAnalysis: options.enableSecurityAnalysis,
            // Map metadata to request if available
            projectId: urlData.metadata?.projectId,
          };

          const result = await this.redirectTracker.trackUrl(trackRequest, userId);
          
          const finishTime = new Date();
          const duration = finishTime.getTime() - startTime.getTime();

          results.push({
            url: urlData.url,
            label: urlData.label,
            checkId: result.check.id,
            status: 'success',
            timing: {
              startedAt: startTime,
              finishedAt: finishTime,
              durationMs: duration,
            },
          });

          successful++;
          
        } catch (error) {
          const finishTime = new Date();
          const duration = finishTime.getTime() - startTime.getTime();

          logger.warn(`Failed to process URL: ${urlData.url}`, {
            jobId,
            userId,
            error: error instanceof Error ? error.message : 'Unknown error',
          });

          results.push({
            url: urlData.url,
            label: urlData.label,
            status: 'failed',
            error: error instanceof Error ? error.message : 'Unknown error',
            timing: {
              startedAt: startTime,
              finishedAt: finishTime,
              durationMs: duration,
            },
          });

          failed++;
        }

        processed++;

        // Update database progress periodically
        if (processed % 10 === 0 || processed === urls.length) {
          await this.updateJobProgress(jobId, {
            processedUrls: processed,
            successfulUrls: successful,
            failedUrls: failed,
            resultsJson: JSON.stringify(results),
          });
        }

        // Small delay between requests to be respectful
        if (processed < urls.length) {
          await new Promise(resolve => setTimeout(resolve, 100));
        }
      }

      // Final update
      await this.updateJobStatus(jobId, 'completed', {
        finishedAt: new Date(),
        processedUrls: processed,
        successfulUrls: successful,
        failedUrls: failed,
        resultsJson: JSON.stringify(results),
      });

      logger.info(`Bulk job completed: ${jobId}`, {
        userId,
        total: urls.length,
        successful,
        failed,
      });

      return {
        jobId,
        total: urls.length,
        successful,
        failed,
        results,
      };

    } catch (error) {
      logger.error(`Bulk job processing failed: ${jobId}`, {
        userId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });

      // Update job status to failed
      await this.updateJobStatus(jobId, 'failed', {
        finishedAt: new Date(),
      });

      throw error;
    }
  }

  /**
   * Update job status in database
   */
  private async updateJobStatus(
    jobId: string,
    status: string,
    updates: Record<string, any> = {}
  ): Promise<void> {
    try {
      await prisma.bulkJob.update({
        where: { id: jobId },
        data: {
          status,
          ...updates,
        },
      });
    } catch (error) {
      logger.error(`Failed to update job status: ${jobId}`, error);
    }
  }

  /**
   * Update job progress in database
   */
  private async updateJobProgress(
    jobId: string,
    updates: Record<string, any>
  ): Promise<void> {
    try {
      await prisma.bulkJob.update({
        where: { id: jobId },
        data: updates,
      });
    } catch (error) {
      logger.error(`Failed to update job progress: ${jobId}`, error);
    }
  }

  /**
   * Start the worker
   */
  async start(): Promise<void> {
    logger.info('Starting bulk tracking worker...');
    // Worker starts automatically when instantiated
  }

  /**
   * Stop the worker gracefully
   */
  async stop(): Promise<void> {
    logger.info('Stopping bulk tracking worker...');
    await this.worker.close();
    await this.redis.disconnect();
  }

  /**
   * Get worker health status
   */
  getHealthStatus(): {
    isRunning: boolean;
    isHealthy: boolean;
    concurrency: number;
  } {
    return {
      isRunning: !this.worker.closing,
      isHealthy: !this.worker.closing,
      concurrency: this.worker.opts.concurrency || 1,
    };
  }
}

