// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package google.registry.ui.server;

import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import com.google.common.collect.ImmutableList;
import google.registry.util.EmailMessage;
import google.registry.util.SendEmailService;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;

/** Unit tests for {@link SendEmailUtils}. */
@RunWith(JUnit4.class)
public class SendEmailUtilsTest {

  private final SendEmailService emailService = mock(SendEmailService.class);
  private SendEmailUtils sendEmailUtils;

  private void setRecipients(ImmutableList<String> recipients) throws Exception {
    sendEmailUtils =
        new SendEmailUtils(
            new InternetAddress("[email protected]"),
            "outgoing display name",
            recipients,
            emailService);
  }

  @Test
  public void testSuccess_sendToOneAddress() throws Exception {
    setRecipients(ImmutableList.of("[email protected]"));
    assertThat(sendEmailUtils.hasRecipients()).isTrue();
    assertThat(
            sendEmailUtils.sendEmail(
                "Welcome to the Internet",
                "It is a dark and scary place."))
        .isTrue();
    verifyMessageSent("[email protected]");
  }

  @Test
  public void testSuccess_sendToMultipleAddresses() throws Exception {
    setRecipients(ImmutableList.of("[email protected]", "[email protected]"));
    assertThat(sendEmailUtils.hasRecipients()).isTrue();
    assertThat(
            sendEmailUtils.sendEmail(
                "Welcome to the Internet",
                "It is a dark and scary place."))
        .isTrue();
    verifyMessageSent("[email protected]", "[email protected]");
  }

  @Test
  public void testSuccess_ignoresMalformedEmailAddress() throws Exception {
    setRecipients(ImmutableList.of("[email protected]", "1iƱvalidemail"));
    assertThat(sendEmailUtils.hasRecipients()).isTrue();
    assertThat(
            sendEmailUtils.sendEmail(
                "Welcome to the Internet",
                "It is a dark and scary place."))
        .isTrue();
    verifyMessageSent("[email protected]");
  }

  @Test
  public void testFailure_noAddresses() throws Exception {
    setRecipients(ImmutableList.of());
    assertThat(sendEmailUtils.hasRecipients()).isFalse();
    assertThat(
            sendEmailUtils.sendEmail(
                "Welcome to the Internet",
                "It is a dark and scary place."))
        .isFalse();
    verify(emailService, never()).sendEmail(any());
  }

  @Test
  public void testFailure_onlyGivenMalformedAddress() throws Exception {
    setRecipients(ImmutableList.of("1iƱvalidemail"));
    assertThat(sendEmailUtils.hasRecipients()).isTrue();
    assertThat(
            sendEmailUtils.sendEmail(
                "Welcome to the Internet",
                "It is a dark and scary place."))
        .isFalse();
    verify(emailService, never()).sendEmail(any());
  }

  @Test
  public void testFailure_exceptionThrownDuringSend() throws Exception {
    setRecipients(ImmutableList.of("[email protected]"));
    assertThat(sendEmailUtils.hasRecipients()).isTrue();
    doThrow(new RuntimeException(new MessagingException("expected")))
        .when(emailService)
        .sendEmail(any());
    assertThat(
            sendEmailUtils.sendEmail(
                "Welcome to the Internet",
                "It is a dark and scary place."))
        .isFalse();
    verifyMessageSent("[email protected]");
  }

  @Test
  public void testAdditionalRecipients() throws Exception {
    setRecipients(ImmutableList.of("[email protected]"));
    assertThat(sendEmailUtils.hasRecipients()).isTrue();
    assertThat(
            sendEmailUtils.sendEmail(
                "Welcome to the Internet",
                "It is a dark and scary place.",
                ImmutableList.of("[email protected]", "[email protected]")))
        .isTrue();
    verifyMessageSent("[email protected]", "[email protected]", "[email protected]");
  }

  @Test
  public void testOnlyAdditionalRecipients() throws Exception {
    setRecipients(ImmutableList.of());
    assertThat(sendEmailUtils.hasRecipients()).isFalse();
    assertThat(
            sendEmailUtils.sendEmail(
                "Welcome to the Internet",
                "It is a dark and scary place.",
                ImmutableList.of("[email protected]", "[email protected]")))
        .isTrue();
    verifyMessageSent("[email protected]", "[email protected]");
  }

  private void verifyMessageSent(String... expectedRecipients) throws Exception {
    ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
    verify(emailService).sendEmail(contentCaptor.capture());
    EmailMessage emailMessage = contentCaptor.getValue();
    ImmutableList.Builder<InternetAddress> recipientBuilder = ImmutableList.builder();
    for (String expectedRecipient : expectedRecipients) {
      recipientBuilder.add(new InternetAddress(expectedRecipient));
    }
    EmailMessage expectedContent =
        EmailMessage.newBuilder()
            .setSubject("Welcome to the Internet")
            .setBody("It is a dark and scary place.")
            .setFrom(new InternetAddress("[email protected]"))
            .setRecipients(recipientBuilder.build())
            .build();
    assertThat(emailMessage).isEqualTo(expectedContent);
  }
}