package com.atlassian.stash.internal.rest.admin;

import com.atlassian.bitbucket.mail.MailHostConfiguration;
import com.atlassian.bitbucket.mail.MailProtocol;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.permission.PermissionValidationService;
import com.atlassian.bitbucket.rest.RestErrors;
import com.atlassian.bitbucket.rest.mail.RestMailConfiguration;
import com.atlassian.bitbucket.rest.util.ResponseFactory;
import com.atlassian.bitbucket.rest.util.RestUtils;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.atlassian.plugins.rest.common.security.AnonymousAllowed;
import com.atlassian.sal.api.transaction.TransactionCallback;
import com.atlassian.sal.api.transaction.TransactionTemplate;
import com.google.common.base.MoreObjects;
import com.sun.jersey.spi.resource.Singleton;
import org.apache.commons.lang3.StringUtils;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@AnonymousAllowed
@Consumes({MediaType.APPLICATION_JSON})
@Path("admin/mail-server")
@Produces({RestUtils.APPLICATION_JSON_UTF8})
@Singleton
public class MailServerResource {

    private final PermissionValidationService permissionValidationService;
    private final ApplicationPropertiesService propertiesService;
    private final TransactionTemplate transactionTemplate;

    public MailServerResource(PermissionValidationService permissionValidationService,
                              ApplicationPropertiesService propertiesService, TransactionTemplate transactionTemplate) {
        this.permissionValidationService = permissionValidationService;
        this.propertiesService = propertiesService;
        this.transactionTemplate = transactionTemplate;
    }

    /**
     * Deletes the current mail configuration.
     * <p>
     * The authenticated user must have the <strong>SYS_ADMIN</strong> permission to call this resource.
     *
     * @response.representation.204.mediaType application/json
     * @response.representation.204.doc The mail configuration was successfully deleted.
     *
     * @response.representation.401.mediaType application/json
     * @response.representation.401.qname errors
     * @response.representation.401.example {@link RestErrors#EXAMPLE}
     * @response.representation.401.doc The currently authenticated user has insufficient permissions to
     *                                  delete the mail server configuration.
     */
    @DELETE
    public Response deleteMailConfig() {
        propertiesService.deleteMailHostConfiguration();
        propertiesService.setServerEmailAddress(null);

        return ResponseFactory.noContent().build();
    }

    /**
     * Retrieves the current mail configuration.
     *
     * The authenticated user must have the <strong>SYS_ADMIN</strong> permission to call this resource.
     *
     * @response.representation.200.mediaType application/json
     * @response.representation.200.qname mailHostConfiguration
     * @response.representation.200.example {@link RestMailConfiguration#RESPONSE_EXAMPLE}
     * @response.representation.200.doc The mail configuration.
     *
     * @response.representation.401.mediaType application/json
     * @response.representation.401.qname errors
     * @response.representation.401.example {@link RestErrors#EXAMPLE}
     * @response.representation.401.doc The currently authenticated user has insufficient permissions to retrieve the
     * mail configuration.
     *
     * @response.representation.404.mediaType application/json
     * @response.representation.404.doc The mail server hasn't been configured
     */
    @GET
    public Response getMailConfig() {
        permissionValidationService.validateForGlobal(Permission.SYS_ADMIN);
        MailHostConfiguration configuration = propertiesService.getMailHostConfiguration();
        String senderAddress = propertiesService.getServerEmailAddress();
        return configuration == null && senderAddress == null ?
                ResponseFactory.notFound().build() :
                ResponseFactory.ok(new RestMailConfiguration(configuration, senderAddress)).build();
    }

