import { Queue, Worker, QueueEvents } from 'bullmq';
import Redis from 'ioredis';
import { checkMonitor } from './monitor';
import { Monitor } from '../db';

// Redis connection
const redisConnection = new Redis(process.env.REDIS_URL || 'redis://localhost:6380', {
  maxRetriesPerRequest: null,
});

// Monitor check queue
export const monitorQueue = new Queue('monitor-checks', {
  connection: redisConnection,
  defaultJobOptions: {
    removeOnComplete: 100, // Keep last 100 completed jobs
    removeOnFail: 50, // Keep last 50 failed jobs
    attempts: 3, // Retry failed jobs 3 times
    backoff: {
      type: 'exponential',
      delay: 5000, // Start with 5 second delay
    },
  },
});

// Queue events for monitoring
const queueEvents = new QueueEvents('monitor-checks', { connection: redisConnection });

queueEvents.on('completed', ({ jobId }) => {
  console.log(`[Scheduler] Job ${jobId} completed`);
});

queueEvents.on('failed', ({ jobId, failedReason }) => {
  console.error(`[Scheduler] Job ${jobId} failed:`, failedReason);
});

/**
 * Schedule a monitor for recurring checks
 */
export async function scheduleMonitor(monitor: Monitor): Promise<void> {
  if (monitor.status !== 'active') {
    console.log(`[Scheduler] Skipping inactive monitor ${monitor.id}`);
    return;
  }

  const jobId = `monitor-${monitor.id}`;

  try {
    // Remove existing job if it exists
    await unscheduleMonitor(monitor.id);

    // Add new recurring job
    await monitorQueue.add(
      'check-monitor',
      {
        monitorId: monitor.id,
        url: monitor.url,
        name: monitor.name,
      },
      {
        jobId,
        repeat: {
          every: monitor.frequency * 60 * 1000, // Convert minutes to milliseconds
        },
      }
    );

    console.log(
      `[Scheduler] Monitor ${monitor.id} scheduled (frequency: ${monitor.frequency} min)`
    );
  } catch (error) {
    console.error(`[Scheduler] Failed to schedule monitor ${monitor.id}:`, error);
    throw error;
  }
}

/**
 * Remove a monitor from the schedule
 */
export async function unscheduleMonitor(monitorId: string): Promise<void> {
  const jobId = `monitor-${monitorId}`;

  try {
    // Get all repeatable jobs
    const repeatableJobs = await monitorQueue.getRepeatableJobs();

    // Find and remove the job for this monitor
    const job = repeatableJobs.find((j) => j.id === jobId);
    if (job && job.key) {
      await monitorQueue.removeRepeatableByKey(job.key);
      console.log(`[Scheduler] Monitor ${monitorId} unscheduled`);
    }

    // Also remove any pending jobs with this ID
    const jobs = await monitorQueue.getJobs(['waiting', 'delayed']);
    for (const j of jobs) {
      if (j.id === jobId) {
        await j.remove();
      }
    }
  } catch (error) {
    console.error(`[Scheduler] Failed to unschedule monitor ${monitorId}:`, error);
    // Don't throw - unscheduling is best-effort
  }
}

/**
 * Reschedule a monitor (useful when frequency changes)
 */
export async function rescheduleMonitor(monitor: Monitor): Promise<void> {
  console.log(`[Scheduler] Rescheduling monitor ${monitor.id}`);
  await unscheduleMonitor(monitor.id);
  await scheduleMonitor(monitor);
}

/**
 * Initialize the worker to process monitor checks
 */
export function startWorker(): Worker {
  const worker = new Worker(
    'monitor-checks',
    async (job) => {
      const { monitorId } = job.data;
      console.log(`[Worker] Processing check for monitor ${monitorId}`);

      try {
        await checkMonitor(monitorId);
        console.log(`[Worker] Successfully checked monitor ${monitorId}`);
      } catch (error) {
        console.error(`[Worker] Error checking monitor ${monitorId}:`, error);
        throw error; // Re-throw to mark job as failed
      }
    },
    {
      connection: redisConnection,
      concurrency: 5, // Process up to 5 monitors concurrently
    }
  );

  worker.on('completed', (job) => {
    console.log(`[Worker] Job ${job.id} completed`);
  });

  worker.on('failed', (job, err) => {
    console.error(`[Worker] Job ${job?.id} failed:`, err.message);
  });

  worker.on('error', (err) => {
    console.error('[Worker] Worker error:', err);
  });

  console.log('[Worker] Monitor check worker started');
  return worker;
}

/**
 * Gracefully shutdown the scheduler
 */
export async function shutdownScheduler(): Promise<void> {
  console.log('[Scheduler] Shutting down...');

  try {
    await monitorQueue.close();
    await queueEvents.close();
    await redisConnection.quit();
    console.log('[Scheduler] Shutdown complete');
  } catch (error) {
    console.error('[Scheduler] Error during shutdown:', error);
  }
}

/**
 * Get scheduler stats for monitoring
 */
export async function getSchedulerStats() {
  try {
    const [waiting, active, completed, failed, delayed, repeatableJobs] = await Promise.all([
      monitorQueue.getWaitingCount(),
      monitorQueue.getActiveCount(),
      monitorQueue.getCompletedCount(),
      monitorQueue.getFailedCount(),
      monitorQueue.getDelayedCount(),
      monitorQueue.getRepeatableJobs(),
    ]);

    return {
      waiting,
      active,
      completed,
      failed,
      delayed,
      scheduled: repeatableJobs.length,
    };
  } catch (error) {
    console.error('[Scheduler] Error getting stats:', error);
    return null;
  }
}
