/* BdfServer_Email - Copyright (c) 2011-2020 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.email;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.BdfServerConstants;
import fr.exemole.bdfserver.api.interaction.domains.MailingDomain;
import fr.exemole.bdfserver.api.users.BdfUser;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.fichotheque.Fichotheque;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.namespaces.BdfSpace;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.utils.SphereUtils;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.attr.Attribute;
import net.mapeadores.util.models.EmailCore;
import net.mapeadores.util.models.EmailCoreUtils;
import net.mapeadores.util.text.StringUtils;


/**
 *
 * @author Vincent Calame
 */
public class EmailBuffer {

    public final static String TO_FIELD = "to";
    public final static String CC_FIELD = "cc";
    public final static String BCC_FIELD = "bcc";
    public final static String REPLYTO_FIELD = "replyto";
    private final static List<WrongAddress> EMPTY_WRONGLIST = Collections.emptyList();
    private final static List<ValidAddress> EMPTY_VALIDLIST = Collections.emptyList();
    private final EmailCore redacteurEmail;
    private final EmailCore fromEmail;
    private final Map<String, EmailCore> replytoMap = new LinkedHashMap<String, EmailCore>();
    private final String sendType;
    private final Object sendObject;
    private final Map<String, List<ValidAddress>> validMap = new HashMap<String, List<ValidAddress>>();
    private final Map<String, List<WrongAddress>> wrongMap = new HashMap<String, List<WrongAddress>>();
    private boolean withRedacteurBcc;
    private boolean withRedacteurReplyTo;
    private String subject;
    private String message;
    private FicheAttachmentParameters ficheAttachmentParameters = getDefaultFicheAttachmentParameters();

    public EmailBuffer(EmailCore redacteurEmail, EmailCore fromEmail, String sendType, Object sendObject) {
        this.redacteurEmail = redacteurEmail;
        this.fromEmail = fromEmail;
        this.sendType = sendType;
        this.sendObject = sendObject;
    }

    public String getSendType() {
        return sendType;
    }

    /**
     * La nature de l'objet varie en fonction de sendType;
     *
     * @return
     */
    @Nullable
    public Object getSendObject() {
        return sendObject;
    }

    public boolean isWithRedacteurBcc() {
        return withRedacteurBcc;
    }


    public EmailBuffer setWithRedacteurBcc(boolean withRedacteurBcc) {
        this.withRedacteurBcc = withRedacteurBcc;
        return this;
    }

    public boolean isWithRedacteurReplyTo() {
        return withRedacteurBcc;
    }

    public EmailBuffer setWithRedacteurReplyTo(boolean withRedacteurReplyTo) {
        this.withRedacteurReplyTo = withRedacteurReplyTo;
        return this;
    }

    public String getSubject() {
        return subject;
    }

    public EmailBuffer setSubject(String subject) {
        if (subject != null) {
            subject = StringUtils.cleanString(subject);
            if (subject.length() == 0) {
                subject = null;
            }
        }
        this.subject = subject;
        return this;
    }

    public String getMessage() {
        return message;
    }

    public EmailBuffer setMessage(String message) {
        if (message != null) {
            message = message.trim();
            if (message.length() == 0) {
                message = null;
            }
        }
        this.message = message;
        return this;
    }

    public boolean isWithoutField(String fieldName) {
        return ((!validMap.containsKey(fieldName)) && (!wrongMap.containsKey(fieldName)));
    }

    public boolean hasWrongAddresses() {
        return !wrongMap.isEmpty();
    }

    public List<WrongAddress> getWrongAddressList(String fieldName) {
        List<WrongAddress> list = wrongMap.get(fieldName);
        if (list == null) {
            return EMPTY_WRONGLIST;
        } else {
            return list;
        }
    }

    public List<ValidAddress> getValidAddressList(String fieldName) {
        List<ValidAddress> list = validMap.get(fieldName);
        if (list == null) {
            return EMPTY_VALIDLIST;
        } else {
            return list;
        }
    }

    public EmailBuffer addValidAddress(String fieldName, ValidAddress validAddress) {
        List<ValidAddress> list = validMap.get(fieldName);
        if (list == null) {
            list = new ArrayList<ValidAddress>();
            validMap.put(fieldName, list);
        }
        list.add(validAddress);
        return this;
    }

    public EmailBuffer addWrongAddress(String fieldName, WrongAddress wrongAddress) {
        List< WrongAddress> list = wrongMap.get(fieldName);
        if (list == null) {
            list = new ArrayList< WrongAddress>();
            wrongMap.put(fieldName, list);
        }
        list.add(wrongAddress);
        return this;
    }

    public EmailBuffer parseToArray(Fichotheque fichotheque, String fieldName, List<String> valueList, SubsetKey defaultSphereKey) {
        for (String value : valueList) {
            int idx = value.indexOf('@');
            if (idx == -1) {
                try {
                    Redacteur redacteur = SphereUtils.parse(fichotheque, value, defaultSphereKey);
                    EmailCore emailCore = redacteur.getEmailCore();
                    if (emailCore == null) {
                        addWrongAddress(fieldName, WrongAddress.redacteurNoAdressError(value));
                    } else {
                        addValidAddress(fieldName, new ValidAddress(emailCore, value));
                    }
                } catch (SphereUtils.RedacteurLoginException rle) {
                    addWrongAddress(fieldName, WrongAddress.redacteurUnknownError(value));
                }
            } else {
                try {
                    EmailCore emailCore = EmailCoreUtils.parse(value);
                    addValidAddress(fieldName, new ValidAddress(emailCore, value));
                } catch (ParseException pe2) {
                    addWrongAddress(fieldName, WrongAddress.malformedAddressError(value));
                }
            }
        }
        return this;
    }

    public EmailCore getFromEmail() {
        return fromEmail;
    }

