package com.atlassian.stash.internal.plugin;

import com.atlassian.plugin.Plugin;
import com.atlassian.plugin.PluginAccessor;
import com.atlassian.plugin.PluginInformation;
import com.atlassian.plugin.elements.ResourceDescriptor;
import com.atlassian.plugin.event.PluginEventManager;
import com.atlassian.plugin.event.events.PluginDisabledEvent;
import com.atlassian.plugin.event.events.PluginEnabledEvent;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

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

import static java.util.stream.Collectors.toList;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class I18nStateHasherTest {

    @Mock
    private PluginAccessor pluginAccessor;

    @Mock
    private PluginEventManager pluginEventManager;

    @InjectMocks
    private I18nStateHasher i18nStateHasher;

    private List<Plugin> plugins;

    @Before
    public void setUp() throws Exception {
        plugins = new ArrayList<>();
        when(pluginAccessor.getEnabledPlugins()).thenReturn(plugins);
    }

    @Test
    public void testHashChangesWhenAddingPluginWithI18n() throws Exception {
        String hash = i18nStateHasher.get();
        assertThat(hash, not(isEmptyString()));

        addPlugin("plugin.key", "1.0", "foo", "i18n");
        String newHash = i18nStateHasher.get();
        assertThat(newHash, not(equalTo(hash)));
    }

    @Test
    public void testHashChangesWhenRemovingPluginWithI18n() throws Exception {
        addPlugin("plugin.key", "1.0", "foo", "i18n");
        String hash = i18nStateHasher.get();
        removePlugin("plugin.key");
        assertThat(i18nStateHasher.get(), not(equalTo(hash)));
    }

    @Test
    public void testHashDoesNotChangeWhenRemovingPluginWithoutI18n() throws Exception {
        addPlugin("plugin.key", "1.0", "foo", "i18n");
        addPlugin("other.plugin", "1.0", "bar");
        String hash = i18nStateHasher.get();
        removePlugin("other.plugin");
        assertThat(i18nStateHasher.get(), equalTo(hash));
    }

    @Test
    public void testHashDoesNotChangeWhenAddingPluginWithoutI18n() throws Exception {
        addPlugin("plugin.key", "1.0", "foo", "i18n");
        String hash = i18nStateHasher.get();
        addPlugin("other.plugin.key", "2.1", "foo", "bar");
        assertThat(i18nStateHasher.get(), equalTo(hash));
    }

    private Plugin createPlugin(String key, String version, String... resourceTypes) {
        Plugin plugin = mock(Plugin.class);
        when(plugin.getKey()).thenReturn(key);
        PluginInformation pluginInformation = mock(PluginInformation.class);
        when(pluginInformation.getVersion()).thenReturn(version);
        when(plugin.getPluginInformation()).thenReturn(pluginInformation);

        List<ResourceDescriptor> descriptors = Arrays.stream(resourceTypes)
                .map(resourceType -> {
                    ResourceDescriptor rd = mock(ResourceDescriptor.class);
                    when(rd.getType()).thenReturn(resourceType);
                    return rd;
                })
                .collect(toList());

        when(plugin.getResourceDescriptors()).thenReturn(descriptors);
        return plugin;
    }

    private void addPlugin(String key, String version, String... resourceTypes) {
        Plugin plugin = createPlugin(key, version, resourceTypes);
        plugins.add(plugin);
        i18nStateHasher.onPluginEnabled(new PluginEnabledEvent(plugin));
    }

    private void removePlugin(String key)  {
        plugins.stream()
            .filter(plugin -> plugin.getKey().equals(key))
            .findFirst()
            .ifPresent(plugin -> {
                i18nStateHasher.onPluginDisabled(new PluginDisabledEvent(plugin));
                plugins.remove(plugin);
            });
    }


}