package cz.moneta.test.harness.support.mobile.share;

import cz.moneta.test.harness.endpoints.MobileEndpoint;
import cz.moneta.test.harness.support.mobile.MobileLookup;
import cz.moneta.test.harness.support.web.DynamicPathPart;
import cz.moneta.test.harness.support.web.SelectByValue;
import cz.moneta.test.harness.support.web.Until;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;

public class AnnotationsBuilder {

    protected static void doWait(String[] value, int waitSecondsForElement, MobileLookup by, Until until, int explicitWaitSeconds, boolean isStringDynamicXpath, MobileEndpoint endpoint, Object[] args) {
        Stream.<Optional<Runnable>>of(
                Optional.of(value)
                        .filter(ArrayUtils::isNotEmpty)
                        .map(present -> () -> endpoint.waitForElementsToLoad(waitSecondsForElement, by,
                                until, constructDynamicElements(present, isStringDynamicXpath, args))),
                Optional.of(explicitWaitSeconds)
                        .filter(s -> s > 0)
                        .map(s -> () -> endpoint.sleepSeconds(s)))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .forEach(Runnable::run);
    }

    protected static void doCustomAction(Object proxy, Method method, Object[] args) {
        Optional.ofNullable(method)
                .filter(m -> m.isAnnotationPresent(MobileCustomAction.class))
                .filter(Method::isDefault)
                .ifPresent(m -> Builders.invokeDefaultMethod(proxy, m, args));
    }

    protected static void doScroll(Direction direction, Annotation annotation, String untilElementPresent, MobileLookup by, MobileEndpoint endpoint) {
        Stream.<Optional<Runnable>>of(
                Optional.of(annotation)
                        .filter(s -> Direction.LEFT == direction && StringUtils.isNotEmpty(untilElementPresent))
                        .map(s -> () -> endpoint.scrollToElement(Direction.LEFT, by,
                                untilElementPresent)),
                Optional.of(direction)
                        .filter(Direction.LEFT::equals)
                        .map(d -> () -> endpoint.scroll(Direction.LEFT)),
                Optional.of(annotation)
                        .filter(s -> Direction.RIGHT == direction && StringUtils.isNotEmpty(untilElementPresent))
                        .map(s -> () -> endpoint.scrollToElement(Direction.RIGHT, by,
                                untilElementPresent)),
                Optional.of(direction)
                        .filter(Direction.RIGHT::equals)
                        .map(d -> () -> endpoint.scroll(Direction.RIGHT)),
                Optional.of(annotation)
                        .filter(s -> Direction.UP == direction && StringUtils.isNotEmpty(untilElementPresent))
                        .map(s -> () -> endpoint.scrollToElement(Direction.UP, by,
                                untilElementPresent)),
                Optional.of(direction)
                        .filter(Direction.UP::equals)
                        .map(d -> () -> endpoint.scroll(Direction.UP)),
                Optional.of(annotation)
                        .filter(s -> Direction.DOWN == direction && StringUtils.isNotEmpty(untilElementPresent))
                        .map(s -> () -> endpoint.scrollToElement(Direction.DOWN, by,
                                untilElementPresent)),
                Optional.of(direction)
                        .filter(Direction.DOWN::equals)
                        .map(d -> () -> endpoint.scroll(Direction.DOWN)))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .findFirst()
                .ifPresent(Runnable::run);
    }

    protected static void doCheckElementContent(Annotation[] annotations, String checkValue, MobileLookup by, MobileEndpoint endpoint, Method method, Object[] args) {
        checkAnnotationLength(annotations, method, args);
        Arrays.stream(annotations)
                .filter(check -> StringUtils.isNotBlank(checkValue))
                .forEach(check -> {
                    String value = getSelectValue(args[0]);
                    endpoint.checkElementContent(checkValue, value, by);
                });
    }

    protected static String getSelectValue(Object selectObject) {
        return selectObject instanceof SelectByValue ? ((SelectByValue) selectObject).getValue() : String.valueOf(selectObject);
    }

    protected static void checkAnnotationLength(Annotation[] annotations, Method method, Object[] args) {
        annotations = method.getAnnotationsByType(Annotation.class);
        if (annotations.length > 0 && (args == null || annotations.length != args.length)) {
            throw new IllegalStateException("The number of arguments passed to a annotated method '"
                    + method.getDeclaringClass().getSimpleName() + "#" + method.getName()
                    + "' must match the annotation count!");
        }
    }

    protected static String constructDynamicElement(String path, boolean isStringDynamicXpath, Object[] args) {
        Optional<Object[]> dynamicXpath;
        if (isStringDynamicXpath) {
            dynamicXpath = Optional.ofNullable(args)
                    .map(a -> Arrays.stream(a)
                            .filter(String.class::isInstance)
                            .map(String.class::cast)
                            .toArray());
        } else {
            dynamicXpath = Optional.ofNullable(args)
                    .map(a -> Arrays.stream(a)
                            .filter(DynamicPathPart.class::isInstance)
                            .map(DynamicPathPart.class::cast)
                            .map(DynamicPathPart::getValue)
                            .toArray());
        }
        return dynamicXpath.map(a -> String.format(path, a))
                .orElse(path);
    }

    private static String[] constructDynamicElements(String[] path, boolean isStringDynamicXpath, Object[] args) {
        return Arrays.stream(path)
                .map(p -> constructDynamicElement(p, isStringDynamicXpath, args))
                .toArray(String[]::new);
    }
}