    public EmailCore getRedacteurEmail() {
        return redacteurEmail;
    }

    public Collection<EmailCore> getReplytoCollection() {
        List<EmailCore> list = new ArrayList<EmailCore>();
        for (ValidAddress validAddress : getValidAddressList(REPLYTO_FIELD)) {
            list.add(validAddress.getEmailCore());
        }
        if (withRedacteurReplyTo) {
            list.add(redacteurEmail);
        }
        return list;
    }

    public Collection<EmailCore> getToCollection() {
        List<EmailCore> list = new ArrayList<EmailCore>();
        for (ValidAddress validAddress : getValidAddressList(TO_FIELD)) {
            list.add(validAddress.getEmailCore());
        }
        return list;
    }

    public Collection<EmailCore> getCcCollection() {
        List<EmailCore> list = new ArrayList<EmailCore>();
        for (ValidAddress validAddress : getValidAddressList(CC_FIELD)) {
            list.add(validAddress.getEmailCore());
        }
        return list;
    }

    public Collection<EmailCore> getBccCollection() {
        List<EmailCore> list = new ArrayList<EmailCore>();
        for (ValidAddress validAddress : getValidAddressList(BCC_FIELD)) {
            list.add(validAddress.getEmailCore());
        }
        if (withRedacteurBcc) {
            list.add(redacteurEmail);
        }
        return list;
    }

    public FicheAttachmentParameters getFicheAttachmentParameters() {
        return ficheAttachmentParameters;
    }

    public EmailBuffer setFicheAttachmentParameters(FicheAttachmentParameters ficheAttachmentParameters) {
        if (ficheAttachmentParameters == null) {
            this.ficheAttachmentParameters = getDefaultFicheAttachmentParameters();
        } else {
            this.ficheAttachmentParameters = ficheAttachmentParameters;
        }
        return this;
    }


    public static EmailBuffer buildForFiche(BdfServer bdfServer, BdfUser bdfUser, FicheMeta ficheMeta) {
        if (ficheMeta == null) {
            throw new IllegalArgumentException("ficheMeta is  null");
        }
        return build(bdfServer, bdfUser, MailingDomain.FICHE_SENDTYPE, ficheMeta);
    }

    public static EmailBuffer buildForCompilation(BdfServer bdfServer, BdfUser bdfUser) {
        return build(bdfServer, bdfUser, MailingDomain.COMPILATION_SENDTYPE, null);
    }

    public static EmailBuffer buildForSelection(BdfServer bdfServer, BdfUser bdfUser, Set<FicheMeta> ficheMetaSet) {
        return build(bdfServer, bdfUser, MailingDomain.SELECTION_SENDTYPE, ficheMetaSet);
    }

    public static EmailBuffer buildForTableExport(BdfServer bdfServer, BdfUser bdfUser, TableExportEmail tableExportEmail) {
        return build(bdfServer, bdfUser, MailingDomain.TABLEEXPORT_SENDTYPE, tableExportEmail);
    }

    public static EmailBuffer build(BdfServer bdfServer, BdfUser bdfUser, String sendType, Object sendObject) {
        Redacteur redacteur = bdfUser.getRedacteur();
        EmailCore redacteurEmail = redacteur.getEmailCore();
        if (redacteurEmail == null) {
            throw new IllegalArgumentException("bdfUser.getRedacteur().getEmailCore() is null");
        }
        boolean redacteurReplyTo = false;
        EmailCore fromEmail = redacteurEmail;
        if (!isSelfFrom(redacteur)) {
            EmailCore customFrom = getCustomFrom(redacteur);
            if (customFrom != null) {
                if (customFrom.getComputedRealName().isEmpty()) {
                    customFrom = EmailCoreUtils.derive(customFrom, redacteurEmail.getComputedRealName());
                }
                fromEmail = customFrom;
                redacteurReplyTo = true;
            } else {
                SmtpManager smtpManager = (SmtpManager) bdfServer.getContextObject(BdfServerConstants.SMTPMANAGER_CONTEXTOBJECT);
                EmailCore defaultFrom = smtpManager.getSmtpParameters().getDefaultFrom();
                if (defaultFrom != null) {
                    fromEmail = EmailCoreUtils.derive(defaultFrom, redacteurEmail.getComputedRealName());
                    redacteurReplyTo = true;
                }
            }
        }
        return EmailBuffer.init(redacteurEmail, fromEmail, sendType, sendObject)
                .setWithRedacteurReplyTo(redacteurReplyTo);
    }

    public static EmailBuffer init(EmailCore redacteurEmail, EmailCore fromEmail, String sendType, Object sendObject) {
        return new EmailBuffer(redacteurEmail, fromEmail, sendType, sendObject);
    }

    private static boolean isSelfFrom(Redacteur redacteur) {
        Attribute attribute = redacteur.getAttributes().getAttribute(BdfSpace.SENDPOLICY_KEY);
        if (attribute != null) {
            return attribute.getFirstValue().equals("selffrom");
        }
        return false;
    }

    private static EmailCore getCustomFrom(Redacteur redacteur) {
        Attribute attribute = redacteur.getAttributes().getAttribute(BdfSpace.SENDPOLICY_KEY);
        if ((attribute != null) && (attribute.size() > 1) && (attribute.getFirstValue().equals("from"))) {
            try {
                EmailCore email = EmailCoreUtils.parse(attribute.get(1));
                return email;
            } catch (ParseException pe) {
            }
        }
        return null;
    }

    private static FicheAttachmentParameters getDefaultFicheAttachmentParameters() {
        FicheAttachmentParameters ficheAttachmentParameters = new FicheAttachmentParameters();
        ficheAttachmentParameters.setWithHtml(true);
        return ficheAttachmentParameters;
    }

}
