import { Queue, Worker, type Job } from 'bullmq';
import { env } from '../config/env';
import { prisma } from '../config/database';
import { logger } from '../utils/logger';

interface PollAutoFinalizeJobData {
  pollId: string;
}

class PollAutoFinalizeQueueService {
  private queue: Queue;
  private worker: Worker | null = null;

  constructor() {
    this.queue = new Queue('poll-auto-finalize', {
      connection: { url: env.REDIS_URL },
      defaultJobOptions: {
        attempts: 3,
        backoff: { type: 'exponential', delay: 5000 },
        removeOnComplete: { age: 7 * 24 * 60 * 60, count: 500 },
        removeOnFail: { age: 30 * 24 * 60 * 60 },
      },
    });
  }

  startWorker() {
    this.worker = new Worker(
      'poll-auto-finalize',
      async (job: Job<PollAutoFinalizeJobData>) => {
        const { pollId } = job.data;
        logger.info(`Processing poll auto-finalize job ${job.id}`, { pollId });

        // Dynamic import to avoid circular dependency
        const { meetingPlannerService } = await import(
          '../modules/meeting-planner/meeting-planner.service'
        );
        await meetingPlannerService.processAutoFinalize(pollId);
      },
      {
        connection: { url: env.REDIS_URL },
        concurrency: 1,
      }
    );

    this.worker.on('completed', (job) => {
      logger.info(`Poll auto-finalize job ${job.id} completed`);
    });

    this.worker.on('failed', (job, err) => {
      logger.error(`Poll auto-finalize job ${job?.id} failed: ${err.message}`);
    });

    logger.info('Poll auto-finalize queue worker started');

    // Startup recovery: process past-due polls and re-schedule future ones
    this.recoverOnStartup().catch((err) =>
      logger.error('Poll auto-finalize startup recovery failed', { error: err })
    );
  }

  async scheduleJob(pollId: string, deadline: Date): Promise<string | null> {
    const delay = deadline.getTime() - Date.now();
    if (delay <= 0) {
      // Already past deadline — process immediately
      const job = await this.queue.add(`finalize-${pollId}`, { pollId }, {
        jobId: `poll-finalize-${pollId}`,
      });
      return job.id ?? null;
    }

    const job = await this.queue.add(`finalize-${pollId}`, { pollId }, {
      delay,
      jobId: `poll-finalize-${pollId}`,
    });
    logger.info(`Scheduled poll auto-finalize for ${deadline.toISOString()}`, {
      pollId,
      jobId: job.id,
      delayMs: delay,
    });
    return job.id ?? null;
  }

  async cancelJob(pollId: string): Promise<void> {
    try {
      const jobs = await this.queue.getJobs(['delayed', 'waiting']);
      for (const job of jobs) {
        if (job.data.pollId === pollId) {
          await job.remove();
          logger.info(`Cancelled auto-finalize job for poll ${pollId}`, { jobId: job.id });
        }
      }
    } catch (error) {
      logger.error('Failed to cancel poll auto-finalize job', { error, pollId });
    }
  }

  private async recoverOnStartup() {
    const openPolls = await prisma.schedulingPoll.findMany({
      where: {
        autoFinalize: true,
        status: 'OPEN',
        votingDeadline: { not: null },
      },
      select: { id: true, votingDeadline: true },
    });

    for (const poll of openPolls) {
      if (!poll.votingDeadline) continue;
      const jobId = await this.scheduleJob(poll.id, poll.votingDeadline);
      if (jobId) {
        await prisma.schedulingPoll.update({
          where: { id: poll.id },
          data: { autoFinalizeJobId: jobId },
        }).catch(() => {}); // Best-effort
      }
    }

    if (openPolls.length > 0) {
      logger.info(`Recovered ${openPolls.length} poll auto-finalize jobs on startup`);
    }
  }

  async close() {
    if (this.worker) {
      await this.worker.close();
    }
    await this.queue.close();
    logger.info('Poll auto-finalize queue closed');
  }
}

export const pollAutoFinalizeQueueService = new PollAutoFinalizeQueueService();
