package com.atlassian.stash.internal.notification.batch;

import com.atlassian.stash.internal.notification.NotificationConfig;
import com.atlassian.stash.internal.notification.batch.dao.UserNotification;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import org.joda.time.Duration;
import org.joda.time.Instant;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.Arrays;
import java.util.List;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.junit.runners.Parameterized.Parameter;
import static org.junit.runners.Parameterized.Parameters;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(Parameterized.class)
public class BatchTimingTest {

    private static final Instant NOW = Instant.now();

    private BatchTiming batchTiming;


    @Parameter(0)
    public List<Instant> notificationTimes;

    @Parameter(1)
    public boolean expectedShouldBeSent;

    @Parameter(2)
    public boolean expectedCanBeDiscarded;

    @Parameters(name = "notifications {0}, shouldBeSent {1}, canBeDiscarded {2}")
    public static List<Object[]> data() {
        return Arrays.asList(new Object[][]{
                {Arrays.asList(NOW), false, false},
                {Arrays.asList(NOW, minutesAgo(2)), false, false},
                {Arrays.asList(minutesAgo(1), minutesAgo(2), minutesAgo(3)), false, false},
                {Arrays.asList(minutesAgo(3), minutesAgo(4), minutesAgo(5)), true, false},
                {Arrays.asList(minutesAgo(1), minutesAgo(5), minutesAgo(11)), true, false},
                {Arrays.asList(minutesAgo(41), minutesAgo(42)), true, true},
        });
    }

    @Before
    public void setup() {
        NotificationConfig notificationConfig = mock(NotificationConfig.class);
        when(notificationConfig.getBatchMinWaitMinutes()).thenReturn(2L);
        when(notificationConfig.getBatchMaxWaitMinutes()).thenReturn(10L);
        batchTiming = new BatchTiming(notificationConfig);
    }

    @Test
    public void testShouldBeSent() {
        Iterable<UserNotification> batch = Iterables.transform(notificationTimes, new Function<Instant, UserNotification>() {
            public UserNotification apply(Instant instant) {
                return notification(instant);
            }
        });
        assertThat(batchTiming.batchShouldBeSent(batch, NOW.getMillis()), is(expectedShouldBeSent));
    }

    @Test
    public void testCanBeDiscarded() {
        Iterable<UserNotification> batch = Iterables.transform(notificationTimes, new Function<Instant, UserNotification>() {
            public UserNotification apply(Instant instant) {
                return notification(instant);
            }
        });
        assertThat(batchTiming.batchCanBeDiscarded(batch, NOW.getMillis()), is(expectedCanBeDiscarded));
    }

    private static Instant minutesAgo(long minutes) {
        return NOW.minus(Duration.standardMinutes(minutes));
    }

    private static UserNotification notification(Instant timestamp) {
        UserNotification notification = mock(UserNotification.class);
        when(notification.getTimestamp()).thenReturn(timestamp.toDate());
        return notification;
    }
}
