
const getChunks = (file, chunkSize) => {
    const chunks = [];
    let start = 0;
    let end = Math.min(chunkSize, file.size);

    while (start < end)
    {
        chunks.push(file.slice(start, end));
        start = end;
        end = Math.min(start + chunkSize, file.size);
    }

    return chunks;
};

const uploadChunk = (url, chunk, headers) => {
    return fetch(url, {
        method: 'POST',
        body: chunk,
        headers,
    }).then(response => response.json()).then(json => {
        if(json.result == 'fail') return Promise.reject(json);
        return new Promise(resolve => setTimeout(() => resolve(json), 100)); // 增加延迟，给后端处理session的时间
    });
}

function uploadFileByChunk(url, file, chunkSize = 1024 * 1024, onProgress = null)
{
    const chunks = getChunks(file, chunkSize);
    let i = 0;

    return new Promise((resolve, reject) => {
        const uploadNextChunk = () => {
            if(i >= chunks.length)
            {
                if(typeof onProgress === 'function') onProgress(1);
                resolve();
                return;
            }

            const headers = {
                'X-CHUNK-INDEX': i,
                'X-TOTAL-CHUNKS': chunks.length,
                'X-FILENAME': encodeURIComponent(file.name),
                'X-FILESIZE': file.size,
            };
            uploadChunk(url, chunks[i], headers)
                .then(() => {
                    i++;
                    if(typeof onProgress === 'function') onProgress(i / chunks.length);
                    uploadNextChunk();
                })
                .catch(reject);
        };

        uploadNextChunk();
    });
};

/**
 * 控制并发上传数量
 * @param {File[]} files
 * @param {string} uploadUrl
 * @param {number} chunkSize
 * @param {number} maxConcurrency
 */
const uploadFilesWithConcurrency = async (files, uploadUrl, chunkSize, maxConcurrency = 2) => {
    const results = [];
    const inProgress = new Set();

    const uploadFile = async (file, fileIndex) => {
        return new Promise((resolve, reject) => {
            uploadFileByChunk(uploadUrl, file, chunkSize, function(progress) {
            }).then(() => {
                resolve({ file, fileIndex, success: true });
            }).catch(error => {
                reject({ file, fileIndex, error });
            });
        });
    };

    const processQueue = async () => {
        for (let i = 0; i < files.length; i++) {
            if (inProgress.size >= maxConcurrency) {
                await Promise.race(inProgress);
            }

            const file = files[i];
            const uploadPromise = uploadFile(file, i).finally(() => {
                inProgress.delete(uploadPromise);
            });

            inProgress.add(uploadPromise);
            results.push(uploadPromise);
        }

        await Promise.all(results);
    };

    await processQueue();
    return results;
};

window.uploadImages = function(selector, options, $uploadBtn)
{
    const $fileBox = $(selector);
    const fileBox  = $fileBox.zui();
    const files    = fileBox.$.files;

    if(!files.length)
    {
        if(!$uploadBtn.hasClass('disabled')) zui.Modal.alert(options.errorUploadEmpty);
        return;
    }

    const progressMap = new Map();
    let uploadedCount = 0;
    $uploadBtn.attr('disabled', 'disabled');
    $uploadBtn.find('.as-progress').text(' 0%');
    const render = () =>
    {
        fileBox.render({disabled: true, itemProps: (file) =>
        {
            const progress = progressMap.get(file.file);
            if(progress === undefined) return {};
            if(progress === 1) return {icon: 'check text-success absolute right-0 bottom-2'};

            return {title: Math.round(progress * 100) + '% | ' + file.name};
        }});
    };

    render();

    /* 串行上传文件 */
    const uploadFilesSequentially = async (fileIndex = 0) => {
        if (fileIndex >= files.length) {
            const modalID = $uploadBtn.closest('.modal').attr('id');
            zui.Modal.hide('#' + modalID);

            const $form  = $('body').find('form.form-batch[data-zui-batchform]');
            const $modal = $form.closest('.modal')
            if($modal.length > 0)
            {
                $.ajax(
                {
                    url: options.locateUrl,
                    headers:{'X-Zui-Modal': true},
                    dataType: 'json',
                    success: function(data)
                    {
                        $modal.find('[data-zui-ajaxform]').zui('ajaxform').destroy();
                        $modal.find('[data-zui-batchform]').zui('batchForm').destroy();
                        setTimeout(function()
                        {
                            loadModal(data.load, $modal.attr('id'));
                        }, 500);
                    }
                });
                return;
            }
            loadPage(options.locateUrl);
            return;
        }

        const file = files[fileIndex];
        try {
            await uploadFileByChunk(options.uploadUrl, file, options.chunkSize, function(progress)
            {
                progressMap.set(file, progress);
                render();
                $uploadBtn.find('.as-progress').text(' ' + Math.round((uploadedCount + progress) / files.length * 100) + '%' );
            });

            uploadedCount++;
            uploadFilesSequentially(fileIndex + 1);
        } catch (error) {
            $uploadBtn.removeAttr('disabled');
            $uploadBtn.find('.as-progress').text('');
            fileBox.render({disabled: false});
            if(typeof(error.message) != 'undefined') zui.Modal.alert(error.message);
        }
    };

    uploadFilesSequentially();
};
