/* BdfServer_Multi - 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.multi.impl;

import fr.exemole.bdfserver.api.storage.BdfStorageException;
import fr.exemole.bdfserver.multi.api.central.CentralUser;
import fr.exemole.bdfserver.multi.api.central.CentralUserEditor;
import fr.exemole.bdfserver.multi.xml.CentralUserXMLPart;
import fr.exemole.bdfserver.storage.directory.StorageDirectory;
import fr.exemole.bdfserver.tools.storage.StorageFile;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Locale;
import net.fichotheque.EditOrigin;
import net.fichotheque.FichothequeConstants;
import net.fichotheque.sphere.LoginKey;
import net.mapeadores.util.attr.Attribute;
import net.mapeadores.util.attr.AttributeKey;
import net.mapeadores.util.attr.AttributeUtils;
import net.mapeadores.util.attr.Attributes;
import net.mapeadores.util.attr.AttributesCache;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LangPreference;
import net.mapeadores.util.localisation.LocalisationUtils;
import net.mapeadores.util.localisation.UserLangContext;
import net.mapeadores.util.models.EmailCore;
import net.mapeadores.util.models.PersonCore;
import net.mapeadores.util.models.PersonCoreUtils;
import net.mapeadores.util.xml.AppendableXMLWriter;
import net.mapeadores.util.xml.XMLUtils;


/**
 *
 * @author Vincent Calame
 */
class CentralUserImpl implements CentralUser {

    private final LoginKey loginKey;
    private String encryptedPassword = "";
    private UserLangContext userLangContext = LocalisationUtils.DEFAULT_LANGCONTEXT;
    private Locale customFormatLocale;
    private LangPreference customLangPreference;
    private PersonCore person = PersonCoreUtils.EMPTY_PERSONCORE;
    private InternalEmail email = null;
    private String status = FichothequeConstants.ACTIVE_STATUS;
    private AttributesCache attributesCache = null;

    CentralUserImpl(LoginKey loginKey) {
        this.loginKey = loginKey;
    }

    @Override
    public LoginKey getLoginKey() {
        return loginKey;
    }

    @Override
    public String getStatus() {
        return status;
    }

    @Override
    public UserLangContext getUserLangContext() {
        return userLangContext;
    }

    @Override
    public PersonCore getPerson() {
        return person;
    }

    @Override
    public EmailCore getEmail() {
        return email;
    }

    @Override
    public String getEncryptedPassword() {
        return encryptedPassword;
    }

    @Override
    public Attributes getAttributes() {
        if (attributesCache == null) {
            return AttributeUtils.EMPTY_ATTRIBUTES;
        }
        return attributesCache.getAttributes();
    }

    @Override
    public Locale getCustomFormatLocale() {
        return customFormatLocale;
    }

    @Override
    public LangPreference getCustomLangPreference() {
        return customLangPreference;
    }

    private synchronized boolean innerRemoveAttribute(AttributeKey attributeKey) {
        if (attributesCache == null) {
            return false;
        }
        return attributesCache.removeAttribute(attributeKey);
    }

    private synchronized boolean innerPutAttribute(Attribute attribute) {
        if (attributesCache == null) {
            attributesCache = new AttributesCache();
        }
        return attributesCache.putAttribute(attribute);
    }

    private boolean setEmail(EmailCore newEmail) {
        if (newEmail == null) {
            if (email != null) {
                email = null;
                return true;
            } else {
                return false;
            }
        }
        String realName = newEmail.getRealName();
        if (realName.equals(person.toStandardStyle())) {
            realName = "";
        }
        InternalEmail newInternalEmail = new InternalEmail(newEmail.getAddrSpec(), realName);
        if (email == null) {
            email = newInternalEmail;
            return true;
        }
        if (email.equals(newInternalEmail)) {
            return false;
        } else {
            email = newInternalEmail;
            return true;
        }
    }

    CentralUserEditorImpl getCentalUserEditor(EditOrigin editOrigin) {
        return new CentralUserEditorImpl(this, editOrigin);
    }


    static class CentralUserEditorImpl implements CentralUserEditor {

        private final CentralUserImpl centralUserImpl;
        private final EditOrigin editOrigin;
        private boolean withChanges = false;

