package it.com.atlassian.bitbucket.mirror;

import com.atlassian.bitbucket.test.BaseFuncTest;
import com.atlassian.bitbucket.test.DefaultFuncTestData;
import com.atlassian.bitbucket.test.SetupTestHelper;
import com.atlassian.pageobjects.TestedProductFactory;
import com.atlassian.pageobjects.page.LoginPage;
import com.atlassian.webdriver.bitbucket.BitbucketTestedProduct;
import com.atlassian.webdriver.bitbucket.page.admin.mirror.*;
import com.atlassian.webdriver.bitbucket.page.mirror.BitbucketMirrorTestedProduct;
import com.atlassian.webdriver.bitbucket.page.setup.MirrorSetupPage;
import com.atlassian.webdriver.testing.annotation.WindowSize;
import com.atlassian.webdriver.testing.rule.WebDriverScreenshotRule;
import com.atlassian.webdriver.testing.rule.WindowSizeRule;
import org.junit.Test;
import org.junit.rules.TestRule;

import java.io.IOException;
import java.util.List;

import static com.atlassian.bitbucket.test.TestRuleOrderingSyntax.outerRules;
import static org.junit.Assert.*;

@WindowSize(maximize = true)
public class SetupMirrorTest extends BaseFuncTest {

    private static final String MIRROR_NAME = "BB Europe";

    private final BitbucketMirrorTestedProduct MIRROR = createBitbucketMirror("bitbucket-mirror");
    private final BitbucketTestedProduct UPSTREAM = createBitbucketInstance("bitbucket-upstream");

    @Test
    public void testSetup() throws IOException, InterruptedException {
        verifyTestRequirements();

        // switch to production mode to test HTTPS checks
        setDevMode(false);
        testSetupFormValidation();

        // switch back to dev.mode to allow setup with http URLs
        setDevMode(true);
        setupMirror();
        MirrorConfigPage configPage = acceptMirror();

        // now that the mirror is integrated, we can switch back to production mode
        // to get as close to the 'real' thing as we can
        setDevMode(false);
        configureMirroredProjects(configPage);
    }

    @Override
    protected List<TestRule> getRuleChain() {
        return outerRules(
                new WebDriverScreenshotRule(),
                new WindowSizeRule())
                .around(super.getRuleChain());
    }

    private static BitbucketTestedProduct createBitbucketInstance(String instanceId) {
        return TestedProductFactory.create(BitbucketTestedProduct.class, instanceId, null);
    }

    private static BitbucketMirrorTestedProduct createBitbucketMirror(String instanceId) {
        return TestedProductFactory.create(BitbucketMirrorTestedProduct.class, instanceId, null);
    }

    private MirrorConfigPage acceptMirror() throws IOException {
        MirrorsAdminPage mirrorAdmin = UPSTREAM.visit(LoginPage.class).loginAsSysAdmin(MirrorsAdminPage.class);

        List<MirrorItem> mirrors = mirrorAdmin.getMirrors();
        List<MirrorRequestItem> requests = mirrorAdmin.getMirrorRequests();
        assertEquals("no mirrors were expected", 0, mirrors.size());
        assertEquals("1 mirroring request was expected", 1, requests.size());

        // switch to dev mode to allow the non-https add-on installation
        MirrorTestUtils.setDevMode(UPSTREAM.getProductInstance(), true);
        MirrorTestUtils.setDevMode(MIRROR.getProductInstance(), true);

        // select the mirroring request
        MirrorRequestPage requestPage = requests.get(0).click();
        // approve the request
        MirrorConfigPage configPage = requestPage.clickApprove();

        mirrors = configPage.getMirrors();
        requests = configPage.getMirrorRequests();
        assertEquals("1 mirror was expected", 1, mirrors.size());
        assertEquals("No mirroring request were expected", 0, requests.size());

        return configPage;
    }

    private void configureMirroredProjects(MirrorConfigPage configPage) throws IOException {
        configPage.addProject(DefaultFuncTestData.getProject1Name());
    }

    private void setupMirror() throws IOException {
        MirrorSetupPage page = MIRROR.visit(MirrorSetupPage.class);

        page.setMirrorBaseUrl(MIRROR.getProductInstance().getBaseUrl());
        page.setMirrorName(MIRROR_NAME);
        page.setPrimaryServerUrl(UPSTREAM.getProductInstance().getBaseUrl());

        // no field errors
        assertFalse("No errors expected for mirror base URL", page.hasMirrorBaseUrlWarning());
        assertFalse("No errors expected for primary URL", page.hasPrimaryServerUrlWarning());
        assertFalse(page.hasMirrorNameWarning());

        page.clickSubmit();
    }

    private void setDevMode(boolean enabled) throws  IOException {
        MirrorTestUtils.setDevMode(UPSTREAM.getProductInstance(), enabled);
        MirrorTestUtils.setDevMode(MIRROR.getProductInstance(), enabled);
    }

    private void testSetupFormValidation() {
        MirrorSetupPage page = MIRROR.visit(MirrorSetupPage.class);

        page.setMirrorBaseUrl(MIRROR.getProductInstance().getBaseUrl());
        page.setMirrorName("");
        page.setPrimaryServerUrl(UPSTREAM.getProductInstance().getBaseUrl());

        page.clickSubmit();

        page = MIRROR.getPageBinder().bind(MirrorSetupPage.class);

        assertTrue("Expected a warning about an empty mirror name", page.hasMirrorNameWarning());
        assertTrue("Expected a HTTPS warning about the mirror base URL", page.hasMirrorBaseUrlWarning());
        assertTrue("Expected a HTTPS warning about the primary URL", page.hasPrimaryServerUrlWarning());
    }

    private void verifyTestRequirements() {
        String mirrorBaseUrl = MIRROR.getProductInstance().getBaseUrl();
        String upstreamBaseUrl = UPSTREAM.getProductInstance().getBaseUrl();
        assertFalse("This test requires a mirror instance which is not setup yet. (url = " + mirrorBaseUrl + ")",
                SetupTestHelper.isSetup(mirrorBaseUrl));
        assertTrue("This test requires a fully set up upstream instance. (url = " + upstreamBaseUrl + ")",
                SetupTestHelper.isSetup(UPSTREAM.getProductInstance().getBaseUrl()));
    }
}
