import { REMAINING_EVENT } from "../../types/events/remaining.js";
import { localStorageTest } from "../../utils/localStorageTest.js";
import { getStaticOptions } from "../../utils/options.js";
import { request } from "../../utils/request.js";
import { locationRestJobExecute } from "../../wp-api/job.post.js";
import { fetchStatus, getConfigurableStatusParameters } from "../fetchStatus.js";
import { LOCAL_STORAGE_KEY_RESTORE_JOBS } from "../restoreFromLocalStorage.js";

import type { RemainingEvent } from "../../types/events/remaining.js";
import type { Job } from "../../types/job.js";
import type { ParamsRouteJobExecute, RequestRouteJobExecute, ResponseRouteJobExecute } from "../../wp-api/job.post.js";

/**
 * Execute a server job. This can potentially execute more jobs by IDs to get rid
 * of requests overhead.
 */
async function executeServerJob(job: Job, tryIds: number[]): Promise<ResponseRouteJobExecute> {
    const { id, delay_ms } = job;
    const result = await request<RequestRouteJobExecute, ParamsRouteJobExecute, ResponseRouteJobExecute>({
        location: locationRestJobExecute,
        request: {
            try: tryIds.join(","),
            ...getConfigurableStatusParameters(),
        },
        params: {
            id,
        },
    });

    const { done, failed, remaining, status } = result;
    fetchStatus({ from: "object", status });

    // Override global state
    getStaticOptions().remaining = remaining;

    document.dispatchEvent(
        new CustomEvent<RemainingEvent>(REMAINING_EVENT, {
            detail: {
                remaining,
            },
        }),
    );

    // Is the passed `job` failed?
    const [failedJob] = failed.filter(({ id: failedId }) => failedId === id);
    const [doneJob] = done.filter(({ id: doneId }) => doneId === id);

    if (
        // Check if the requested job is completely processed and retry again
        (done.length === 0 && !failedJob) ||
        // Check if the requested job needs to be retried cause it failed
        (failedJob && failedJob.runs <= failedJob.retries)
    ) {
        await new Promise<void>((resolve) => setTimeout(resolve, delay_ms));
        return executeServerJob(job, tryIds);
    }

    // Is the passed `job` completed?
    if (
        // Our job is already processed by another worker? Perhaps another tab? Perhaps cronjob? We do not know
        (failedJob?.exception?.errors?.["real_queue_job_locked"] ||
            doneJob ||
            (failedJob && failedJob.runs > failedJob.retries)) &&
        localStorageTest()
    ) {
        // Remove from our localStorage, as we do not need to restore it no more
        const lsKey = `${LOCAL_STORAGE_KEY_RESTORE_JOBS}-${getStaticOptions().localStorageSuffix}`;
        const restorable = localStorage.getItem(lsKey)?.split(",").map(Number) || [];
        const idx = restorable.indexOf(id);
        if (idx > -1) {
            restorable.splice(idx, 1);
        }
        localStorage.setItem(lsKey, restorable.join(","));
    }

    return result;
}

export { executeServerJob };