        private CentralUserEditorImpl(CentralUserImpl centralUserImpl, EditOrigin editOrigin) {
            this.centralUserImpl = centralUserImpl;
            this.editOrigin = editOrigin;
        }

        @Override
        public boolean setStatus(String status) {
            String currentStatus = centralUserImpl.getStatus();
            if (!status.equals(currentStatus)) {
                centralUserImpl.status = FichothequeConstants.checkRedacteurStatus(status);
                fireChange();
                return true;
            } else {
                return false;
            }
        }

        @Override
        public boolean setPerson(PersonCore person) {
            PersonCore currentPerson = centralUserImpl.person;
            if (!(PersonCoreUtils.areEqual(person, currentPerson))) {
                centralUserImpl.person = PersonCoreUtils.clonePersonCore(person);
                fireChange();
                return true;
            } else {
                return false;
            }
        }

        @Override
        public boolean setEmail(EmailCore newEmail) {
            boolean done = centralUserImpl.setEmail(newEmail);
            if (done) {
                fireChange();
            }
            return done;
        }

        @Override
        public boolean setLangContext(Lang workingLang, Locale customFormatLocale, LangPreference customLangPreference) {
            centralUserImpl.userLangContext = LocalisationUtils.toUserLangContext(workingLang, customFormatLocale, customLangPreference);
            centralUserImpl.customFormatLocale = customFormatLocale;
            centralUserImpl.customLangPreference = customLangPreference;
            fireChange();
            return true;
        }

        @Override
        public boolean setEncryptedPassword(String encryptedPassword) {
            if (encryptedPassword == null) {
                encryptedPassword = "";
            }
            if (!encryptedPassword.equals(centralUserImpl.encryptedPassword)) {
                centralUserImpl.encryptedPassword = encryptedPassword;
                fireChange();
                return true;
            } else {
                return false;
            }
        }

        @Override
        public boolean putAttribute(Attribute attribute) {
            boolean done = centralUserImpl.innerPutAttribute(attribute);
            if (done) {
                fireChange();
            }
            return done;
        }

        @Override
        public boolean removeAttribute(AttributeKey attributeKey) {
            boolean done = centralUserImpl.innerRemoveAttribute(attributeKey);
            if (done) {
                fireChange();
            }
            return done;
        }

        boolean isWithChanges() {
            return withChanges;
        }

        void fireChange() {
            withChanges = true;
        }

        boolean saveChanges(StorageDirectory storageDirectory) {
            if (isWithChanges()) {
                StorageFile storageFile = MultiStorageFileCatalog.centralUser(storageDirectory, centralUserImpl.getLoginKey());
                try (BufferedWriter buf = storageFile.archiveAndGetWriter(editOrigin)) {
                    AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(buf);
                    xmlWriter.appendXMLDeclaration();
                    CentralUserXMLPart part = new CentralUserXMLPart(xmlWriter);
                    part.appendCentralUser(centralUserImpl);
                } catch (IOException ioe) {
                    throw new BdfStorageException(ioe);
                }
                return true;
            } else {
                return false;
            }
        }

    }


    private class InternalEmail implements EmailCore {

        private final String addrSpec;
        private final String realName;

        private InternalEmail(String addrSpec, String realName) {
            this.addrSpec = addrSpec;
            this.realName = realName;
        }

        @Override
        public String getAddrSpec() {
            return addrSpec;
        }

        @Override
        public String getRealName() {
            return realName;
        }

        @Override
        public String getComputedRealName() {
            if (realName.isEmpty()) {
                return person.toStandardStyle();
            } else {
                return realName;
            }
        }

        @Override
        public int hashCode() {
            return addrSpec.hashCode() + realName.hashCode();
        }

        @Override
        public boolean equals(Object other) {
            if (other == null) {
                return false;
            }
            if (other == this) {
                return true;
            }
            if (other.getClass() != this.getClass()) {
                return false;
            }
            InternalEmail otherEmail = (InternalEmail) other;
            return ((otherEmail.addrSpec.equals(this.addrSpec)) && (otherEmail.realName.equals(this.realName)));
        }

    }

}
