import { Injectable } from '@nestjs/common';
import { JobLockService } from './job-lock.service';
import { JobsService } from './jobs.service';
import { BackgroundJobRecord } from './jobs.types';

export type BackgroundJobHandler = (payload: unknown, job: BackgroundJobRecord) => Promise<void> | void;

export interface RunJobOnceOptions {
  workerId: string;
  now?: Date;
  leaseMs?: number;
  retryDelayMs?: number;
}

@Injectable()
export class BackgroundJobWorker {
  private readonly handlers = new Map<string, BackgroundJobHandler>();

  constructor(
    private readonly locks: JobLockService,
    private readonly jobs: JobsService,
  ) {}

  registerHandler(type: string, handler: BackgroundJobHandler) {
    this.handlers.set(type, handler);
  }

  async runOnce(options: RunJobOnceOptions): Promise<boolean> {
    const job = await this.locks.claimNext({
      workerId: options.workerId,
      types: Array.from(this.handlers.keys()),
      now: options.now,
      leaseMs: options.leaseMs,
    });
    if (!job) return false;

    const handler = this.handlers.get(job.type);
    if (!handler) {
      await this.jobs.markFailed(job.id, `No handler registered for ${job.type}`, {
        now: options.now,
        retryDelayMs: options.retryDelayMs,
      });
      return false;
    }

    try {
      await handler(job.payload, job);
      await this.jobs.markSucceeded(job.id);
      return true;
    } catch (error) {
      await this.jobs.markFailed(job.id, error, {
        now: options.now,
        retryDelayMs: options.retryDelayMs,
      });
      return false;
    }
  }
}
