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

import cz.moneta.test.harness.support.web.Clickable;
import cz.moneta.test.harness.support.web.Lookup;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;
import java.util.Optional;

public class Clicks {

    private final SeleniumWebConnector connector;
    private static final int CLICKABLE_TIMEOUT = 5;

    private final static String DOUBLE_CLICK_JS = "var evt = document.createEvent('MouseEvents');"
            + "evt.initMouseEvent('dblclick',true, true, window, 10, 0, 0, 0, 0, false, false, false, false, 0,null);"
            + "arguments[0].dispatchEvent(evt);";

    private final static String JS_CLICK_SCRIPT = "console.debug('jsClick start'); " +
            "setTimeout(function(elem){ elem.click();}, 0, arguments[0]); " +
            "console.debug('jsClick end');";

    public Clicks(SeleniumWebConnector connector) {
        this.connector = connector;
    }

    public void click(Clickable clickable, Lookup lookup) {
        WebElement element = connector.waits().waitForLazyElement(clickable.getPath(), lookup);
        isClickable(element).click();
    }

    protected WebElement isClickable(WebElement webElement) {
        WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(CLICKABLE_TIMEOUT));
        return wait.until(ExpectedConditions.elementToBeClickable(webElement));
    }

    protected WebElement isClickable(Clickable clickable, Lookup lookup) {
        WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(CLICKABLE_TIMEOUT));
        return wait.until(ExpectedConditions.elementToBeClickable(connector.resolveLookup(lookup).apply(clickable.getPath())));
    }

    public void doubleClick(Clickable clickable, Lookup lookup) {
        Actions actions = new Actions(connector.getDriver());
        WebElement element = connector.waits().waitForLazyElement(clickable.getPath(), lookup);
        actions.doubleClick(element);
    }

    public void jsDoubleClick(Clickable clickable, Lookup lookup) {
        Optional.of(connector.getDriver())
                .filter(JavascriptExecutor.class::isInstance)
                .map(JavascriptExecutor.class::cast)
                .ifPresent(e -> e.executeScript(DOUBLE_CLICK_JS,
                        connector.waits().waitForLazyElement(clickable.getPath(), lookup)));
    }

    public void jsClick(Clickable clickable, Lookup lookup) {
        Optional.of(connector.getDriver())
                .filter(JavascriptExecutor.class::isInstance)
                .map(JavascriptExecutor.class::cast)
                .ifPresent(e -> e.executeScript(JS_CLICK_SCRIPT,
                        connector.waits().waitForLazyElement(clickable.getPath(), lookup)));
    }
}