// Copyright 2019 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.tools;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadByKeys;
import static google.registry.testing.DatabaseHelper.loadByKeysIfPresent;
import static google.registry.testing.DatabaseHelper.persistResource;

import com.google.common.collect.ImmutableList;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.poll.PollMessage;
import google.registry.model.poll.PollMessage.Autorenew;
import google.registry.model.poll.PollMessage.OneTime;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import google.registry.testing.DatabaseHelper;
import java.time.Instant;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/** Unit tests for {@link AckPollMessagesCommand}. */
public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesCommand> {

  private DomainHistory domainHistory;

  @BeforeEach
  final void beforeEach() {
    command.clock = fakeClock;
    fakeClock.setTo(Instant.parse("2015-02-04T08:16:32.064Z"));
    createTld("tld");
    Domain domain =
        DatabaseHelper.newDomain("example.tld").asBuilder().setRepoId("FSDGS-TLD").build();
    persistResource(domain);
    domainHistory =
        persistResource(
            new DomainHistory.Builder()
                .setModificationTime(fakeClock.now())
                .setDomain(domain)
                .setRegistrarId(domain.getCreationRegistrarId())
                .setType(HistoryEntry.Type.DOMAIN_CREATE)
                .build());
    fakeClock.advanceOneMilli();
  }

  @Test
  void testSuccess_doesntDeletePollMessagesInFuture() throws Exception {
    VKey<OneTime> pm1 =
        persistPollMessage(316L, Instant.parse("2014-01-01T22:33:44Z"), "foobar").createVKey();
    VKey<OneTime> pm2 =
        persistPollMessage(624L, Instant.parse("2013-05-01T22:33:44Z"), "ninelives").createVKey();
    VKey<OneTime> pm3 =
        persistPollMessage(791L, Instant.parse("2015-01-08T22:33:44Z"), "ginger").createVKey();
    OneTime futurePollMessage =
        persistPollMessage(123L, Instant.parse("2015-09-01T22:33:44Z"), "notme");
    VKey<OneTime> pm4 = futurePollMessage.createVKey();
    runCommand("-c", "TheRegistrar");
    assertThat(loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3, pm4)).values())
        .containsExactly(futurePollMessage);
    assertInStdout(
        "624-2013,2013-05-01T22:33:44Z,ninelives",
        "316-2014,2014-01-01T22:33:44Z,foobar",
        "791-2015,2015-01-08T22:33:44Z,ginger");
    assertNotInStdout("123-2015,2015-09-01T22:33:44Z,notme");
  }

  @Test
  void testSuccess_resavesAutorenewPollMessages() throws Exception {
    VKey<OneTime> pm1 =
        persistPollMessage(316L, Instant.parse("2014-01-01T22:33:44Z"), "foobar").createVKey();
    VKey<OneTime> pm2 =
        persistPollMessage(624L, Instant.parse("2013-05-01T22:33:44Z"), "ninelives").createVKey();
    Autorenew autorenew =
        persistResource(
            new PollMessage.Autorenew.Builder()
                .setId(625L)
                .setHistoryEntry(domainHistory)
                .setEventTime(Instant.parse("2011-04-15T22:33:44Z"))
                .setRegistrarId("TheRegistrar")
                .setMsg("autorenew")
                .build());
    Autorenew resaved =
        autorenew.asBuilder().setEventTime(Instant.parse("2012-04-15T22:33:44Z")).build();
    VKey<Autorenew> pm3 = autorenew.createVKey();
    runCommand("-c", "TheRegistrar");
    assertThat(loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3)).values())
        .containsExactly(resaved);
    assertInStdout(
        "625-2011,2011-04-15T22:33:44Z,autorenew",
        "624-2013,2013-05-01T22:33:44Z,ninelives",
        "316-2014,2014-01-01T22:33:44Z,foobar");
  }

  @Test
  void testSuccess_deletesExpiredAutorenewPollMessages() throws Exception {
    VKey<OneTime> pm1 =
        persistPollMessage(316L, Instant.parse("2014-01-01T22:33:44Z"), "foobar").createVKey();
    VKey<OneTime> pm2 =
        persistPollMessage(624L, Instant.parse("2013-05-01T22:33:44Z"), "ninelives").createVKey();
    Autorenew autorenew =
        persistResource(
            new PollMessage.Autorenew.Builder()
                .setId(625L)
                .setHistoryEntry(domainHistory)
                .setEventTime(Instant.parse("2011-04-15T22:33:44Z"))
                .setAutorenewEndTime(Instant.parse("2012-01-01T22:33:44Z"))
                .setRegistrarId("TheRegistrar")
                .setMsg("autorenew")
                .build());
    VKey<Autorenew> pm3 = autorenew.createVKey();
    runCommand("-c", "TheRegistrar");
    assertThat(loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3))).isEmpty();
    assertInStdout(
        "625-2011,2011-04-15T22:33:44Z,autorenew",
        "624-2013,2013-05-01T22:33:44Z,ninelives",
        "316-2014,2014-01-01T22:33:44Z,foobar");
  }

  @Test
  void testSuccess_onlyDeletesPollMessagesMatchingMessage() throws Exception {
    VKey<OneTime> pm1 =
        persistPollMessage(316L, Instant.parse("2014-01-01T22:33:44Z"), "food is good")
            .createVKey();
    OneTime notMatched1 =
        persistPollMessage(624L, Instant.parse("2013-05-01T22:33:44Z"), "theft is bad");
    VKey<OneTime> pm2 = notMatched1.createVKey();
    VKey<OneTime> pm3 =
        persistPollMessage(791L, Instant.parse("2015-01-08T22:33:44Z"), "mmmmmfood").createVKey();
    OneTime notMatched2 =
        persistPollMessage(123L, Instant.parse("2015-09-01T22:33:44Z"), "time flies");
    VKey<OneTime> pm4 = notMatched2.createVKey();
    runCommand("-c", "TheRegistrar", "-m", "food");
    assertThat(loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3, pm4)).values())
        .containsExactly(notMatched1, notMatched2);
  }

  @Test
  void testSuccess_onlyDeletesPollMessagesMatchingClientId() throws Exception {
    VKey<OneTime> pm1 =
        persistPollMessage(316L, Instant.parse("2014-01-01T22:33:44Z"), "food is good")
            .createVKey();
    VKey<OneTime> pm2 =
        persistPollMessage(624L, Instant.parse("2013-05-01T22:33:44Z"), "theft is bad")
            .createVKey();
    OneTime notMatched =
        persistResource(
            new PollMessage.OneTime.Builder()
                .setId(2474L)
                .setHistoryEntry(domainHistory)
                .setRegistrarId("NewRegistrar")
                .setEventTime(Instant.parse("2013-06-01T22:33:44Z"))
                .setMsg("baaaahh")
                .build());
    VKey<OneTime> pm3 = notMatched.createVKey();
    runCommand("-c", "TheRegistrar");
    assertThat(loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3)).values())
        .containsExactly(notMatched);
  }

  @Test
  void testSuccess_dryRunDoesntDeleteAnything() throws Exception {
    OneTime pm1 = persistPollMessage(316L, Instant.parse("2014-01-01T22:33:44Z"), "foobar");
    OneTime pm2 = persistPollMessage(624L, Instant.parse("2013-05-01T22:33:44Z"), "ninelives");
    OneTime pm3 = persistPollMessage(791L, Instant.parse("2015-01-08T22:33:44Z"), "ginger");
    OneTime pm4 = persistPollMessage(123L, Instant.parse("2015-09-01T22:33:44Z"), "notme");
    runCommand("-c", "TheRegistrar", "-d");
    assertThat(
            loadByKeys(
                ImmutableList.of(pm1, pm2, pm3, pm4).stream()
                    .map(OneTime::createVKey)
                    .collect(toImmutableList())))
        .containsExactly(pm1, pm2, pm3, pm4);
  }

  private OneTime persistPollMessage(long id, Instant eventTime, String message) {
    return persistResource(
        new PollMessage.OneTime.Builder()
            .setId(id)
            .setHistoryEntry(domainHistory)
            .setRegistrarId("TheRegistrar")
            .setEventTime(eventTime)
            .setMsg(message)
            .build());
  }
}
