import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Logger } from '@nestjs/common';
import type { Job } from 'bullmq';
import { MiddlewareSifenService } from 'src/middleware-sifen/middleware-sifen.service';
import { PrismaService } from 'src/prisma/prisma.service';
import { tareasPendientesPorTipo, type TareaSync } from './sifen-sync.plan';

@Processor('sifen-sync-queue')
export class SifenSyncProcessor extends WorkerHost {
  private readonly logger = new Logger(SifenSyncProcessor.name);

  constructor(
    private readonly middlewareSifenService: MiddlewareSifenService,
    private readonly prisma: PrismaService,
  ) {
    super();
  }

  /**
   * Recorre el plan de sincronización (documentos y eventos de facturas, NC y
   * remisiones) y consulta SIFEN solo para las empresas que tienen algo
   * pendiente de ese tipo. Un fallo en una empresa no corta el resto.
   */
  async process(job: Job): Promise<void> {
    this.logger.log(`Iniciando sincronización SIFEN automática - Job ID: ${job.id}`);

    const pendientes = await tareasPendientesPorTipo((tarea) => this.empresasConPendientes(tarea));

    for (const { tarea, empresaIds } of pendientes) {
      let actualizados = 0;
      for (const empresaId of empresaIds) {
        try {
          const res = await this.middlewareSifenService.sincronizarSifen(empresaId, tarea.tipo, tarea.modo);
          actualizados += res.actualizados;
        } catch (error) {
          this.logger.error(
            `Error sincronizando ${tarea.etiqueta} de la empresa ${empresaId}: ${
              error instanceof Error ? error.message : 'Error desconocido'
            }`,
          );
        }
      }
      if (actualizados > 0) {
        this.logger.log(
          `Sincronización SIFEN: ${actualizados} ${tarea.etiqueta} actualizadas en ${empresaIds.length} empresa(s)`,
        );
      }
    }
  }

  /** Empresas con documentos/eventos pendientes para una tarea del plan. */
  private async empresasConPendientes(tarea: TareaSync): Promise<string[]> {
    const model = (this.prisma as unknown as Record<string, {
      findMany: (args: unknown) => Promise<Array<{ empresa_id: string | null }>>;
    }>)[tarea.tabla];
    if (!model) {
      this.logger.warn(`Tabla ${tarea.tabla} no encontrada en prisma client`);
      return [];
    }
    const filas = await model.findMany({
      where: tarea.where,
      select: { empresa_id: true },
      distinct: ['empresa_id'],
    });
    return filas.map((f) => f.empresa_id).filter((v): v is string => !!v);
  }
}
