/* 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.Fiche;
import net.fichotheque.croisement.CroisementChange;
import net.fichotheque.croisement.CroisementEditor;
import net.fichotheque.croisement.CroisementKey;
import net.fichotheque.croisement.CroisementRevision;
import net.fichotheque.croisement.Lien;
import net.fichotheque.history.CroisementHistory;
import net.fichotheque.history.FicheHistory;
import net.fichotheque.history.HistoryUnit;
import net.fichotheque.utils.CroisementUtils;
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 {
        Fiche fiche = corpus.getFicheRevision(id, lastRevision.getName());
        List<CroisementBySubset> list = checkCroisements();
        FicheMeta ficheMeta;
        try (EditSession session = startEditSession(Domains.CORPUS, COMMANDNAME)) {
            FichothequeEditor fichothequeEditor = session.getFichothequeEditor();
            CorpusEditor corpusEditor = fichothequeEditor.getCorpusEditor(corpus);
            CroisementEditor croisementEditor = fichothequeEditor.getCroisementEditor();

            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 (CroisementBySubset croisemetBySubset : list) {
                croisemetBySubset.restoreCroisements(ficheMeta, croisementEditor);
            }
        }
        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<CroisementBySubset> checkCroisements() {
        List<CroisementBySubset> result = new ArrayList<CroisementBySubset>();
        for (Subset subset : fichotheque.getThesaurusList()) {
            addCroisementBySubset(result, subset);
        }
        for (Subset subset : fichotheque.getCorpusList()) {
            addCroisementBySubset(result, subset);
        }
        return result;
    }

    private void addCroisementBySubset(List<CroisementBySubset> list, Subset subset) {
        CroisementBySubset cbt = new CroisementBySubset(subset);
        List<CroisementHistory> croisementHistoryList = corpus.getCroisementHistoryList(id, subset.getSubsetKey());
        for (CroisementHistory croisementHistory : croisementHistoryList) {
            HistoryUnit croisementUnit = croisementHistory.getCroisementUnit();
            if (!croisementUnit.isEmpty()) {
                EditOrigin editOrigin = croisementUnit.getMostRecentEditOrigin();
                if (editOrigin.getISOTime().equals(isoTime)) {
                    cbt.add(croisementHistory.getCroisementKey(), croisementUnit.getMostRecentRevision().getName());
                }
            }
        }
        if (!cbt.isEmpty()) {
            list.add(cbt);
        }
    }


    private class CroisementBySubset {

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

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

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

        private void add(CroisementKey croisementKey, String versionName) {
            coupleList.add(new Couple(croisementKey, versionName));
        }

        private void restoreCroisements(FicheMeta ficheMeta, CroisementEditor croisementEditor) {
            for (Couple couple : coupleList) {
                CroisementKey croisementKey = couple.croisementKey;
                String versionName = couple.versionName;
                SubsetItem otherSubsetItem = CroisementUtils.getOther(ficheMeta, croisementKey, subset);
                if (otherSubsetItem != null) {
                    CroisementRevision croisementRevision = fichotheque.getCroisementRevision(croisementKey, versionName);
                    if (croisementRevision != null) {
                        croisementEditor.updateCroisement(ficheMeta, otherSubsetItem, new RestoreCroisementChange(croisementRevision));
                    }
                }
            }
        }

    }


    private static class Couple {

        private final CroisementKey croisementKey;
        private final String versionName;

        private Couple(CroisementKey croisementKey, String versionName) {
            this.croisementKey = croisementKey;
            this.versionName = versionName;
        }

    }


    private static class RestoreCroisementChange implements CroisementChange {

        private final CroisementRevision croisementRevision;

        private RestoreCroisementChange(CroisementRevision croisementRevision) {
            this.croisementRevision = croisementRevision;
        }

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

        @Override
        public List<Lien> getChangedLienList() {
            return croisementRevision.getLienList();
        }

    }

}
