import { Forbidden } from '../../../errors/Forbidden.js';
import isolateObjectProperty from '../../../utilities/isolateObjectProperty.js';
import { getUpdateJobFunction } from './runJob/getUpdateJobFunction.js';
import { importHandlerPath } from './runJob/importHandlerPath.js';
import { runJob } from './runJob/index.js';
import { runJSONJob } from './runJSONJob/index.js';
export const runJobs = async ({ id, limit = 10, overrideAccess, queue, req, where: whereFromProps })=>{
    if (!overrideAccess) {
        const hasAccess = await req.payload.config.jobs.access.run({
            req
        });
        if (!hasAccess) {
            throw new Forbidden(req.t);
        }
    }
    const where = {
        and: [
            {
                completedAt: {
                    exists: false
                }
            },
            {
                hasError: {
                    not_equals: true
                }
            },
            {
                processing: {
                    equals: false
                }
            },
            {
                or: [
                    {
                        waitUntil: {
                            exists: false
                        }
                    },
                    {
                        waitUntil: {
                            less_than: new Date().toISOString()
                        }
                    }
                ]
            }
        ]
    };
    if (queue) {
        where.and.push({
            queue: {
                equals: queue
            }
        });
    }
    if (whereFromProps) {
        where.and.push(whereFromProps);
    }
    // Find all jobs and ensure we set job to processing: true as early as possible to reduce the chance of
    // the same job being picked up by another worker
    const jobsQuery = id ? {
        docs: [
            await req.payload.update({
                id,
                collection: 'payload-jobs',
                data: {
                    processing: true,
                    seenByWorker: true
                },
                depth: req.payload.config.jobs.depth,
                disableTransaction: true,
                showHiddenFields: true
            })
        ]
    } : await req.payload.update({
        collection: 'payload-jobs',
        data: {
            processing: true,
            seenByWorker: true
        },
        depth: req.payload.config.jobs.depth,
        disableTransaction: true,
        limit,
        showHiddenFields: true,
        where
    });
    /**
   * Just for logging purposes, we want to know how many jobs are new and how many are existing (= already been tried).
   * This is only for logs - in the end we still want to run all jobs, regardless of whether they are new or existing.
   */ const { newJobs } = jobsQuery.docs.reduce((acc, job)=>{
        if (job.totalTried > 0) {
            acc.existingJobs.push(job);
        } else {
            acc.newJobs.push(job);
        }
        return acc;
    }, {
        existingJobs: [],
        newJobs: []
    });
    if (!jobsQuery.docs.length) {
        return {
            noJobsRemaining: true,
            remainingJobsFromQueried: 0
        };
    }
    if (jobsQuery?.docs?.length) {
        req.payload.logger.info(`Running ${jobsQuery.docs.length} jobs.`);
    }
    const jobsToDelete = req.payload.config.jobs.deleteJobOnComplete ? [] : undefined;
    const jobPromises = jobsQuery.docs.map(async (job)=>{
        if (!job.workflowSlug && !job.taskSlug) {
            throw new Error('Job must have either a workflowSlug or a taskSlug');
        }
        const jobReq = isolateObjectProperty(req, 'transactionID');
        const workflowConfig = job.workflowSlug ? req.payload.config.jobs?.workflows.find(({ slug })=>slug === job.workflowSlug) : {
            slug: 'singleTask',
            handler: async ({ job, tasks })=>{
                await tasks[job.taskSlug]('1', {
                    input: job.input
                });
            }
        };
        if (!workflowConfig) {
            return null // Skip jobs with no workflow configuration
            ;
        }
        const updateJob = getUpdateJobFunction(job, jobReq);
        // the runner will either be passed to the config
        // OR it will be a path, which we will need to import via eval to avoid
        // Next.js compiler dynamic import expression errors
        let workflowHandler;
        if (typeof workflowConfig.handler === 'function' || typeof workflowConfig.handler === 'object' && Array.isArray(workflowConfig.handler)) {
            workflowHandler = workflowConfig.handler;
        } else {
            workflowHandler = await importHandlerPath(workflowConfig.handler);
            if (!workflowHandler) {
                const jobLabel = job.workflowSlug || `Task: ${job.taskSlug}`;
                const errorMessage = `Can't find runner while importing with the path ${workflowConfig.handler} in job type ${jobLabel}.`;
                req.payload.logger.error(errorMessage);
                await updateJob({
                    error: {
                        error: errorMessage
                    },
                    hasError: true,
                    processing: false
                });
                return;
            }
        }
        if (typeof workflowHandler === 'function') {
            const result = await runJob({
                job,
                req: jobReq,
                updateJob,
                workflowConfig,
                workflowHandler
            });
            if (result.status !== 'error' && jobsToDelete) {
                jobsToDelete.push(job.id);
            }
            return {
                id: job.id,
                result
            };
        } else {
            const result = await runJSONJob({
                job,
                req: jobReq,
                updateJob,
                workflowConfig,
                workflowHandler
            });
            if (result.status !== 'error' && jobsToDelete) {
                jobsToDelete.push(job.id);
            }
            return {
                id: job.id,
                result
            };
        }
    });
    const resultsArray = await Promise.all(jobPromises);
    if (jobsToDelete && jobsToDelete.length > 0) {
        try {
            await req.payload.delete({
                collection: 'payload-jobs',
                req,
                where: {
                    id: {
                        in: jobsToDelete
                    }
                }
            });
        } catch (err) {
            req.payload.logger.error({
                err,
                msg: `failed to delete jobs ${jobsToDelete.join(', ')} on complete`
            });
        }
    }
    const resultsObject = resultsArray.reduce((acc, cur)=>{
        if (cur !== null) {
            // Check if there's a valid result to include
            acc[cur.id] = cur.result;
        }
        return acc;
    }, {});
    let remainingJobsFromQueried = 0;
    for(const jobID in resultsObject){
        const jobResult = resultsObject[jobID];
        if (jobResult.status === 'error') {
            remainingJobsFromQueried++ // Can be retried
            ;
        }
    }
    return {
        jobStatus: resultsObject,
        remainingJobsFromQueried
    };
};

//# sourceMappingURL=index.js.map