import { JOBS_DELETED_EVENT } from "../../types/events/jobsDeleted.js";
import { JOBS_RETRIED_EVENT } from "../../types/events/jobsRetried.js";
import { JOBS_SKIPPED_EVENT } from "../../types/events/jobsSkipped.js";
import { request } from "../../utils/request.js";
import { locationRestJobResultPost } from "../../wp-api/jobResult.post.js";

import type { JobsDeletedEvent } from "../../types/events/jobsDeleted.js";
import type { JobsRetriedEvent } from "../../types/events/jobsRetried.js";
import type { JobsSkippedEvent } from "../../types/events/jobsSkipped.js";
import type {
    ParamsRouteJobResultPost,
    RequestRouteJobResultPost,
    ResponseRouteJobResultPost,
} from "../../wp-api/jobResult.post.js";

async function saveJobResult(
    id: number,
    process: number,
    exception: {
        code?: string;
        message?: string;
        data?: any;
    } = {},
): Promise<ResponseRouteJobResultPost> {
    const { code: errorCode, message: errorMessage, data: errorData } = exception;
    try {
        const result = await request<RequestRouteJobResultPost, ParamsRouteJobResultPost, ResponseRouteJobResultPost>({
            location: locationRestJobResultPost,
            request: {
                process,
                errorCode,
                errorMessage,
                errorData: errorData ? JSON.stringify(errorData) : undefined,
            },
            params: {
                id,
            },
        });

        if (result.pauseToAvoidRecurringException) {
            await new Promise<void>((resolve) => {
                const {
                    job: { type },
                } = result;

                // Wait for user reactivates (delete, retry) the job
                const listenerDeleted = (async ({
                    detail: {
                        params: { type: deletedType },
                    },
                }: CustomEvent<JobsDeletedEvent>) => {
                    if (deletedType === type) {
                        document.removeEventListener(JOBS_DELETED_EVENT, listenerDeleted);
                        resolve();
                    }
                }) as any;

                const listenerRetried = (async ({
                    detail: {
                        request: { type: retriedType },
                    },
                }: CustomEvent<JobsRetriedEvent>) => {
                    if (retriedType === type) {
                        document.removeEventListener(JOBS_RETRIED_EVENT, listenerRetried);
                        resolve();
                    }
                }) as any;

                const listenerSkipped = (async ({
                    detail: {
                        request: { type: skippedType },
                    },
                }: CustomEvent<JobsSkippedEvent>) => {
                    if (skippedType === type) {
                        document.removeEventListener(JOBS_RETRIED_EVENT, listenerRetried);
                        resolve();
                    }
                }) as any;

                document.addEventListener(JOBS_DELETED_EVENT, listenerDeleted);
                document.addEventListener(JOBS_RETRIED_EVENT, listenerRetried);
                document.addEventListener(JOBS_SKIPPED_EVENT, listenerSkipped);
            });
        }

        return result;
    } catch (e) {
        // Something went wrong - but this should not be the case as we should always be able to store
        // job results. Let's wait a bit and try again
        await new Promise<void>((resolve) => setTimeout(resolve, 60 * 1000));
        return await saveJobResult(id, process, exception);
    }
}

export { saveJobResult };
