/* BdfServer - Copyright (c) 2026 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package fr.exemole.bdfserver.tools.filesupload;

import fr.exemole.bdfserver.api.BdfServer;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.utils.AddendaUtils;
import net.mapeadores.util.primitives.FileLength;
import net.mapeadores.util.request.FileValue;
import net.mapeadores.util.text.FileName;


/**
 *
 * @author Vincent Calame
 */
public final class FilesUploadEngine {

    public final static String UNIQUE_MODE = "unique";
    public final static String MULTI_MODE = "multi";
    private final static FileName DEFAULT_FILENAME = FileName.build("file.octetstream");
    private final BdfServer bdfServer;
    private final ResultBuilder resultBuilder;

    private FilesUploadEngine(BdfServer bdfServer, ResultBuilder resultBuilder) {
        this.bdfServer = bdfServer;
        this.resultBuilder = resultBuilder;
    }

    public void addFileUpload(FileValue fileValue) throws IOException {
        if (fileValue.length() < 1) {
            fileValue.free();
            return;
        }
        String extension = resultBuilder.setCurrent(fileValue.getName());
        File tmpFile;
        try {
            tmpFile = FilesUploadUtils.saveTmpFile(bdfServer, fileValue, extension);
        } finally {
            fileValue.free();
        }
        resultBuilder.addTmpFile(tmpFile.getName(), new FileLength(tmpFile.length()));
    }

    public FilesUploadResult getResult() {
        return resultBuilder.getResult();
    }

    public static String checkMode(String mode) {
        switch (mode) {
            case UNIQUE_MODE:
                return UNIQUE_MODE;
            case MULTI_MODE:
                return MULTI_MODE;
            default:
                throw new IllegalArgumentException("Unknown mode");
        }
    }

    public static FilesUploadEngine newInstance(BdfServer bdfServer, String mode) {
        switch (mode) {
            case UNIQUE_MODE:
                return new FilesUploadEngine(bdfServer, new UniqueResultBuilder());
            case MULTI_MODE:
                return new FilesUploadEngine(bdfServer, new MultiResultBuilder());
            default:
                throw new IllegalArgumentException("Unknown mode: " + mode);
        }
    }


    private static abstract class ResultBuilder {

        protected String current;
        protected String currentExtension;
        protected String currentOriginalBasename;
        protected String currentNormalizedBasename;

        protected ResultBuilder() {

        }

        public abstract FilesUploadResult getResult();

        private String setCurrent(String current) {
            this.current = current;
            FileName fileName;
            try {
                fileName = FileName.parse(current, "octetstream");
            } catch (ParseException pe) {
                fileName = DEFAULT_FILENAME;
            }
            currentExtension = fileName.getExtension().toLowerCase();
            if (!AddendaUtils.testExtension(currentExtension)) {
                currentExtension = "octetstream";
            }
            currentOriginalBasename = fileName.getBasename();
            currentNormalizedBasename = AddendaUtils.checkNormalization(currentOriginalBasename);
            return currentExtension;
        }

        protected void addTmpFile(String tmpFileName, FileLength tmpFileLength) {
            addTmpFileInfo(new TmpFileInfo(currentExtension, tmpFileName, currentOriginalBasename, currentNormalizedBasename));
        }

        protected abstract void addTmpFileInfo(TmpFileInfo tmpFileInfo);

    }


    private static class UniqueResultBuilder extends ResultBuilder {

        private final Map<String, TmpFileInfo> extensionMap = new LinkedHashMap<String, TmpFileInfo>();
        private final List<UploadError> uploadErrorList = new ArrayList<UploadError>();

        private UniqueResultBuilder() {

        }

        @Override
        public FilesUploadResult getResult() {
            List<TmpFileInfo> list = FilesUploadUtils.wrap(extensionMap.values().toArray(new TmpFileInfo[extensionMap.size()]));
            return new FilesUploadResult.Unique(list, uploadErrorList);
        }

        @Override
        protected void addTmpFileInfo(TmpFileInfo tmpFileInfo) {
            String extension = tmpFileInfo.getExtension();
            if (!extensionMap.containsKey(extension)) {
                extensionMap.put(extension, tmpFileInfo);
            } else {
                uploadErrorList.add(UploadError.build(tmpFileInfo, "_ error.unsupported.extensionrepeated", extension));
            }
        }

    }


    private static class MultiResultBuilder extends ResultBuilder {

        private final SortedMap<String, TmpFileGroup> groupMap = new TreeMap<String, TmpFileGroup>();
        private final List<UploadError> uploadErrorList = new ArrayList<UploadError>();

        private MultiResultBuilder() {

        }

        @Override
        public FilesUploadResult getResult() {
            List<TmpFileGroup> groupList = FilesUploadUtils.wrap(groupMap.values().toArray(new TmpFileGroup[groupMap.size()]));
            return new FilesUploadResult.Multi(groupList, uploadErrorList);
        }

        @Override
        protected void addTmpFileInfo(TmpFileInfo tmpFileInfo) {
            String originalBaseName = tmpFileInfo.getOriginalBasename();
            TmpFileGroup currentGroup = groupMap.get(originalBaseName);
            if (currentGroup == null) {
                currentGroup = new TmpFileGroup(tmpFileInfo);
                groupMap.put(originalBaseName, currentGroup);
            } else {
                if (currentGroup.containsExtension(tmpFileInfo.getExtension())) {
                    uploadErrorList.add(UploadError.build(tmpFileInfo, "_ error.unsupported.filerepeated", tmpFileInfo.getOriginalBasename() + "." + tmpFileInfo.getExtension()));
                } else {
                    currentGroup.addTmpFileInfo(tmpFileInfo);
                }
            }

        }

    }


}
