import type { QueueOption } from '@stacksjs/types';

declare const queueDriver: 'database';
declare interface JobConfig {
  handle?: (payload?: any) => Promise<void>
  action?: string | (() => Promise<void>)
}
declare interface Dispatchable {
  dispatch: () => Promise<void>
  dispatchNow: () => Promise<void>
  delay: (seconds: number) => this
  afterResponse: () => this
  chain: (jobs: Dispatchable[]) => this
  onQueue: (queue: string) => this
}
export declare function runJob(name: string, options: QueueOption): Promise<void>;
export declare class Queue implements Dispatchable {
  protected options: QueueOption = {}

  constructor(
    protected name: string,
    protected payload?: any,
  ) { }

  async dispatch(): Promise<void> {
    const queueName = this.options.queue || 'default'

    const jobPayload = this.createJobPayload(queueName)

    if (this.isQueuedDriver()) {
      await this.storeQueuedJob(jobPayload)
      return
    }

    if (this.options.afterResponse) {
      this.deferAfterResponse()
      return
    }

    if (this.options.delay) {
      this.deferWithDelay()
      return
    }

    await this.runJobImmediately(jobPayload)
  }

  private isQueuedDriver(): boolean {
    return ['database', 'redis'].includes(queueDriver)
  }

  private createJobPayload(queueName: string): QueueOption {
    return {
      queue: queueName,
      payload: this.payload,
      context: this.options.context,
      maxTries: this.options.maxTries,
      timeout: this.options.timeout,
      backoff: this.options.backoff,
      delay: this.options.delay,
    }
  }

  private async storeQueuedJob(jobPayload: any): Promise<void> {
    await storeJob(this.name, jobPayload)
  }

  private deferAfterResponse(): void {
    process.on('beforeExit', async () => {
      await this.dispatchNow()
    })
  }

  private deferWithDelay(): void {
    setTimeout(async () => {
      await this.dispatchNow()
    }, this.options.delay || 0)
  }

  private async runJobImmediately(jobPayload: any): Promise<void> {
    try {
      await runJob(this.name, jobPayload)
      await this.runChainedJobs()
    }
    catch (error) {
      log.error(`Failed to dispatch job ${this.name}:`, error)
      throw error
    }
  }

  private async runChainedJobs(): Promise<void> {
    if (!this.options.chainedJobs?.length)
      return

    for (const job of this.options.chainedJobs) {
      await job.dispatch()
    }
  }

  async dispatchNow(): Promise<void> {
    try {
      await runJob(this.name, {
        payload: this.payload,
        context: this.options.context,
        immediate: true,
      })

      if (this.options.chainedJobs?.length) {
        for (const job of this.options.chainedJobs) {
          await job.dispatchNow()
        }
      }
    }
    catch (error) {
      log.error(`Failed to execute job ${this.name}:`, error)
      throw error
    }
  }

  delay(seconds: number): this {
    this.options.delay = seconds
    return this
  }

  onQueue(queue: string): this {
    this.options.queue = queue
    return this
  }

  chain(jobs: Dispatchable[]): this {
    this.options.chainedJobs = jobs
    return this
  }

  afterResponse(): this {
    this.options.afterResponse = true
    return this
  }

  tries(count: number): this {
    this.options.maxTries = count
    return this
  }

  timeout(seconds: number): this {
    this.options.timeout = seconds
    return this
  }

  backoff(attempts: number[]): this {
    this.options.backoff = attempts
    return this
  }

  withContext(context: any): this {
    this.options.context = context
    return this
  }
}
export declare class JobFactory {
  static make(name: string, payload?: any): Queue {
    return new Queue(name, payload)
  }

  static dispatch(name: string, payload?: any): Queue {
    const job = new Queue(name, payload)
    job.dispatch()
    return job
  }

  static async dispatchNow(name: string, payload?: any): Promise<void> {
    await new Queue(name, payload).dispatchNow()
  }

  static later(delay: number, name: string, payload?: any): Queue {
    return new Queue(name, payload).delay(delay)
  }

  static chain(jobs: Queue[]): Queue {
    const firstJob = jobs[0]
    return firstJob.chain(jobs.slice(1))
  }
}
export declare function job(name: string, payload?: any): Queue;
export declare function processOrder(payload, context): void;

export default {
  async handle() {
  }
};
export default {
  action: 'SendWelcomeEmail'
};
export default {
  action: async () => {
  }
};
export default async function(payload, context) {
};