    /**
     * Updates the mail configuration
     *
     * The authenticated user must have the <strong>SYS_ADMIN</strong> permission to call this resource.
     *
     * @param configuration the mail configuration
     *
     * @response.representation.200.mediaType application/json
     * @response.representation.200.qname mailConfiguration
     * @response.representation.200.example {@link RestMailConfiguration#RESPONSE_EXAMPLE}
     * @response.representation.200.doc The updated mail configuration.
     *
     * @response.representation.400.mediaType application/json
     * @response.representation.400.qname errors
     * @response.representation.400.example {@link RestErrors#VALIDATION_EXAMPLE}
     * @response.representation.400.doc The mail configuration was not updated due to a validation error.
     *
     * @response.representation.401.mediaType application/json
     * @response.representation.401.qname errors
     * @response.representation.401.example {@link RestErrors#EXAMPLE}
     * @response.representation.401.doc The currently authenticated user has insufficient permissions to update the
     * mail configuration.
     */
    @PUT
    public Response setMailConfig(final RestMailConfiguration configuration) {
        transactionTemplate.execute(new TransactionCallback<Void>() {
            @Override
            public Void doInTransaction() {
                if (StringUtils.isNotBlank(configuration.getHostname())) {
                    propertiesService.setMailHostConfiguration(new MailHostConfiguration.Builder()
                            .hostname(configuration.getHostname())
                            .port(configuration.getPort())
                            .protocol(MoreObjects.firstNonNull(configuration.getProtocol(), MailProtocol.SMTP))
                            .requireStartTls(configuration.isRequireStartTls())
                            .useStartTls(configuration.isUseStartTls())
                            .username(configuration.getUsername())
                            .password(configuration.getPassword())
                            .build());
                }
                if (StringUtils.isNotBlank(configuration.getSenderAddress())) {
                    propertiesService.setServerEmailAddress(configuration.getSenderAddress());
                }
                return null;
            }
        });

        return ResponseFactory.ok(new RestMailConfiguration(propertiesService.getMailHostConfiguration(),
                propertiesService.getServerEmailAddress())).build();
    }

    /**
     * Clears the server email address.
     * <p>
     * The authenticated user must have the <strong>ADMIN</strong> permission to call this resource.
     *
     * @response.representation.204.mediaType application/json
     * @response.representation.204.doc The server email address was successfully cleared.
     *
     * @response.representation.401.mediaType application/json
     * @response.representation.401.qname errors
     * @response.representation.401.example {@link RestErrors#EXAMPLE}
     * @response.representation.401.doc The currently authenticated user has insufficient permissions to
     *                                  clear the server email address.
     */
    @DELETE
    @Path("sender-address")
    public Response clearSenderAddress() {
        propertiesService.setServerEmailAddress(null);
        return ResponseFactory.noContent().build();
    }

    /**
     * Retrieves the server email address
     *
     * @response.representation.200.mediaType application/json
     * @response.representation.200.qname server email address
     * @response.representation.200.example {@link #EXAMPLE_SENDER_ADDRESS}
     * @response.representation.200.doc The server email address
     *
     * @response.representation.401.mediaType application/json
     * @response.representation.401.qname errors
     * @response.representation.401.example {@link RestErrors#EXAMPLE}
     * @response.representation.401.doc The currently authenticated user has insufficient permissions to retrieve the
     * server email address.
     *
     * @response.representation.404.mediaType application/json
     * @response.representation.404.doc The server email address is not set
     */
    @GET
    @Path("sender-address")
    public Response getSenderAddress() {
        String address = propertiesService.getServerEmailAddress();
        return address == null ?
                ResponseFactory.notFound().build() :
                ResponseFactory.ok(address).build();
    }

    /**
     * Updates the server email address
     *
     * The authenticated user must have the <strong>ADMIN</strong> permission to call this resource.
     *
     * @param senderAddress the from address used in notification emails
     *
     * @response.representation.200.mediaType application/json
     * @response.representation.200.qname senderAddress
     * @response.representation.200.example {@link #EXAMPLE_SENDER_ADDRESS}
     * @response.representation.200.doc The updated server email address.
     *
     * @response.representation.400.mediaType application/json
     * @response.representation.400.qname errors
     * @response.representation.400.example {@link RestErrors#VALIDATION_EXAMPLE}
     * @response.representation.400.doc The server email address was not updated due to a validation error.
     *
     * @response.representation.401.mediaType application/json
     * @response.representation.401.qname errors
     * @response.representation.401.example {@link RestErrors#EXAMPLE}
     * @response.representation.401.doc The currently authenticated user has insufficient permissions to update the
     * server email address.
     */
    @PUT
    @Path("sender-address")
    public Response setSenderAddress(final String senderAddress) {
        propertiesService.setServerEmailAddress(senderAddress);
        return ResponseFactory.ok(propertiesService.getServerEmailAddress()).build();
    }

    @SuppressWarnings("UnusedDeclaration")
    public static final String EXAMPLE_SENDER_ADDRESS = "stash-no-reply@company.com";
}
