/**
 * Batch progress component.
 *
 * @package EaLab\WP_Deployer
 */

const handle = () => {
    // Retrieve all batch progress elements.
    const batchProgressElements = document.querySelectorAll('deploy-sync-content--batch-progress');

    batchProgressElements.forEach((batchProgressElement) => {
        // Check if the element is already initialized.
        if (batchProgressElement.getAttribute('data-initialized') === 'true') {
            return;
        }

        // Mark the element as initialized.
        batchProgressElement.setAttribute('data-initialized', 'true');

        // Build the batch progress component.
        batchProgressElement.innerHTML = `
            <div class="status-text in-progress">${deploy_sync_content__batchProgress_settings?.text?.loadingQueueStatus ?? 'Loading the queue status…'}</div>
            <div class="output-section"><div>${deploy_sync_content__batchProgress_settings?.text?.batchStepsOutput ?? 'Batch steps output:'}</div><div class="output"></div></div>
        `;

        // The value contains the remaining items in the queue.
        const statusText = batchProgressElement.querySelector('.status-text');

        // The value contains the output of the queue.
        const output = batchProgressElement.querySelector('.output');

        // Perform a periodic AJAX request to retrieve the queue progress data.
        const intervalId = setInterval(() => {
            // Prevent multiple requests from being sent at the same time.
            if (batchProgressElement.getAttribute('data-requesting') === 'true') {
                return;
            }

            batchProgressElement.setAttribute('data-requesting', 'true');

            // This endpoint returns the following data:
            // - progress: Array containing the global progress data.
            //   - pending: The number of steps that are pending.
            // - steps_data: Array of objects containing the data for each step.
            //   - id: The step database ID.
            //   - data_id: The step group ID. Usually the "batch_id" sent in the request URL path.
            //   - data: The step data. This is a JSON-encoded string, which can be decoded using JSON.parse().
            //     Once decoded, the data is an array of text to output.
            const url = batchProgressElement.getAttribute('data-url');

            // Add the nonce header to the request.
            const headers = new Headers();
            headers.append('X-WP-Nonce', deploy_sync_content__batchProgress_settings?.rest?.nonce ?? '');

            // Fetch the data from the endpoint.
            fetch(url, {
                method: 'GET', headers: headers,
            })
                .then((response) => {
                    if (!response.ok) {
                        throw new Error(deploy_sync_content__batchProgress_settings?.text?.networkError ?? 'A network error occurred.');
                    }

                    return response.json();
                })
                .then((data) => {
                    // Append the output of the queue to the output element.
                    const stepsData = data?.steps_data ?? [];
                    const outputData = stepsData.map((stepData) => {
                        const parsed = JSON.parse(stepData.data ?? '[]');

                        // The step messages are nested inside the step_messages key.
                        return parsed?.step_messages ?? [];
                    });

                    const flattenedOutputData = outputData.flat(Infinity);

                    const joined = flattenedOutputData.join('\n');

                    if (joined) {
                        // Check if the output is overflown.
                        const wasOverflown = output.scrollHeight > output.clientHeight;
                        const wasScrolledToBottom = output.scrollHeight - output.scrollTop - output.clientHeight <= 1;

                        // Append if the output is not empty.
                        output.textContent = output.textContent + '\n' + joined;

                        // Scroll to the bottom of the output if the element is already scrolled to the bottom,
                        // or scroll to the bottom if the element was not overflown.
                        if (!wasOverflown || wasScrolledToBottom) {
                            output.scrollTop = output.scrollHeight - output.clientHeight;
                        }
                    }

                    const remainingSteps = data.progress.pending;

                    if (remainingSteps) {
                        // Update the remaining items in the queue.
                        statusText.textContent = (deploy_sync_content__batchProgress_settings?.text?.remainingSteps ?? 'Remaining steps: ') + remainingSteps;
                    } else {
                        statusText.textContent = deploy_sync_content__batchProgress_settings?.text?.queueProcessed ?? 'The queue has been processed.';

                        // Stop the periodic request.
                        clearInterval(intervalId);

                        // Change the class from in-progress to ok.
                        statusText.classList.remove('in-progress');
                        statusText.classList.add('ok');
                    }

                    // Find the last ID returned by the endpoint.
                    const stepsDataLength = stepsData?.length || false;

                    if (stepsDataLength > 0) {
                        // Logically, the last ID is the ID of the last step in the queue
                        // because the steps are ordered by ID.
                        const lastId = stepsData.length ? stepsData[stepsData.length - 1].id : 0;

                        if (lastId) {
                            // The lastId is not 0, so there was a valid response with new steps processed.
                            // Update the URL to include the last ID for the next request.
                            const urlWithLastId = new URL(url);
                            urlWithLastId.searchParams.set('after_id', lastId);
                            batchProgressElement.setAttribute('data-url', urlWithLastId.toString());
                        }
                    }

                    // Allow the next request to be sent.
                    batchProgressElement.setAttribute('data-requesting', 'false');
                })
                .catch((error) => {
                    // Stop the periodic request.
                    clearInterval(intervalId);

                    statusText.textContent = error.message;

                    // Change the class from in-progress to error.
                    statusText.classList.remove('in-progress');
                    statusText.classList.add('not-ok');
                });
        }, 1000);
    });
};

handle();
