package cz.moneta.test.harness.connectors.web;

import cz.moneta.test.harness.constants.HarnessConfigConstants;
import cz.moneta.test.harness.context.StoreAccessor;
import cz.moneta.test.harness.exception.HarnessException;
import org.apache.commons.lang3.tuple.Pair;
import org.openqa.selenium.By;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_DOWNLOAD_DIRECTORY;
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_GRID_PLATFORM;

public class SeleniumChromeConnector extends SeleniumWebConnector {

    public SeleniumChromeConnector(Function<String, By> defaultLookup, StoreAccessor store, String... extraOptions) {
        super(defaultLookup, store, extraOptions);
    }

    @Override
    protected Pair<WebDriver, Boolean> getLocalWebDriver() {
        setupDriver();

        return Optional.ofNullable(store.getConfig("selenium.chromebinary.path"))
                .map(bin -> {
                    ChromeOptions chromeOptions = getChromeOptions();
                    chromeOptions.setBinary(bin);
                    return chromeOptions;
                })
                .map(o -> Pair.<WebDriver, Boolean>of(new ChromeDriver(o), false))
                .orElse(null);
    }

    private void setupDriver() {
        Optional.ofNullable(store.getConfig("selenium.chrome.webdriver.path"))
                .map(path -> {
                    System.setProperty("webdriver.chrome.driver", path);
                    return path;
                })
                .orElseThrow(() -> new AssertionError("Please provide path to the Chrome driver using selenium.chrome.webdriver.path configuration key or use a selenium grid"));
    }

    private ChromeOptions getChromeOptions() {
        ChromeOptions chromeOptions = new ChromeOptions();

        Optional.ofNullable(store.getConfig("selenium.webdriver.chrome.options"))
                .map(o -> o.split("\\s+"))
                .ifPresent(chromeOptions::addArguments);

        chromeOptions.addArguments(extraOptions);

        this.driverUtils().addDefaultDesiredCapabilities(chromeOptions);

        this.driverUtils().addAdditionalDesiredCapabilities(chromeOptions, "selenium.chrome.capabilities");

        // Set up Selenium Grid session name
        chromeOptions.setCapability("se:name", store.get(HarnessConfigConstants.TEST_UNIQUE_ID).toString());

        // Choose which Selenium Grid node will be used
        Optional.ofNullable(store.getConfig(SELENIUM_GRID_PLATFORM))
                .ifPresent(platform -> chromeOptions.setPlatformName(platform));

        // Overrides default download directory in Chrome profile preferences, http://chromedriver.chromium.org/capabilities
        // By default headless mode does not download any files unless download directory is set.
        Optional.ofNullable(store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY))
                .ifPresent(experimentalOptions -> {
                    Map<String, Object> prefs = new HashMap<String, Object>();
                    prefs.put("download.default_directory", store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY));
                    chromeOptions.setExperimentalOption("prefs", prefs);
                });

        return chromeOptions;
    }

    @Override
    protected Capabilities getDesiredRemoteCapabilities() {
        return new DesiredCapabilities(getChromeOptions());
    }

    @Override
    public void downloadFileViaIePrompt(int waitInSeconds) {
        throw new HarnessException("This method is not implemented on Chrome. Use capabilities settings instead.");
    }
}
