package com.atlassian.bitbucket.mail;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang3.StringUtils;

import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.util.*;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * Class representing a mail message.
 */
public class MailMessage {

    private final String from;
    private final Map<String, String> headers;
    private final Set<String> to;
    private final Set<String> cc;
    private final Set<String> bcc;
    private final String text;
    private final String subject;
    private final Set<MailAttachment> attachments;

    public MailMessage(@Nonnull Set<String> to,
                       @Nullable String from,
                       @Nullable Set<String> cc,
                       @Nullable Set<String> bcc,
                       @Nullable Set<MailAttachment> attachments,
                       @Nullable String text,
                       @Nullable String subject,
                       @Nullable Map<String, String> headers) {
        checkArgument(!to.isEmpty(), "One or more \"to\" addresses are required");
        this.from = StringUtils.trimToNull(from);
        this.headers = headers == null ? Collections.<String, String>emptyMap() : ImmutableMap.copyOf(headers);
        this.to = ImmutableSet.copyOf(to);
        this.cc = cc != null ? ImmutableSet.copyOf(cc) : Collections.<String>emptySet();
        this.bcc = bcc != null ? ImmutableSet.copyOf(bcc) : Collections.<String>emptySet();
        this.attachments = attachments != null ? ImmutableSet.copyOf(attachments) : Collections.<MailAttachment>emptySet();
        this.text = StringUtils.defaultString(text);
        this.subject = StringUtils.defaultString(subject);
    }

    @Nullable
    public String getFrom() {
        return from;
    }

    @Nonnull
    public Map<String, String> getHeaders() {
        return headers;
    }

    @Nonnull
    public Set<String> getTo() {
        return to;
    }

    @Nonnull
    public Set<String> getCc() {
        return cc;
    }

    @Nonnull
    public Set<String> getBcc() {
        return bcc;
    }

    @Nonnull
    public Set<MailAttachment> getAttachments() {
        return attachments;
    }

    @Nonnull
    public String getText() {
        return text;
    }

    @Nonnull
    public String getSubject() {
        return subject;
    }

    public boolean hasBcc() {
        return !bcc.isEmpty();
    }

    public boolean hasCc() {
        return !cc.isEmpty();
    }

    public boolean hasFrom() {
        return from != null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        MailMessage that = (MailMessage) o;
        return to.equals(that.to) &&
                cc.equals(that.cc) &&
                bcc.equals(that.bcc) &&
                text.equals(that.text) &&
                subject.equals(that.subject) &&
                attachments.equals(that.attachments) &&
                headers.equals(that.headers) &&
                (from == null ? that.from == null : from.equals(that.from));
    }

    @Override
    public int hashCode() {
        int result = to.hashCode();
        result = 31 * result + cc.hashCode();
        result = 31 * result + bcc.hashCode();
        result = 31 * result + text.hashCode();
        result = 31 * result + subject.hashCode();
        result = 31 * result + attachments.hashCode();
        result = 31 * result + headers.hashCode();
        if (from != null) {
            result = 31 * result + from.hashCode();
        }
        return result;
    }

    public static class Builder {

        private final Set<MailAttachment> attachments = new LinkedHashSet<>();
        private final Set<String> bcc = new LinkedHashSet<>();
        private final Set<String> cc = new LinkedHashSet<>();
        private final Map<String, String> headers = new LinkedHashMap<>();
        private final Set<String> to = new LinkedHashSet<>();
        private String from;
        private String subject;
        private String text;

        public Builder() {
        }

        private void addRecipients(@Nonnull Set<String> target, String[] recipients) {
            if (recipients != null) {
                for (String recipient : recipients) {
                    if (recipient != null) {
                        target.add(recipient);
                    }
                }
            }
        }

        private void addRecipients(@Nonnull Set<String> target, Iterable<String> recipients) {
            if (recipients != null) {
                for (String recipient : recipients) {
                    if (recipient != null) {
                        target.add(recipient);
                    }
                }
            }
        }

        @Nonnull
        public Builder from(String value) {
            from = value;
            return this;
        }

        @Nonnull
        public Builder to(String... recipients) {
            addRecipients(to, recipients);
            return this;
        }

        @Nonnull
        public Builder to(Iterable<String> recipients) {
            addRecipients(to, recipients);
            return this;
        }

        @Nonnull
        public Builder cc(String... recipients) {
            addRecipients(cc, recipients);
            return this;
        }

        @Nonnull
        public Builder cc(Iterable<String> recipients) {
            addRecipients(cc, recipients);
            return this;
        }

        @Nonnull
        public Builder bcc(String... recipients) {
            addRecipients(bcc, recipients);
            return this;
        }

        @Nonnull
        public Builder bcc(Iterable<String> recipients) {
            addRecipients(bcc, recipients);
            return this;
        }

        @Nonnull
        public Builder subject(String subject) {
            this.subject = subject;
            return this;
        }

        @Nonnull
        public Builder text(String text) {
            this.text = text;
            return this;
        }

        @Nonnull
        public Builder attachment(String fileName, DataSource source) {
            this.attachments.add(new MailAttachment(fileName, source));
            return this;
        }

        @Nonnull
        public Builder attachment(String fileName, File file) {
            return attachment(fileName, new FileDataSource(file));
        }

        @Nonnull
        public Builder attachment(String fileName, String pathToFile) {
            return attachment(fileName, new File(pathToFile));
        }

        @Nonnull
        public Builder header(String key, String value) {
            headers.put(key, value);
            return this;
        }

        @Nonnull
        public MailMessage build() {
            return new MailMessage(to, from, cc, bcc, attachments, text, subject, headers);
        }
    }
}
