import { __awaiter } from "tslib";
import { useChunkFinishListener, useChunkStartListener, useItemErrorListener, useItemFinishListener, useRequestPreSend } from '@rpldy/chunked-uploady';
import { useItemProgressListener, useItemStartListener } from '@rpldy/uploady';
import { Endpoints, http } from '@selfcommunity/api-services';
import { useSCContext, useSCUser } from '@selfcommunity/react-core';
import { Logger, resizeImage } from '@selfcommunity/utils';
import React, { useEffect, useRef, useState } from 'react';
import { useIntl } from 'react-intl';
import { SCOPE_SC_UI } from '../../constants/Errors';
import messages from '../../messages/common';
import { md5 } from '../../utils/hash';
export default (props) => {
    // PROPS
    const { type = null, onSuccess = null, onProgress = null, onError = null } = props;
    // REFS
    const firstRender = useRef(true);
    const chunkStateRef = React.useRef({ chunks: {}, setChunk: null, setChunks: null });
    // STATE
    const [chunks, _setChunks] = useState({});
    const setChunk = (chunk) => {
        const _chunks = Object.assign(Object.assign({}, chunkStateRef.current.chunks), { [chunk.id]: Object.assign(Object.assign({}, chunkStateRef.current.chunks[chunk.id]), chunk) });
        _setChunks(_chunks);
        chunkStateRef.current.chunks = _chunks;
    };
    const setChunks = (chunks) => {
        _setChunks(chunks);
        chunkStateRef.current.chunks = chunks;
    };
    // Using refs to have the correct chunks values in the callbacks
    // https://stackoverflow.com/questions/57847594/react-hooks-accessing-up-to-date-state-from-within-a-callback
    chunkStateRef.current = { chunks, setChunk, setChunks };
    // CONTEXT
    const scContext = useSCContext();
    const { refreshSession } = useSCUser();
    // INTL
    const intl = useIntl();
    // component update
    useEffect(() => {
        if (firstRender.current) {
            firstRender.current = false;
            return;
        }
        onProgress && onProgress(chunks);
    }, [chunks]);
    // LISTENERS
    useItemStartListener((item) => {
        if (item.file.type.startsWith('image/')) {
            const reader = new FileReader();
            reader.onload = (e) => {
                chunkStateRef.current.setChunk({ id: item.id, image: e.target.result });
            };
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            reader.readAsDataURL(item.file);
        }
        chunkStateRef.current.setChunk({ id: item.id, ['upload_id']: null, completed: 0, name: item.file.name });
    });
    useItemProgressListener((item) => {
        chunkStateRef.current.setChunk({ id: item.id, completed: item.completed });
    });
    useItemFinishListener((item) => {
        md5(item.file, 2142880, (hash) => {
            const formData = new FormData();
            formData.append('upload_id', chunkStateRef.current.chunks[item.id].upload_id);
            formData.append('type', chunkStateRef.current.chunks[item.id].type);
            formData.append('md5', hash);
            http
                .request({
                url: Endpoints.ComposerChunkUploadMediaComplete.url(),
                method: Endpoints.ComposerChunkUploadMediaComplete.method,
                data: formData,
                headers: { 'Content-Type': 'multipart/form-data' }
            })
                .then((res) => {
                setTimeout(() => {
                    const _chunks = Object.assign({}, chunkStateRef.current.chunks);
                    delete _chunks[item.id];
                    chunkStateRef.current.setChunks(_chunks);
                    onSuccess(res.data);
                }, 0);
            })
                .catch((error) => {
                console.log(error);
                onError(Object.assign({}, chunkStateRef.current.chunks[item.id]), intl.formatMessage(messages.fileUploadErrorGeneric));
                const _chunks = Object.assign({}, chunkStateRef.current.chunks);
                delete _chunks[item.id];
                chunkStateRef.current.setChunks(_chunks);
            });
        });
    });
    useItemErrorListener((item) => {
        onError(Object.assign({}, chunkStateRef.current.chunks[item.id]), intl.formatMessage(messages.fileUploadErrorGeneric));
        const _chunks = Object.assign({}, chunkStateRef.current.chunks);
        delete _chunks[item.id];
        chunkStateRef.current.setChunks(_chunks);
    });
    useChunkStartListener((data) => {
        const res = {
            url: `${scContext.settings.portal}${Endpoints.ComposerChunkUploadMedia.url()}`,
            sendOptions: {
                paramName: 'image',
                method: Endpoints.ComposerChunkUploadMedia.method,
                formatServerResponse: (response, status, _headers) => __awaiter(void 0, void 0, void 0, function* () {
                    if (status === 401) {
                        yield refreshSession();
                    }
                    return Promise.resolve(JSON.parse(response));
                })
            }
        };
        if (chunkStateRef.current.chunks[data.item.id].upload_id) {
            res.sendOptions.params = { ['upload_id']: chunkStateRef.current.chunks[data.item.id].upload_id };
        }
        else {
            chunkStateRef.current.setChunk({ id: data.item.id, type: type || data.sendOptions.paramName });
        }
        return res;
    });
    useChunkFinishListener((data) => __awaiter(void 0, void 0, void 0, function* () {
        const _data = yield data.uploadData.response.data;
        chunkStateRef.current.setChunk({ id: data.item.id, ['upload_id']: _data.upload_id });
    }));
    useRequestPreSend(({ items, options }) => __awaiter(void 0, void 0, void 0, function* () {
        const destination = ['JWT', 'OAuth'].includes(scContext.settings.session.type)
            ? {
                headers: { Authorization: `Bearer ${scContext.settings.session.authToken.accessToken}` }
            }
            : {};
        if (items.length == 0) {
            return Promise.resolve({ options, destination });
        }
        //returned object can be wrapped with a promise
        return new Promise((resolve) => {
            Promise.all(items.map((item) => __awaiter(void 0, void 0, void 0, function* () {
                return Object.assign(Object.assign({}, item), { file: item.file.type.startsWith('image/') && item.file.type !== 'image/gif' ? yield resizeImage(item.file) : item.file });
            })))
                .then((items) => {
                resolve({
                    items: items,
                    options: {
                        inputFieldName: options.inputFieldName,
                        destination
                    }
                });
            })
                .catch((error) => {
                Logger.error(error, SCOPE_SC_UI);
                resolve({
                    options: {
                        inputFieldName: options.inputFieldName,
                        destination
                    }
                });
            });
        });
    }));
    return null;
};
