package com.atlassian.bitbucket.internal.ssh.server;

import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import org.apache.sshd.common.NamedFactory;
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.Arrays;
import java.util.List;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class DefaultNamedFactoryFilterTest {

    private static final String FACTORY_1 = "factory1";
    private static final String FACTORY_2 = "factory2";
    private static final String FACTORY_3 = "factory3";

    private List<NamedFactory<Object>> defaultFactories;

    @InjectMocks
    private DefaultNamedFactoryFilter namedFactoryFilter;
    @Mock
    private ApplicationPropertiesService propertiesService;

    @Before
    public void setUp() throws Exception {
        NamedFactory<Object> factory1 = new DefaultNamedFactory(FACTORY_1);
        NamedFactory<Object> factory2 = new DefaultNamedFactory(FACTORY_2);
        NamedFactory<Object> factory3 = new DefaultNamedFactory(FACTORY_3);

        defaultFactories = Arrays.asList(factory1, factory2, factory3);
    }

    @Test
    public void testNormalFactoryFiltering() throws Exception {
        String factoriesToDisable = Joiner.on(",").join(FACTORY_1, FACTORY_3);
        when(propertiesService.getPluginProperty(NamedFactoryType.CIPHER.getDisabledFactoriesProperty(), ""))
                .thenReturn(factoriesToDisable);

        List<NamedFactory<Object>> filteredFactories = namedFactoryFilter.filter(defaultFactories, NamedFactoryType.CIPHER);

        Iterable<String> filteredFactoriesNames = Iterables.transform(filteredFactories, DefaultNamedFactoryFilter.TO_NAME);

        assertThat(filteredFactoriesNames, hasItem(FACTORY_2));
        assertThat(filteredFactoriesNames, not(containsInAnyOrder(FACTORY_1, FACTORY_3)));
    }

    @Test
    public void testExtraSpacesFactoryFiltering() throws Exception {
        String factoriesToDisable = Joiner.on(" , ").join(FACTORY_1, FACTORY_3);
        when(propertiesService.getPluginProperty(NamedFactoryType.CIPHER.getDisabledFactoriesProperty(), ""))
                .thenReturn(factoriesToDisable);

        List<NamedFactory<Object>> filteredFactories = namedFactoryFilter.filter(defaultFactories, NamedFactoryType.CIPHER);

        Iterable<String> filteredFactoriesNames = Iterables.transform(filteredFactories, DefaultNamedFactoryFilter.TO_NAME);

        assertThat(filteredFactoriesNames, hasItem(FACTORY_2));
        assertThat(filteredFactoriesNames, not(containsInAnyOrder(FACTORY_1, FACTORY_3)));
    }

    @Test
    public void testEmptyStringFactoryFiltering() throws Exception {
        String factoriesToDisable = Joiner.on(",").join(FACTORY_1, " ", "", FACTORY_3);
        when(propertiesService.getPluginProperty(NamedFactoryType.CIPHER.getDisabledFactoriesProperty(), ""))
                .thenReturn(factoriesToDisable);

        List<NamedFactory<Object>> filteredFactories = namedFactoryFilter.filter(defaultFactories, NamedFactoryType.CIPHER);

        Iterable<String> filteredFactoriesNames = Iterables.transform(filteredFactories, DefaultNamedFactoryFilter.TO_NAME);

        assertThat(filteredFactoriesNames, hasItem(FACTORY_2));
        assertThat(filteredFactoriesNames, not(containsInAnyOrder(FACTORY_1, FACTORY_3)));
    }

    @Test
    public void testNonExistingFactoryFiltering() throws Exception {
        String factoriesToDisable = Joiner.on(",").join(FACTORY_1, FACTORY_3, "non-existing factory name");
        when(propertiesService.getPluginProperty(NamedFactoryType.CIPHER.getDisabledFactoriesProperty(), ""))
                .thenReturn(factoriesToDisable);

        List<NamedFactory<Object>> filteredFactories = namedFactoryFilter.filter(defaultFactories, NamedFactoryType.CIPHER);

        Iterable<String> filteredFactoriesNames = Iterables.transform(filteredFactories, DefaultNamedFactoryFilter.TO_NAME);

        assertThat(filteredFactoriesNames, hasItem(FACTORY_2));
        assertThat(filteredFactoriesNames, not(containsInAnyOrder(FACTORY_1, FACTORY_3)));
    }

    @Test
    public void testCaseSensitiveFactoryFiltering() throws Exception {
        String factoriesToDisable = Joiner.on(",").join(FACTORY_1, FACTORY_3.toUpperCase());
        when(propertiesService.getPluginProperty(NamedFactoryType.CIPHER.getDisabledFactoriesProperty(), ""))
                .thenReturn(factoriesToDisable);

        List<NamedFactory<Object>> filteredFactories = namedFactoryFilter.filter(defaultFactories, NamedFactoryType.CIPHER);

        Iterable<String> filteredFactoriesNames = Iterables.transform(filteredFactories, DefaultNamedFactoryFilter.TO_NAME);

        assertThat(filteredFactoriesNames, hasItem(FACTORY_2));
        assertThat(filteredFactoriesNames, hasItem(FACTORY_3));
        assertThat(filteredFactoriesNames, not(containsInAnyOrder(FACTORY_1)));
    }

    private static final class DefaultNamedFactory implements NamedFactory<Object> {
        private final String name;

        public DefaultNamedFactory(String name) {
            this.name = name;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public Object create() {
            return null;
        }
    }
}
