import { Worker } from 'bullmq';

import { ensureSchema } from './db/schema.js';
import { db } from './db/client.js';
import {
  runBirdMilestoneReminders,
  runMedicationReminders,
  startBirdMilestoneReminderScheduler,
  startMedicationReminderScheduler,
} from './app.js';
import {
  adoptionReportQueueName,
  closeAdoptionReportQueue,
  type AdoptionReportJobData,
  type AdoptionReportJobResult,
} from './queues/adoptionReportQueue.js';
import {
  birdMilestoneReminderQueueName,
  closeBirdMilestoneReminderQueue,
  type BirdMilestoneReminderJobData,
  type BirdMilestoneReminderJobResult,
} from './queues/birdMilestoneReminderQueue.js';
import {
  closeMedicationReminderQueue,
  medicationReminderQueueName,
  type MedicationReminderJobData,
  type MedicationReminderJobResult,
} from './queues/medicationReminderQueue.js';
import { redisConnection } from './queues/redisConnection.js';
import { renderAdoptionReportForBird } from './reports/adoptionReportJob.js';

let birdMilestoneWorker: Worker<BirdMilestoneReminderJobData, BirdMilestoneReminderJobResult> | null = null;
let medicationReminderWorker: Worker<MedicationReminderJobData, MedicationReminderJobResult> | null = null;
let adoptionReportWorker: Worker<AdoptionReportJobData, AdoptionReportJobResult> | null = null;

const startWorker = async () => {
  await ensureSchema();

  birdMilestoneWorker = new Worker<BirdMilestoneReminderJobData, BirdMilestoneReminderJobResult>(
    birdMilestoneReminderQueueName,
    async (job) => {
      const result = await runBirdMilestoneReminders(job.data.runDate);
      console.log(
        `Bird milestone reminder job completed for ${result.runDate}: checked=${result.checked}, sent=${result.sent}, skipped=${result.skipped}, failed=${result.failed}`,
      );
      return result;
    },
    {
      connection: redisConnection,
      concurrency: 1,
    },
  );

  birdMilestoneWorker.on('failed', (job, error) => {
    console.error(`Bird milestone reminder job failed: id=${job?.id ?? 'unknown'}`, error);
  });

  medicationReminderWorker = new Worker<MedicationReminderJobData, MedicationReminderJobResult>(
    medicationReminderQueueName,
    async (job) => {
      const result = await runMedicationReminders(job.data.runDate, job.data.currentTime);
      console.log(
        `Medication reminder job completed for ${result.runDate} ${result.currentTime}: checked=${result.checked}, sent=${result.sent}, skipped=${result.skipped}, failed=${result.failed}`,
      );
      return result;
    },
    {
      connection: redisConnection,
      concurrency: 1,
    },
  );

  medicationReminderWorker.on('failed', (job, error) => {
    console.error(`Medication reminder job failed: id=${job?.id ?? 'unknown'}`, error);
  });

  adoptionReportWorker = new Worker<AdoptionReportJobData, AdoptionReportJobResult>(
    adoptionReportQueueName,
    async (job) => {
      const pdf = await renderAdoptionReportForBird(job.data);
      console.log(`Adoption report job completed: id=${job.id ?? 'unknown'}, birdId=${job.data.birdId}, bytes=${pdf.length}`);
      return {
        pdfBase64: pdf.toString('base64'),
      };
    },
    {
      connection: redisConnection,
      concurrency: 1,
    },
  );

  adoptionReportWorker.on('failed', (job, error) => {
    console.error(`Adoption report job failed: id=${job?.id ?? 'unknown'}, birdId=${job?.data.birdId ?? 'unknown'}`, error);
  });

  startBirdMilestoneReminderScheduler();
  startMedicationReminderScheduler();
  console.log('FlockPal worker started.');
};

const shutdown = async (signal: string) => {
  console.log(`FlockPal worker received ${signal}; shutting down.`);
  await birdMilestoneWorker?.close();
  await medicationReminderWorker?.close();
  await adoptionReportWorker?.close();
  await closeBirdMilestoneReminderQueue();
  await closeMedicationReminderQueue();
  await closeAdoptionReportQueue();
  await db.close();
  process.exit(0);
};

process.on('SIGINT', () => {
  void shutdown('SIGINT');
});

process.on('SIGTERM', () => {
  void shutdown('SIGTERM');
});

startWorker().catch((error) => {
  console.error('Failed to start FlockPal worker', error);
  process.exit(1);
});
