/* BdfServer_Commands - Copyright (c) 2021-2025 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.commands.corpus;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.EditSession;
import fr.exemole.bdfserver.api.interaction.Domains;
import fr.exemole.bdfserver.tools.instruction.AbstractBdfCommand;
import fr.exemole.bdfserver.tools.instruction.BdfErrors;
import java.util.ArrayList;
import java.util.List;
import net.fichotheque.EditOrigin;
import net.fichotheque.ExistingIdException;
import net.fichotheque.FichothequeEditor;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.CorpusEditor;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.NoMasterIdException;
import net.fichotheque.corpus.fiche.FicheAPI;
import net.fichotheque.history.FicheHistory;
import net.fichotheque.history.HistoryUnit;
import net.fichotheque.history.JunctionHistory;
import net.fichotheque.junction.JunctionChange;
import net.fichotheque.junction.JunctionEditor;
import net.fichotheque.junction.JunctionKey;
import net.fichotheque.junction.JunctionRevision;
import net.fichotheque.junction.Tie;
import net.fichotheque.utils.JunctionUtils;
import net.mapeadores.util.exceptions.ImplementationException;
import net.mapeadores.util.exceptions.ShouldNotOccurException;
import net.mapeadores.util.logging.ErrorMessageException;
import net.mapeadores.util.primitives.FuzzyDate;
import net.mapeadores.util.request.RequestMap;
import net.mapeadores.util.text.StringUtils;


/**
 *
 * @author Vincent Calame
 */
public class FicheRestoreCommand extends AbstractBdfCommand {

    public final static String COMMANDNAME = "FicheRestore";
    private Corpus corpus;
    private int id;
    private FicheHistory ficheHistory;
    private HistoryUnit.Revision lastRevision;
    private String isoTime;

    public FicheRestoreCommand(BdfServer bdfServer, RequestMap requestMap) {
        super(bdfServer, requestMap);
    }

    @Override
    public boolean needSynchronisation() {
        return true;
    }

    @Override
    protected void doCommand() throws ErrorMessageException {
        FicheAPI fiche = corpus.getFicheRevision(id, lastRevision.getName());
        List<JunctionBySubset> list = checkJunctions();
        FicheMeta ficheMeta;
        try (EditSession session = startEditSession(Domains.CORPUS, COMMANDNAME)) {
            FichothequeEditor fichothequeEditor = session.getFichothequeEditor();
            CorpusEditor corpusEditor = fichothequeEditor.getCorpusEditor(corpus);
            JunctionEditor junctionEditor = fichothequeEditor.getJunctionEditor();
            try {
                ficheMeta = corpusEditor.createFiche(id);
            } catch (ExistingIdException eii) {
                throw new ImplementationException(eii);
            } catch (NoMasterIdException nmie) {
                throw new ShouldNotOccurException(nmie);
            }
            corpusEditor.setDate(ficheMeta, FuzzyDate.current(), true);
            corpusEditor.saveFiche(ficheMeta, fiche);
            for (JunctionBySubset croisemetBySubset : list) {
                croisemetBySubset.restoreJunctions(ficheMeta, junctionEditor);
            }
        }
        putResultObject(CORPUS_OBJ, corpus);
        putResultObject(FICHEMETA_OBJ, ficheMeta);
        setDone("_ done.corpus.ficheretrieve");
    }

    @Override
    protected void checkParameters() throws ErrorMessageException {
        corpus = requestHandler.getMandatoryCorpus();
        checkSubsetAdmin(corpus);
        id = requestHandler.getMandatoryId();
        if (corpus.getFicheMetaById(id) != null) {
            throw BdfErrors.error("_ error.existing.id", id);
        }
        ficheHistory = corpus.getFicheHistory(id);
        if (ficheHistory == null) {
            throw BdfErrors.error("_ error.unknown.history", id);
        }
        if (ficheHistory.getFicheUnit().isEmpty()) {
            throw BdfErrors.error("_ error.empty.history", id);
        }
        lastRevision = ficheHistory.getFicheUnit().getMostRecentRevision();
        isoTime = lastRevision.getEditOriginList().get(0).getISOTime();
    }

    private List<JunctionBySubset> checkJunctions() {
        List<JunctionBySubset> result = new ArrayList<JunctionBySubset>();
        for (Subset subset : fichotheque.getThesaurusList()) {
            addJunctionBySubset(result, subset);
        }
        for (Subset subset : fichotheque.getCorpusList()) {
            addJunctionBySubset(result, subset);
        }
        return result;
    }

    private void addJunctionBySubset(List<JunctionBySubset> list, Subset subset) {
        JunctionBySubset jbs = new JunctionBySubset(subset);
        List<JunctionHistory> junctionHistoryList = corpus.getJunctionHistoryList(id, subset.getSubsetKey());
        for (JunctionHistory junctionHistory : junctionHistoryList) {
            HistoryUnit junctionUnit = junctionHistory.getJunctionUnit();
            if (!junctionUnit.isEmpty()) {
                EditOrigin editOrigin = junctionUnit.getMostRecentEditOrigin();
                if (editOrigin.getISOTime().equals(isoTime)) {
                    jbs.add(junctionHistory.getJunctionKey(), junctionUnit.getMostRecentRevision().getName());
                }
            }
        }
        if (!jbs.isEmpty()) {
            list.add(jbs);
        }
    }


    private class JunctionBySubset {

        private final Subset subset;
        private final List<Couple> coupleList = new ArrayList<Couple>();

        private JunctionBySubset(Subset subset) {
            this.subset = subset;
        }

        private boolean isEmpty() {
            return coupleList.isEmpty();
        }

        private void add(JunctionKey junctionKey, String versionName) {
            coupleList.add(new Couple(junctionKey, versionName));
        }

        private void restoreJunctions(FicheMeta ficheMeta, JunctionEditor junctionEditor) {
            for (Couple couple : coupleList) {
                JunctionKey junctionKey = couple.junctionKey;
                String versionName = couple.versionName;
                SubsetItem otherSubsetItem = JunctionUtils.getOther(ficheMeta, junctionKey, subset);
                if (otherSubsetItem != null) {
                    JunctionRevision JunctionRevision = fichotheque.getJunctionRevision(junctionKey, versionName);
                    if (JunctionRevision != null) {
                        junctionEditor.updateJunction(ficheMeta, otherSubsetItem, new RestoreJunctionChange(JunctionRevision));
                    }
                }
            }
        }

    }


    private static class Couple {

        private final JunctionKey junctionKey;
        private final String versionName;

        private Couple(JunctionKey junctionKey, String versionName) {
            this.junctionKey = junctionKey;
            this.versionName = versionName;
        }

    }


    private static class RestoreJunctionChange implements JunctionChange {

        private final JunctionRevision junctionRevision;

        private RestoreJunctionChange(JunctionRevision junctionRevision) {
            this.junctionRevision = junctionRevision;
        }

        @Override
        public List<String> getRemovedModeList() {
            return StringUtils.EMPTY_STRINGLIST;
        }

        @Override
        public List<Tie> getChangedTieList() {
            return junctionRevision.geTieList();
        }

    }

}
