import { Op, type WhereOptions } from 'sequelize';

import { Job, TJob } from '../../store/models/job';
import CustomError from '../error';

export default function createQueueStore(queue: string) {
  return {
    async isCancelled(id: string): Promise<boolean> {
      const job = await Job.findOne({ where: { queue, id }, transaction: null });
      return !!job && !!job.cancelled;
    },
    getJob(id: string): Promise<TJob | null> {
      return Job.findOne({ where: { queue, id }, transaction: null });
    },
    getJobs(): Promise<TJob[]> {
      return Job.findAll({
        where: { queue, delay: -1, cancelled: false },
        order: [['created_at', 'ASC']],
        transaction: null,
      });
    },
    getScheduledJobs(): Promise<TJob[]> {
      return Job.findAll({
        where: { queue, delay: { [Op.not]: -1 }, will_run_at: { [Op.lte]: Date.now() }, cancelled: false },
        order: [['created_at', 'ASC']],
        transaction: null,
      });
    },
    findJobs(predicate: WhereOptions<TJob>): Promise<TJob[]> {
      return Job.findAll({
        where: {
          queue,
          cancelled: false,
          ...predicate,
        },
        order: [['created_at', 'ASC']],
        transaction: null,
      });
    },

    async updateJob(id: string, updates: Partial<TJob>): Promise<TJob> {
      const job = await Job.findOne({ where: { queue, id }, transaction: null });
      if (!job) {
        throw new CustomError('JOB_NOT_FOUND', `Job ${id} does not exist`);
      }

      return job.update(updates);
    },
    // Q1: skipDuplicateCheck skips the pre-create findOne when caller guarantees no duplicate
    // (e.g., after deleteJob). Unique constraint catch still handles concurrent edge cases.
    async addJob(id: string, job: any, attrs: Partial<TJob> = {}, skipDuplicateCheck: boolean = false): Promise<TJob> {
      if (!skipDuplicateCheck) {
        const existingJob = await Job.findOne({ where: { id } });

        if (existingJob) {
          throw new CustomError(
            'JOB_DUPLICATE',
            `Job with id ${id} already exists ${existingJob.queue === queue ? 'in the same queue' : `in queue ${existingJob.queue}`}`
          );
        }
      }

      try {
        const newJob = await Job.create({
          id,
          job,
          queue,
          retry_count: 1,
          cancelled: false,
          ...attrs,
        });
        return newJob;
      } catch (error) {
        if (error.name === 'SequelizeUniqueConstraintError') {
          throw new CustomError('JOB_DUPLICATE', `Job with id ${id} was created concurrently`);
        }
        throw error;
      }
    },
    async deleteJob(id: string): Promise<boolean> {
      const data = await Job.destroy({ where: { queue, id } });
      return data > 0;
    },
  };
}

export type TQueueStore = ReturnType<typeof createQueueStore>;
