import { InjectQueue } from '@nestjs/bullmq';
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import type { Queue } from 'bullmq';
import { PrismaService } from '../../prisma/prisma.service';

type PresentismoSyncJobData = {
  relojId: string;
  empresaId: string;
};

const QUEUE_NAME = 'rrhh-presentismo-queue';
const JOB_NAME = 'sync-reloj-api';
const JOB_ID_PREFIX = 'reloj-sync:';

@Injectable()
export class PresentismoSyncSchedulerService implements OnModuleInit {
  private readonly logger = new Logger(PresentismoSyncSchedulerService.name);

  constructor(
    private readonly prisma: PrismaService,
    @InjectQueue(QUEUE_NAME)
    private readonly queue: Queue<PresentismoSyncJobData>,
  ) {}

  async onModuleInit() {
    await this.reconciliarTodos();
  }

  async reconciliarReloj(relojId: string) {
    const reloj = await this.prisma.rrhh_relojes_marcadores.findUnique({
      where: { id: relojId },
      select: {
        id: true,
        empresa_id: true,
        tipo_conexion: true,
        activo: true,
        intervalo_polling_min: true,
      },
    });

    const jobId = `${JOB_ID_PREFIX}${relojId}`;
    await this.removerPorJobId(jobId);

    if (
      !reloj ||
      reloj.tipo_conexion !== 'API' ||
      !reloj.activo ||
      !reloj.intervalo_polling_min ||
      reloj.intervalo_polling_min < 5
    ) {
      this.logger.debug(`Reloj ${relojId}: sync automático deshabilitado`);
      return;
    }

    await this.queue.add(
      JOB_NAME,
      { relojId: reloj.id, empresaId: reloj.empresa_id },
      {
        jobId,
        repeat: { every: reloj.intervalo_polling_min * 60_000 },
        removeOnComplete: { count: 50 },
        removeOnFail: { count: 200 },
        attempts: 3,
        backoff: { type: 'exponential', delay: 5_000 },
      },
    );
    this.logger.log(
      `Reloj ${reloj.id}: job automático programado cada ${reloj.intervalo_polling_min} min`,
    );
  }

  async reconciliarTodos() {
    const activos = await this.prisma.rrhh_relojes_marcadores.findMany({
      where: {
        tipo_conexion: 'API',
        activo: true,
        intervalo_polling_min: { gte: 5 },
      },
      select: { id: true },
    });

    const idsActivos = new Set(activos.map((r) => `${JOB_ID_PREFIX}${r.id}`));
    const repeatables = await this.queue.getRepeatableJobs();

    for (const rep of repeatables) {
      if (rep.name !== JOB_NAME) continue;
      if (!rep.id || !rep.id.startsWith(JOB_ID_PREFIX)) continue;
      if (!idsActivos.has(rep.id)) {
        await this.queue.removeRepeatableByKey(rep.key);
      }
    }

    for (const reloj of activos) {
      await this.reconciliarReloj(reloj.id);
    }
  }

  private async removerPorJobId(jobId: string) {
    const repeatables = await this.queue.getRepeatableJobs();
    for (const rep of repeatables) {
      if (rep.name === JOB_NAME && rep.id === jobId) {
        await this.queue.removeRepeatableByKey(rep.key);
      }
    }
  }
}

export { JOB_NAME as PRESENTISMO_SYNC_JOB_NAME, QUEUE_NAME as PRESENTISMO_SYNC_QUEUE_NAME };
