/* BdfServer_Multi - Copyright (c) 2021-2023 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.multi.jsonproducers;

import fr.exemole.bdfserver.multi.MultiUtils;
import fr.exemole.bdfserver.multi.api.Multi;
import fr.exemole.bdfserver.multi.api.MultiConstants;
import fr.exemole.bdfserver.multi.api.central.CentralSphere;
import fr.exemole.bdfserver.multi.api.central.CentralUser;
import fr.exemole.bdfserver.multi.jsonproducers.admin.FichothequeArrayJsonProperty;
import fr.exemole.bdfserver.multi.jsonproducers.admin.MultiMetadataJsonProperty;
import fr.exemole.bdfserver.multi.jsonproducers.admin.PersonJsonProperty;
import fr.exemole.bdfserver.multi.jsonproducers.central.CentralSphereMetadataJsonProperty;
import fr.exemole.bdfserver.multi.jsonproducers.central.CentralSphereStatsJsonProperty;
import fr.exemole.bdfserver.multi.jsonproducers.central.CentralUserArrayJsonProperty;
import fr.exemole.bdfserver.multi.jsonproducers.central.CentralUserJsonProperty;
import fr.exemole.bdfserver.multi.jsonproducers.central.PresenceJsonProperty;
import fr.exemole.bdfserver.multi.jsonproducers.central.UserStateJsonProperty;
import fr.exemole.bdfserver.multi.tools.PresenceInfo;
import fr.exemole.bdfserver.tools.instruction.BdfErrors;
import fr.exemole.bdfserver.tools.synthesis.RedacteurSynthesis;
import fr.exemole.bdfserver.tools.synthesis.SphereSynthesis;
import java.text.ParseException;
import java.util.List;
import net.fichotheque.SubsetKey;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.MessageLocalisation;
import net.mapeadores.util.localisation.UserLangContext;
import net.mapeadores.util.logging.CommandMessage;
import net.mapeadores.util.logging.ErrorMessageException;
import net.mapeadores.util.request.RequestMap;
import net.mapeadores.util.text.StringUtils;
import net.mapeadores.util.json.JsonProperty;


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

    private MultiJsonProducerFactory() {

    }

    public static MultiJsonProducer getJsonProducer(Multi multi, String json, RequestMap requestMap, CentralUser authentifiedCentralUser) {
        JsonProducerBuilder builder = new JsonProducerBuilder(multi, requestMap, authentifiedCentralUser);
        builder.appendProperties(json);
        return builder.toJsonProducer();
    }

    public static MultiJsonProducer getJsonProducer(Multi multi, String json, RequestMap requestMap, CommandMessage commandMessage) {
        JsonProducerBuilder builder = new JsonProducerBuilder(multi, requestMap);
        builder.setCommandMessage(commandMessage);
        builder.appendProperties(json);
        return builder.toJsonProducer();
    }


    public static MultiJsonProducer unauthentified(Multi multi) {
        return new MultiJsonProducer(false, multi.getAdminMessageLocalisation());
    }


    private static class JsonProducerBuilder {

        private final Multi multi;
        private final RequestMap requestMap;
        private final MessageLocalisation messageLocalisation;
        private final Lang lang;
        private final MultiJsonProducer jsonProducer;
        private final boolean multiAdminAuthentified;
        private CentralSphere centralSphere = null;
        private CentralUser centralUser = null;
        private boolean withCommandMessage = false;


        private JsonProducerBuilder(Multi multi, RequestMap requestMap) {
            this.multi = multi;
            this.requestMap = requestMap;
            this.messageLocalisation = multi.getAdminMessageLocalisation();
            this.lang = multi.getAdminLang();
            this.jsonProducer = new MultiJsonProducer(true, messageLocalisation);
            this.multiAdminAuthentified = true;
        }

        private JsonProducerBuilder(Multi multi, RequestMap requestMap, CentralUser authentifiedUser) {
            this.multi = multi;
            this.requestMap = requestMap;
            UserLangContext userLangContext = authentifiedUser.getUserLangContext();
            this.messageLocalisation = multi.getMessageLocalisationProvider().getMessageLocalisation(userLangContext.getLangPreference());
            this.lang = userLangContext.getWorkingLang();
            this.jsonProducer = new MultiJsonProducer(true, messageLocalisation);
            this.centralUser = authentifiedUser;
            this.centralSphere = multi.getCentralSphere(authentifiedUser.getLoginKey().getSphereName());
            this.multiAdminAuthentified = false;
        }

        private void setCommandMessage(CommandMessage commandMessage) {
            if (commandMessage != null) {
                jsonProducer.setCommandMessage(commandMessage);
                withCommandMessage = true;
            }
        }

        private CentralSphere getCentralSphere() throws ErrorMessageException {
            if (centralSphere == null) {
                centralSphere = MultiUtils.getCentralSphere(multi, requestMap);
            }
            return centralSphere;
        }

        private CentralUser getCentralUser() throws ErrorMessageException {
            if (centralUser == null) {
                centralUser = MultiUtils.getCentralUser(multi, requestMap);
            }
            return centralUser;
        }

        private void appendProperties(String json) {
            String[] jsonTokens = StringUtils.getTechnicalTokens(json, true);
            boolean done = false;
            try {
                for (String jsonToken : jsonTokens) {
                    if (needMultiAdminAuthentified(jsonToken) && (!multiAdminAuthentified)) {
                        continue;
                    }
                    switch (jsonToken) {
                        case MultiConstants.FICHOTHEQUE_ARRAY_JSON: {
                            jsonProducer.add(new FichothequeArrayJsonProperty(multi, messageLocalisation, lang));
                            done = true;
                            break;
                        }
                        case MultiConstants.MULTIMETADATA_JSON: {
                            jsonProducer.add(new MultiMetadataJsonProperty(multi));
                            done = true;
                            break;
                        }
                        case MultiConstants.PERSON_JSON: {
                            jsonProducer.add(getPersonJsonProperty());
                            done = true;
                            break;
                        }
                        case MultiConstants.CENTRALSPHEREMETADATA_JSON: {
                            jsonProducer.add(new CentralSphereMetadataJsonProperty(getCentralSphere()));
                            done = true;
                            break;
                        }
                        case MultiConstants.CENTRALSPHERESTATS_JSON: {
                            jsonProducer.add(new CentralSphereStatsJsonProperty(multi, getCentralSphere(), lang));
                            done = true;
                            break;
                        }
                        case MultiConstants.USER_JSON: {
                            jsonProducer.add(new CentralUserJsonProperty(getCentralUser()));
                            done = true;
                            break;
                        }
                        case MultiConstants.USER_ARRAY_JSON: {
                            jsonProducer.add(new CentralUserArrayJsonProperty(getCentralSphere()));
                            done = true;
                            break;
                        }
                        case MultiConstants.USERSTATE_JSON: {
                            jsonProducer.add(new UserStateJsonProperty(multi, getCentralUser(), lang));
                            done = true;
                            break;
                        }
                        case MultiConstants.PRESENCE_JSON: {
                            List<PresenceInfo> list = PresenceInfo.scan(multi, getCentralUser(), false);
                            jsonProducer.add(new PresenceJsonProperty(list, lang));
                            done = true;
                            break;
                        }
                        case "":
                        case MultiConstants.PING_JSON: {
                            done = true;
                            break;
                        }
                    }
                }
                if (!done) {
                    if (!withCommandMessage) {
                        throw new ErrorMessageException("_ error.unknown.json", json);
                    }
                }
            } catch (ErrorMessageException eme) {
                jsonProducer.setCommandMessage(eme.getErrorMessage());
            }
        }

        private JsonProperty getPersonJsonProperty() throws ErrorMessageException {
            if (!multi.isPersonManagementAllowed()) {
                throw new ErrorMessageException("_ error.unsupported.personmanagementnotallowed");
            }
            String sphereName = requestMap.getParameter("sphere");
            if (sphereName == null) {
                throw BdfErrors.emptyMandatoryParameter("sphere");
            }
            String login = requestMap.getParameter("login");
            if (login == null) {
                throw BdfErrors.emptyMandatoryParameter("login");
            }
            SubsetKey sphereKey;
            try {
                sphereKey = SubsetKey.parse(SubsetKey.CATEGORY_SPHERE, sphereName.trim());
            } catch (ParseException pe) {
                throw new ErrorMessageException("_ error.unknown.sphere", sphereName);
            }
            SphereSynthesis sphereSynthesis = MultiUtils.getSphereSynthesis(multi, sphereKey);
            if (sphereSynthesis.getFichothequeSize() == 0) {
                throw new ErrorMessageException("_ error.unknown.sphere", sphereName);
            }
            RedacteurSynthesis redacteurSynthesis = sphereSynthesis.getRedacteurSynthesis(login);
            if (redacteurSynthesis == null) {
                throw new ErrorMessageException("_ error.unknown.redacteur", login);
            }
            return new PersonJsonProperty(multi, sphereSynthesis, redacteurSynthesis, multi.getAdminLang());
        }

        private MultiJsonProducer toJsonProducer() {
            return jsonProducer;
        }

    }

    private static boolean needMultiAdminAuthentified(String json) {
        switch (json) {
            case MultiConstants.PRESENCE_JSON:
            case MultiConstants.PING_JSON:
            case "":
                return false;
            default:
                return true;
        }
    }

}
