package com.atlassian.stash.internal.bootstrap;

import com.atlassian.bitbucket.server.ApplicationMode;
import com.atlassian.crowd.manager.application.ApplicationManager;
import com.atlassian.crowd.manager.directory.DirectoryManager;
import com.atlassian.stash.internal.config.ConfigurationService;
import com.atlassian.stash.internal.config.RemoveSetupConfigurationRequest;
import com.atlassian.stash.internal.license.InternalLicenseService;
import com.atlassian.stash.internal.license.LicenseHelper;
import com.atlassian.stash.internal.server.ApplicationPropertyDao;
import com.atlassian.stash.internal.server.InternalApplicationPropertiesService;
import com.atlassian.stash.internal.user.GlobalPermissionDao;
import com.atlassian.stash.internal.user.InternalNormalUser;
import com.atlassian.stash.internal.user.StashUserDao;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.runners.MockitoJUnitRunner;

import java.net.URI;
import java.util.Properties;

import static com.atlassian.bitbucket.server.ApplicationMode.DEFAULT;
import static com.atlassian.bitbucket.server.ApplicationMode.MIRROR;
import static com.atlassian.stash.internal.ApplicationConstants.*;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class DevBootstrapOperationTest extends BootstrapOperationTestBase {
    private InternalNormalUser devAdminUser;
    private InternalNormalUser devUser;

    @Before
    public void setUp() throws Throwable {
        super.setUp();

        devAdminUser = new InternalNormalUser.Builder().name("admin").build();
        devUser = new InternalNormalUser.Builder().name("user").build();

        when(userDao.findByName("admin")).thenReturn(devAdminUser);
        when(userDao.findByName("user")).thenReturn(devUser);
        when(propertiesService.getMode()).thenReturn(DEFAULT);
        bootstrapOperation = new LocalhostDevBootstrapOperation(directoryManager, propertiesService,
                userDao, applicationProperties, applicationManager, licenseHelper, configurationService,
                globalPermissionDao, applicationPropertyDao, licenseService);
    }

    @Test
    public void testIsSetupAfterBootstrapWithoutAnyUnattendedProperties() throws Exception {
        mockInteractionsForNoExistingDirectory();

        // we verify the default dev license was set later but must mock it first when asked for it
        when(licenseHelper.getDecoded()).thenReturn(dualLicense);

        bootstrapOperation.perform();

        verifyInteractionsForNoExistingDirectory();

        verifyStashUsersGroupPermsSet();

        verify(licenseService).set(DevBootstrapOperation.LICENSE_CLUSTERED);

        verifyDefaultDevUsersCreatedForMode(DEFAULT);

        verify(propertiesService).setSetup(true);
        verify(propertiesService).setBaseURL(new URI("http://localhost:7990/bitbucket"));

        verify(configurationService, never()).removeSetupProperties(any(RemoveSetupConfigurationRequest.class));
    }

    @Test
    public void testIsSetupAfterBootstrapInMirrorModeWithAllSupportedUnattendedProperties() throws Exception {
        when(propertiesService.getMode()).thenReturn(MIRROR);
        bootstrapOperation = new LocalhostDevBootstrapOperation(directoryManager, propertiesService, userDao,
                applicationProperties, applicationManager, licenseHelper, configurationService, globalPermissionDao,
                applicationPropertyDao, licenseService);

        mockInteractionsForNoExistingDirectory();

        // we verify the dev license was set later but must mock it first when asked for it
        when(licenseHelper.getDecoded()).thenReturn(dualLicense);

        // mock up unattended properties including the sysadmin details which will be ignored
        when(applicationProperties.getProperty(eq(SETUP_LICENSE))).thenReturn(DevBootstrapOperation.LICENSE_CLUSTERED);
        when(applicationProperties.getProperty(eq(SETUP_BASE_URL))).thenReturn(BASE_URL);
        when(applicationProperties.getProperty(eq(SETUP_DISPLAY_NAME))).thenReturn(APP_DISPLAY_NAME);
        when(applicationProperties.getProperty(eq(SETUP_USER_NAME))).thenReturn("someadmin");
        when(applicationProperties.getProperty(eq(SETUP_USER_PASSWORD))).thenReturn("someadmin");
        when(applicationProperties.getProperty(eq(SETUP_USER_DISPLAY_NAME))).thenReturn("Some Admin");
        when(applicationProperties.getProperty(eq(SETUP_USER_EMAIL_ADDRESS))).thenReturn("someadmin@example.com");
        when(applicationProperties.getProperty(eq(BootstrapOperation.KEY_MIRROR_UPSTREAM))).thenReturn("http://example.com");
        when(applicationProperties.getProperty(eq(BootstrapOperation.KEY_MIRROR_TYPE))).thenReturn("server");

        when(licenseHelper.decode(eq(DevBootstrapOperation.LICENSE_CLUSTERED))).thenReturn(dualLicense);

        bootstrapOperation.perform();

        verifyInteractionsForNoExistingDirectory();

        verifyStashUsersGroupPermsSet();

        verifyDefaultDevUsersCreatedForMode(MIRROR);

        ArgumentCaptor<RemoveSetupConfigurationRequest> requestCaptor = captureRemoveConfigRequest();
        verify(configurationService).removeSetupProperties(requestCaptor.capture());
        // sys admins andlicenses can't be set for mirrors
        assertFalse(requestCaptor.getValue().isRemoveSysAdmin());
        assertFalse(requestCaptor.getValue().isRemoveLicense());
        // everything else should be commented out
        assertTrue(requestCaptor.getValue().isRemoveBaseUrl());
        assertTrue(requestCaptor.getValue().isRemoveDisplayName());

        verify(licenseService).setUnvalidated(BootstrapOperation.LICENSE_MIRROR);
        verify(propertiesService).setSetup(true);
        verify(propertiesService).setBaseURL(new URI(BASE_URL));
        verify(propertiesService).setDisplayName(APP_DISPLAY_NAME);
    }

    protected void verifyDefaultDevUsersCreatedForMode(ApplicationMode mode) throws Exception {
        verifyUserCreated("admin", "admin", "admin", "Administrator", "admin@example.com", "admin", times(1));
        verifySysAdminPermissionGranted(devAdminUser, times(1));
        if (mode == DEFAULT) {
            verifyUserCreated("user", "user", "user", "Administrator", "user@example.com", "user", times(1));
            verifySysAdminPermissionGranted(devUser, never());
        } else {
            verifyUserCreated("user", "user", "user", "Administrator", "user@example.com", "user", never());
            verifySysAdminPermissionGranted(devUser, never());
        }
    }

    private class LocalhostDevBootstrapOperation extends DevBootstrapOperation {
        public LocalhostDevBootstrapOperation(DirectoryManager directoryManager, InternalApplicationPropertiesService propertiesService,
                                              StashUserDao userDao, Properties applicationProperties, ApplicationManager appManager,
                                              LicenseHelper licenseHelper, ConfigurationService configurationService,
                                              GlobalPermissionDao globalPermissionDao, ApplicationPropertyDao applicationPropertyDao,
                                              InternalLicenseService licenseService) {
            super(directoryManager, propertiesService, userDao, applicationProperties, appManager,
                    licenseHelper, configurationService, globalPermissionDao,
                    applicationPropertyDao, licenseService);
        }

        @Override
        protected String getBaseUrlMachineName() {
            return "localhost";
        }
    }
}