package com.atlassian.bitbucket.mail;

import com.atlassian.bitbucket.util.BuilderSupport;
import com.atlassian.bitbucket.validation.annotation.OptionalString;
import com.atlassian.bitbucket.validation.annotation.RequiredString;
import org.hibernate.validator.constraints.Range;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;

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

/**
 * Class containing the mail host configuration.
 */
public class MailHostConfiguration {

    @RequiredString
    private final String hostname;
    @OptionalString
    private final String password;
    @Range(min = 0, max = 65535)
    private final Integer port;
    @Nonnull
    private final MailProtocol protocol;
    private final boolean requireStartTls;
    @OptionalString
    private final String username;
    private final boolean useStartTls;

    private MailHostConfiguration(Builder builder) {
        hostname = builder.hostname;
        password = builder.password;
        protocol = builder.protocol;
        port = builder.port;
        requireStartTls = builder.requireStartTls;
        username = builder.username;
        useStartTls = builder.useStartTls;
    }

    public String getHostname() {
        return hostname;
    }

    public String getPassword() {
        return password;
    }

    public Integer getPort() {
        return port;
    }

    @Nonnull
    public MailProtocol getProtocol() {
        return protocol;
    }

    public String getUsername() {
        return username;
    }

    /**
     * @return {@code true} if the product should always use the STARTTLS extension when connecting to SMTP (note: not SMPTS)
     * mail servers and fail if the server does not support it (ensuring mail is always sent over SSL/TLS and never in plaintext).
     * @see <a href="https://en.wikipedia.org/wiki/STARTTLS">https://en.wikipedia.org/wiki/STARTTLS</a>
     */
    public boolean isRequireStartTls() {
        return requireStartTls;
    }

    /**
     * @return {@code true} if the product will attempt to use the STARTTLS extension if the SMTP (note: not SMTPS) server
     * on the other end supports it. If {@code true} and the server does not support it mail will be transmitted
     * in plaintext. If {@code false} mail will always be transmitted in plaintext.
     * @see <a href="https://en.wikipedia.org/wiki/STARTTLS">https://en.wikipedia.org/wiki/STARTTLS</a>
     */
    public boolean isUseStartTls() {
        return useStartTls;
    }

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

        MailHostConfiguration that = (MailHostConfiguration) o;

        return Objects.equals(hostname, that.hostname) &&
                Objects.equals(port, that.port) &&
                Objects.equals(username, that.username) &&
                Objects.equals(password, that.password) &&
                Objects.equals(protocol, that.protocol) &&
                useStartTls == that.useStartTls &&
                requireStartTls == that.requireStartTls;
    }

    @Override
    public int hashCode() {
        return Objects.hash(hostname, port, username, password, protocol, useStartTls, requireStartTls);
    }

    public static class Builder extends BuilderSupport {

        private String hostname;
        private String password;
        private Integer port;
        private MailProtocol protocol;
        private boolean requireStartTls;
        private String username;
        private boolean useStartTls;

        public Builder() {
            protocol = MailProtocol.SMTP;
        }

        public Builder(@Nonnull MailHostConfiguration other) {
            hostname(checkNotNull(other, "other").hostname)
                    .port(other.port)
                    .username(other.username)
                    .protocol(other.protocol)
                    .useStartTls(other.useStartTls)
                    .requireStartTls(other.requireStartTls)
                    .password(other.password);
        }

        @Nonnull
        public MailHostConfiguration build() {
            return new MailHostConfiguration(this);
        }

        @Nonnull
        public Builder hostname(@Nonnull String value) {
            hostname = value;

            return this;
        }

        @Nonnull
        public Builder password(@Nullable String value) {
            password = value;

            return this;
        }

        @Nonnull
        public Builder port(@Nullable Integer value) {
            port = value;

            return this;
        }

        @Nonnull
        public Builder protocol(@Nonnull MailProtocol value) {
            protocol = checkNotNull(value, "protocol");

            return this;
        }

        @Nonnull
        public Builder requireStartTls(boolean value) {
            requireStartTls = value;
            if (value) {
                useStartTls = true;
            }

            return this;
        }

        @Nonnull
        public Builder username(@Nullable String value) {
            username = value;

            return this;
        }

        @Nonnull
        public Builder useStartTls(boolean value) {
            useStartTls = value;
            if (!value) {
                requireStartTls = false;
            }

            return this;
        }
    }
}
