import { FSFileData, CHUNK_SIZE } from "./FSFileData.js";

/**
 * File uploader on FileServer.
 * @returns {{files: Array<FSFileData>, section: string, init(): void, readonly numberOfFiles: number, readonly isUploading: boolean, readonly hasErrors: boolean, readonly allFilesUploaded: boolean, totalChunksNumber(*): number, handleSubmitFile(Event): Promise<void>, handleRetryFile(number): Promise<void>, removeFile(number): void}|boolean|this is FSFileData[]|number|boolean}
 * @constructor
 */
export function FSUploader(){

    return {

        /**
         * Array of uploaded files.
         * @type {Array<FSFileData>}
         */
        files: [],

        /**
         * Section that files must be uploaded.
         * @type {string}
         */
        section: document.querySelector("meta[name='fs-section']")?.content ?? '',

        /**
         * Triggered in fs-upload.blade.php
         * Refresh icons.
         *
         * @param {Array<FSFileData>} oldFilesArray
         */
        init( oldFilesArray ){
            this.$watch('files', () =>{
                this.$nextTick(() => window.refreshIcons(this.$el) )
            })

            if( oldFilesArray !== undefined && oldFilesArray.length > 0)
                this.files = oldFilesArray;
        },

        /**
         * Shortcut to files.length.
         * @returns {number}
         */
        get numberOfFiles(){
            return this.files.length
        },

        /**
         * Look if some files are currently in upload.
         * @returns {boolean}
         */
        get isUploading(){
            return this.files.some(
                file => file.isUploading
            );
        },

        /**
         * Check if some files have an error or not.
         * @returns {boolean}
         */
        get hasErrors(){
            return this.files.some(
                file => file.error
            );
        },

        /**
         * Check if all files are uploaded.
         * True if all files are uploaded or no one.
         *
         * @returns {boolean}
         */
        get allFilesUploaded(){
            return ( this.numberOfFiles === 0 || this.files.every(file => file.done) );
        },

        /**
         * Get total chunks size of a raw file.
         * @param rawFile
         * @returns {number}
         */
        totalChunksNumber( rawFile ){
            return Math.ceil( rawFile.size / CHUNK_SIZE );
        },

        /**
         * Handle file submission.
         * You can submit multiple files at once.
         *
         * @param {Event} e
         * @returns {Promise<void>}
         */
        async handleSubmitFile( e ){

            const RAW_FILES_FROM_EVENT = Array.from( e.target.files );
            e.target.value = ''; // Default.

            for( const RAW_FILE of RAW_FILES_FROM_EVENT ){
                const TOTAL_CHUNKS = this.totalChunksNumber(RAW_FILE);
                let fsData = FSFileData( RAW_FILE.name, TOTAL_CHUNKS, RAW_FILE );

                this.files.push( fsData );
                await this.files[this.files.length - 1].upload(this.section);
            }

        },

        handleDownloadFile( index ){
            let download_url = this.files[index].download_url;
            window.location.href = download_url;
        },

        async handleFileExplorer( index ){

            if( this.files[index].file_explorer_files !== null )
                return;

            let file_explorer_url = this.files[index].file_explorer;

            let response = await fetch(file_explorer_url, { method: 'GET', headers: { 'Content-Type': 'application/json' } });
            let json = await response.json();

            if( !json.files ){
                this.files[index].file_explorer_files = [ "An error occurred during request" ];
                return;
            }

            this.files[index].file_explorer_files = json.files;
        },

        /**
         * Retry file uploading.
         *
         * @param {number} index FSFileData index in this.files.
         * @returns {Promise<void>}
         */
        handleRetryFile( index ){

            const OLDFSFILE = this.files[index];
            let fsData = FSFileData(OLDFSFILE.name, this.totalChunksNumber(OLDFSFILE.rawFile), OLDFSFILE.rawFile );
            this.files[index] = fsData;

            this.files[index].upload(this.section);

        },

        /**
         * Remove a file.
         * @param {number} index FSFileData index in this.files.
         */
        handleRemoveFile( index ){
            if( this.files[index].state === 'archived')
                return;
            this.files.splice(index, 1);
        },

        changeFileState( index, newState ){
            this.files[index].state = newState;
        }

    }
}
