/* BDF - Copyright (c) 2023-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.servlets;

import fr.exemole.bdfserver.conf.WebappDirs;
import fr.exemole.bdfserver.conf.dom.ConfDOMReader;
import fr.exemole.bdfserver.email.SmtpManager;
import fr.exemole.bdfserver.multi.api.MultiConstants;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import javax.servlet.ServletContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import net.fichotheque.SubsetKey;
import net.fichotheque.utils.FichothequeUtils;
import net.mapeadores.util.exceptions.InitException;
import net.mapeadores.util.servlets.ServletUtils;
import net.mapeadores.util.text.StringUtils;
import net.mapeadores.util.xml.DOMUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;


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

    public final static String BDF_CONF_FILE = "bdfConfFile";
    public final static String MULTI_BDF = "multiBdf";
    public final static String DISABLE_LOGIN_PASSWORD = "disableLoginPassword";
    public final static String ALLOW_AUTHENTIFICATION_SHARING = "allowAuthentificationSharing";
    public final static String CENTRAL_SPHERE_LIST = "centralSphereList";
    public final static String SMTP_FILE = "smtpFile";
    public final static String ALLOW_SUBSCRIBE = "allowSubscribe";
    private final ServletContext servletContext;
    private final Map<String, String> parameterMap = new HashMap<String, String>();

    private BDFInitParametersEngine(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    private BDFInitParameters populate() {
        populateFromServletContext();
        Element confRootElement = getConfRootElement();
        populateFromConfFile(confRootElement);
        boolean multiBdf = getBoolean(MULTI_BDF);
        WebappDirs webappDirs = getWebappDirs(confRootElement, multiBdf);
        multiBdf = webappDirs.isMultiBdf();
        boolean loginPasswordDisabled = getBoolean(DISABLE_LOGIN_PASSWORD);
        String authenficationSharing = getAuthentificationSharing(multiBdf);
        boolean subscribeAllowed = getBoolean(ALLOW_SUBSCRIBE);
        List<SubsetKey> centralSphereList = getCentralSphereList(multiBdf);
        SmtpManager smtpManager = getSmtpManager();
        return new BDFInitParameters(webappDirs, loginPasswordDisabled, authenficationSharing, centralSphereList, smtpManager, subscribeAllowed);
    }

    private void populateFromServletContext() {
        for (Enumeration<String> e = servletContext.getInitParameterNames(); e.hasMoreElements();) {
            String name = e.nextElement();
            String value = servletContext.getInitParameter(name);
            value = value.trim();
            if (value.length() > 0) {
                parameterMap.put(name, value);
            }
        }
    }

    private void populateFromConfFile(Element confRootElement) {
        DOMUtils.readChildren(confRootElement, new ParameterConsumer());
        if (!parameterMap.containsKey(MULTI_BDF)) {
            String multiAttribute = confRootElement.getAttribute("multi").trim();
            if (!multiAttribute.isEmpty()) {
                parameterMap.put(MULTI_BDF, multiAttribute);
            }
        }
    }

    private Element getConfRootElement() {
        File confFile = getConfFile();
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(confFile);
            return doc.getDocumentElement();
        } catch (ParserConfigurationException pce) {
            throw new InitException("ParserConfigurationException : " + pce.getMessage());
        } catch (IOException ioe) {
            throw new InitException("IOException when reading bdfConfFile : " + ioe.getMessage() + " / " + confFile.getAbsolutePath());
        } catch (SAXException sax) {
            throw new InitException("XML Error when reading bdfConfFile : " + sax.getMessage() + " / " + confFile.getAbsolutePath());
        }
    }

    private File getConfFile() {
        String bdfConfFileParam = ServletUtils.getInitParameter(servletContext, BDF_CONF_FILE);
        File confFile = null;
        if (bdfConfFileParam != null) {
            confFile = new File(bdfConfFileParam);
            if (!confFile.exists()) {
                confFile = null;
            }
        }
        if (confFile == null) {
            confFile = ServletUtils.getRealFile(servletContext, "/META-INF/bdf-conf.xml");
        }
        if (confFile == null) {
            confFile = ServletUtils.getRealFile(servletContext, "/WEB-INF/bdf-conf.xml");
        }
        if (confFile == null) {
            StringBuilder buf = new StringBuilder();
            buf.append("bdfConfFile does not exist : ");
            if (bdfConfFileParam != null) {
                buf.append(bdfConfFileParam);
                buf.append(" | ");
            }
            buf.append("/WEB-INF|META-INF/bdf-conf.xml");
            throw new InitException(buf.toString());
        }
        if (confFile.isDirectory()) {
            throw new InitException("bdfConfFile is a directory : " + confFile.getAbsolutePath());
        }
        return confFile;
    }

    private boolean getBoolean(String name) {
        String value = parameterMap.get(name);
        if (value == null) {
            return false;
        }
        value = value.trim().toLowerCase();
        switch (value) {
            case "true":
            case "on":
            case "1":
                return true;
            default:
                return false;
        }
    }

    private WebappDirs getWebappDirs(Element confRootElement, boolean multiBdf) {
        String servletContextName = ServletUtils.getServletContextName(servletContext);
        File relativeDirectory = ServletUtils.getRealFile(servletContext, "/");
        if (relativeDirectory == null) {
            throw new InitException("Unable to find webapps root : /");
        }
        ConfDOMReader reader = new ConfDOMReader(servletContextName, relativeDirectory);
        return reader.readConf(confRootElement, multiBdf);
    }

    private String getAuthentificationSharing(boolean multiBdf) {
        if (!multiBdf) {
            return MultiConstants.NONE_SHARING;
        }
        String value = parameterMap.get(ALLOW_AUTHENTIFICATION_SHARING);
        if (value == null) {
            return MultiConstants.NONE_SHARING;
        }
        value = value.trim().toLowerCase();
        switch (value) {
            case MultiConstants.STRICT_SHARING:
                return MultiConstants.STRICT_SHARING;
            case MultiConstants.LAX_SHARING:
            case "true":
            case "on":
            case "1":
                return MultiConstants.LAX_SHARING;
            default:
                return MultiConstants.NONE_SHARING;
        }
    }

    private List<SubsetKey> getCentralSphereList(boolean multiBdf) {
        if (!multiBdf) {
            return FichothequeUtils.EMPTY_SUBSETKEYLIST;
        }
        String value = parameterMap.get(CENTRAL_SPHERE_LIST);
        if (value == null) {
            return FichothequeUtils.EMPTY_SUBSETKEYLIST;
        }
        String[] tokens = StringUtils.getTechnicalTokens(value, true);
        Set<SubsetKey> subsetKeySet = new LinkedHashSet<SubsetKey>();
        for (String token : tokens) {
            try {
                SubsetKey sphereKey = SubsetKey.parse(SubsetKey.CATEGORY_SPHERE, token);
                subsetKeySet.add(sphereKey);
            } catch (ParseException pe) {

            }
        }
        int size = subsetKeySet.size();
        if (size == 0) {
            return FichothequeUtils.EMPTY_SUBSETKEYLIST;
        } else {
            return FichothequeUtils.wrap(subsetKeySet.toArray(new SubsetKey[size]));
        }
    }

    private SmtpManager getSmtpManager() {
        File smtpFile = null;
        String smtpFileParam = parameterMap.get(SMTP_FILE);
        if (smtpFileParam != null) {
            smtpFile = new File(smtpFileParam);
        } else {
            File oldFile = ServletUtils.getRealFile(servletContext, "/WEB-INF/smtp.ini");
            if (oldFile == null) {
                oldFile = ServletUtils.getRealFile(servletContext, "/WEB-INF/bdf-smtp.properties");
            }
            if (oldFile != null) {
                smtpFile = oldFile;
            }
        }
        return new SmtpManager(smtpFile);
    }

    public static BDFInitParameters run(ServletContext servletContext) {
        BDFInitParametersEngine engine = new BDFInitParametersEngine(servletContext);
        return engine.populate();
    }


    private class ParameterConsumer implements Consumer<Element> {

        private ParameterConsumer() {
        }

        @Override
        public void accept(Element element) {
            String tagName = element.getTagName().toLowerCase();
            if (tagName.equals("parameter")) {
                String name = element.getAttribute("name");
                String value = element.getAttribute("value");
                value = value.trim();
                if ((!parameterMap.containsKey(name)) && (value.length() > 0)) {
                    parameterMap.put(name, value);
                }
            }
        }

    }


}
