import cron from 'node-cron';

export interface CronTask {
  start(): void;
  stop(): void;
  destroy(): void;
}

export interface CronAdapter {
  validate(expression: string): boolean;
  schedule(expression: string, callback: () => void | Promise<void>, options?: { scheduled?: boolean }): CronTask;
}

export class NodeCronAdapter implements CronAdapter {
  validate(expression: string): boolean {
    return cron.validate(expression);
  }

  schedule(expression: string, callback: () => void | Promise<void>, options?: { scheduled?: boolean }): CronTask {
    return cron.schedule(expression, callback, options as any);
  }
}

export class MockCronAdapter implements CronAdapter {
  private mockTasks: Map<string, MockCronTask> = new Map();
  private shouldValidate: boolean = true;

  setShouldValidate(value: boolean): void {
    this.shouldValidate = value;
  }

  validate(expression: string): boolean {
    return this.shouldValidate;
  }

  schedule(expression: string, callback: () => void | Promise<void>, options?: { scheduled?: boolean }): CronTask {
    const task = new MockCronTask(callback, options?.scheduled !== false);
    this.mockTasks.set(expression, task);
    return task;
  }

  getTasks(): Map<string, MockCronTask> {
    return this.mockTasks;
  }

  clear(): void {
    this.mockTasks.clear();
  }
}

class MockCronTask implements CronTask {
  private isRunning: boolean;
  private callback: () => void | Promise<void>;
  private destroyed: boolean = false;

  constructor(callback: () => void | Promise<void>, autoStart: boolean = true) {
    this.callback = callback;
    this.isRunning = autoStart;
  }

  start(): void {
    if (!this.destroyed) {
      this.isRunning = true;
    }
  }

  stop(): void {
    this.isRunning = false;
  }

  destroy(): void {
    this.isRunning = false;
    this.destroyed = true;
  }

  isActive(): boolean {
    return this.isRunning && !this.destroyed;
  }

  isDestroyed(): boolean {
    return this.destroyed;
  }

  async trigger(): Promise<void> {
    if (this.isRunning && !this.destroyed) {
      await this.callback();
    }
  }
}