/*
 * Copyright (C) 2015, 2016 WTFDYUM
 *
 * This file is part of the WTFDYUM project.
 *
 * 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 com.jeanchampemont.wtfdyum.service;

import com.jeanchampemont.wtfdyum.WTFDYUMApplication;
import com.jeanchampemont.wtfdyum.dto.Event;
import com.jeanchampemont.wtfdyum.dto.Feature;
import com.jeanchampemont.wtfdyum.dto.type.EventType;
import com.jeanchampemont.wtfdyum.dto.type.UserLimitType;
import com.jeanchampemont.wtfdyum.service.impl.UserServiceImpl;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.time.Clock;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = WTFDYUMApplication.class)
public class UserServiceTest {

    private UserService sut;

    @Mock
    private RedisTemplate<String, Long> longRedisTemplate;

    @Mock
    private RedisTemplate<String, Event> eventRedisTemplate;

    @Mock
    private RedisTemplate<String, Feature> featureRedisTemplate;

    @Mock
    private SetOperations<String, Long> longSetOperations;

    @Mock
    private ValueOperations<String, Long> longValueOperations;

    @Mock
    private SetOperations<String, Feature> featureSetOperations;

    @Mock
    private ListOperations<String, Event> eventListOperations;

    @Mock
    private FeatureService featureService;

    private final Clock clock = Clock.fixed(Instant.parse("2007-12-03T10:15:30.00Z"), ZoneId.of("Z"));

    @Before
    public void _init() {
        initMocks(this);
        sut = new UserServiceImpl(eventRedisTemplate, featureRedisTemplate, longRedisTemplate, featureService, clock);
    }

    @Test
    public void addEventTest() {
        when(eventRedisTemplate.opsForList()).thenReturn(eventListOperations);

        final Event event = new Event(EventType.REGISTRATION, "data");

        sut.addEvent(31L, event);

        verify(eventListOperations, times(1)).leftPush("EVENTS_31", event);
        assertThat(event.getCreationDateTime()).isEqualTo(LocalDateTime.now(clock));
    }

    @Test
    public void applyLimitTestNotReached() {
        when(longRedisTemplate.opsForValue()).thenReturn(longValueOperations);
        when(longValueOperations.increment(UserLimitType.CREDENTIALS_INVALID.name() + "_442", 1)).thenReturn(4L);

        final boolean result = sut.applyLimit(442L, UserLimitType.CREDENTIALS_INVALID);

        assertThat(result).isFalse();
    }

    @Test
    public void applyLimitTestReached() {
        when(longRedisTemplate.opsForValue()).thenReturn(longValueOperations);
        when(eventRedisTemplate.opsForList()).thenReturn(eventListOperations);
        when(longValueOperations.increment(UserLimitType.CREDENTIALS_INVALID.name() + "_442", 1)).thenReturn(5L);

        final boolean result = sut.applyLimit(442L, UserLimitType.CREDENTIALS_INVALID);

        assertThat(result).isTrue();
        for (final Feature f : Feature.values()) {
            verify(featureService, times(1)).disableFeature(442L, f);
        }
        final ArgumentCaptor<Event> eventCaptor = ArgumentCaptor.forClass(Event.class);
        verify(eventListOperations, times(1)).leftPush(eq("EVENTS_442"), eventCaptor.capture());
        assertThat(eventCaptor.getValue().getType()).isEqualTo(EventType.CREDENTIALS_INVALID_LIMIT_REACHED);
        assertThat(eventCaptor.getValue().getAdditionalData()).isEmpty();
        assertThat(eventCaptor.getValue().getCreationDateTime()).isEqualTo(LocalDateTime.now(clock));
    }

    @Test
    public void getEnabledFeaturesTest() {
    	when(featureRedisTemplate.opsForSet()).thenReturn(featureSetOperations);
    	when(featureSetOperations.members("FEATURES_1234")).thenReturn(new HashSet<>(Arrays.asList(Feature.NOTIFY_UNFOLLOW, Feature.TWEET_UNFOLLOW)));

    	final Set<Feature> result = sut.getEnabledFeatures(1234L);

    	assertThat(result).isEqualTo(new HashSet<>(Arrays.asList(Feature.NOTIFY_UNFOLLOW, Feature.TWEET_UNFOLLOW)));
    }

    @Test
    public void getRecentEventsTest() {
        final List<Event> result = Arrays.asList(new Event(EventType.REGISTRATION, "reg"),
                new Event(EventType.UNFOLLOW, "unfoll"));

        when(eventRedisTemplate.opsForList()).thenReturn(eventListOperations);
        when(eventListOperations.range("EVENTS_1249", 0, 12)).thenReturn(result);

        final List<Event> returnedResult = sut.getRecentEvents(1249L, 12);

        assertThat(returnedResult).isNotNull();
        assertThat(returnedResult.size()).isEqualTo(2);
        assertThat(returnedResult).isEqualTo(result);
    }

    @Test
    public void getRecentEventsWithStartTest() {
        final List<Event> result = Arrays.asList(new Event(EventType.REGISTRATION, "reg"),
            new Event(EventType.UNFOLLOW, "unfoll"));

        when(eventRedisTemplate.opsForList()).thenReturn(eventListOperations);
        when(eventListOperations.range("EVENTS_1249", 11, 12)).thenReturn(result);

        final List<Event> returnedResult = sut.getRecentEvents(1249L, 12, 11);

        assertThat(returnedResult).isNotNull();
        assertThat(returnedResult.size()).isEqualTo(2);
        assertThat(returnedResult).isEqualTo(result);
    }

    @Test
    public void resetLimitTest() {

        sut.resetLimit(199L, UserLimitType.CREDENTIALS_INVALID);

        verify(longRedisTemplate, times(1)).delete(UserLimitType.CREDENTIALS_INVALID.name() + "_199");
    }
}

@@ -6200,13 +6200,13 @@
 %22, 1
-1, 12
+0, 15
 )).t
@@ -6299,14 +6299,13 @@
 9L, 
-12, 11
+5, 10
 );%0A%0A

fix test