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

const QUEUE_NAME = 'scheduled-jobs';

type ScheduledJobType =
  | 'reengagement-scan'
  | 'social-digest-scan'
  | 'close-abandoned-canvass-sessions'
  | 'close-stale-tracking-sessions'
  | 'cleanup-tracking-data'
  | 'cleanup-docs-analytics'
  | 'cleanup-analytics-data'
  | 'cleanup-verification-tokens'
  | 'listmonk-full-sync'
  | 'validate-mkdocs-exports'
  | 'cleanup-docs-collab-states'
  | 'purge-expired-participant-needs';

interface ScheduledJobData {
  type: ScheduledJobType;
}

const HOUR = 60 * 60 * 1000;

const JOB_DEFINITIONS: Array<{ type: ScheduledJobType; every: number; conditional?: boolean }> = [
  { type: 'reengagement-scan', every: 24 * HOUR },
  { type: 'social-digest-scan', every: 24 * HOUR },
  { type: 'close-abandoned-canvass-sessions', every: HOUR },
  { type: 'close-stale-tracking-sessions', every: HOUR },
  { type: 'cleanup-tracking-data', every: 24 * HOUR },
  { type: 'cleanup-docs-analytics', every: 24 * HOUR },
  { type: 'cleanup-analytics-data', every: 24 * HOUR },
  { type: 'cleanup-verification-tokens', every: HOUR },
  { type: 'listmonk-full-sync', every: 6 * HOUR, conditional: true },
  { type: 'validate-mkdocs-exports', every: 24 * HOUR },
  { type: 'cleanup-docs-collab-states', every: 24 * HOUR },
  { type: 'purge-expired-participant-needs', every: 24 * HOUR },
];

async function executeJob(type: ScheduledJobType): Promise<void> {
  switch (type) {
    case 'reengagement-scan': {
      const { reengagementService } = await import('./reengagement.service');
      await reengagementService.scan();
      break;
    }
    case 'social-digest-scan': {
      const { socialDigestService } = await import('./social-digest.service');
      await socialDigestService.scan();
      break;
    }
    case 'close-abandoned-canvass-sessions': {
      const { canvassService } = await import('../modules/map/canvass/canvass.service');
      await canvassService.closeAbandonedSessions();
      break;
    }
    case 'close-stale-tracking-sessions': {
      const { trackingService } = await import('../modules/map/tracking/tracking.service');
      await trackingService.closeStaleTrackingSessions(120);
      break;
    }
    case 'cleanup-tracking-data': {
      const { trackingService } = await import('../modules/map/tracking/tracking.service');
      await trackingService.cleanupOldData(30);
      break;
    }
    case 'cleanup-docs-analytics': {
      // Delegated to unified analytics cleanup
      const { analyticsCleanupService } = await import('../modules/analytics/analytics-cleanup.service');
      await analyticsCleanupService.cleanupAll();
      break;
    }
    case 'cleanup-analytics-data': {
      const { analyticsCleanupService } = await import('../modules/analytics/analytics-cleanup.service');
      await analyticsCleanupService.cleanupAll();
      break;
    }
    case 'cleanup-verification-tokens': {
      const { verificationTokenService } = await import('./verification-token.service');
      const { passwordResetTokenService } = await import('./password-reset-token.service');
      await verificationTokenService.cleanupExpiredTokens();
      await passwordResetTokenService.cleanupExpiredTokens();
      break;
    }
    case 'listmonk-full-sync': {
      const { listmonkSyncService } = await import('./listmonk-sync.service');
      await listmonkSyncService.syncAll();
      break;
    }
    case 'validate-mkdocs-exports': {
      const { pagesService } = await import('../modules/pages/pages.service');
      await pagesService.validateExports();
      break;
    }
    case 'cleanup-docs-collab-states': {
      const { docsCollabService } = await import('../modules/docs/docs-collab.service');
      await docsCollabService.cleanupStaleStates();
      break;
    }
    case 'purge-expired-participant-needs': {
      const { participantNeedsService } = await import('../modules/people/participant-needs.service');
      await participantNeedsService.purgeExpired();
      break;
    }
  }
}

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

  constructor() {
    this.queue = new Queue(QUEUE_NAME, {
      connection: { url: env.REDIS_URL },
      defaultJobOptions: {
        removeOnComplete: { age: 60 * 60, count: 200 },
        removeOnFail: { age: 24 * 60 * 60 },
      },
    });
  }

  startWorker() {
    // Register repeatable jobs
    for (const def of JOB_DEFINITIONS) {
      // Skip conditional jobs when their feature is disabled
      if (def.type === 'listmonk-full-sync' && env.LISTMONK_SYNC_ENABLED !== 'true') {
        continue;
      }

      this.queue.add(
        def.type,
        { type: def.type } satisfies ScheduledJobData,
        {
          repeat: { every: def.every },
          jobId: `scheduled-${def.type}`,
        }
      );
    }

    this.worker = new Worker(
      QUEUE_NAME,
      async (job: Job<ScheduledJobData>) => {
        const { type } = job.data;
        logger.debug(`Scheduled job starting: ${type}`);
        await executeJob(type);
      },
      {
        connection: { url: env.REDIS_URL },
        concurrency: 2,
      }
    );

    this.worker.on('completed', (job) => {
      logger.debug(`Scheduled job ${job.name} completed`);
    });

    this.worker.on('failed', (job, err) => {
      logger.error(`Scheduled job ${job?.name} failed: ${err.message}`);
    });

    logger.info('Scheduled jobs queue worker started (11 job types)');
  }

  async close() {
    if (this.worker) {
      await this.worker.close();
    }
    await this.queue.close();
    logger.info('Scheduled jobs queue closed');
  }
}

export const scheduledJobsQueueService = new ScheduledJobsQueueService();
