package cz.moneta.test.harness.connectors.mobile.ios;

import cz.moneta.test.harness.connectors.mobile.AppiumMobileConnector;
import cz.moneta.test.harness.context.StoreAccessor;
import cz.moneta.test.harness.exception.HarnessException;
import cz.moneta.test.harness.support.mobile.MobileLookup;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.remote.AutomationName;
import io.appium.java_client.remote.IOSMobileCapabilityType;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.*;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class IosConnector extends AppiumMobileConnector<IOSDriver> {
    private IOSDriver iosDriver;

    public IosConnector(Function<String, By> defaultLookup, StoreAccessor store, String host, String deviceName, String udid, String bundleId, String appBinaryPath) {
        super(defaultLookup, store, host, appBinaryPath);
        this.udid = udid;
        this.bundleId = bundleId;
        this.deviceName = deviceName;
        this.iosDriver = initDriver();
    }

    @Override
    protected IOSDriver getDriver() {
        return iosDriver;
    }

    private IOSDriver initDriver() {
        return startDriver()
                .map(this::startRecording)
                .flatMap(d -> Stream.<Supplier<Optional<IOSDriver>>>of(
                        () -> launchExistingApp(d))
                        .map(Supplier::get)
                        .filter(Optional::isPresent)
                        .map(Optional::get)
                        .findFirst())
                .orElseThrow(() -> new HarnessException("App was not started"));
    }

    private Optional<IOSDriver> startDriver() {
        return Optional.ofNullable(host)
                .map(cs -> {
                    DesiredCapabilities capabilities = new DesiredCapabilities();
                    setDesiredCapabilities(Platform.IOS, deviceName, udid, AutomationName.IOS_XCUI_TEST, capabilities);
                    mobileLanguage(capabilities, "en_US", "cs_CZ");
                    capabilities.setCapability(IOSMobileCapabilityType.BUNDLE_ID, this.bundleId);
                    try {
                        return new IOSDriver(new URL(cs), capabilities);
                    } catch (MalformedURLException e) {
                        throw new HarnessException(String.format("Appium connection to IOS device is invalid: %s", cs), e);
                    } catch (WebDriverException e) {
                        if (e.getCause() instanceof ConnectException) {
                            throw new HarnessException(String.format("Cannot connect to Appium on %s. Is your Appium Desktop running?", cs), e);
                        } else {
                            throw new HarnessException("Ios driver didn't start, please check the connect to server", e);
                        }
                    }
                });
    }

    @Override
    public void captureVideo(String videoFileName) {
        if (!checkVideoRecord()) {
            return;
        }
        String videoPath = store.getConfig("appium.video.path", "target/videos");
        File file = Paths.get(videoPath, videoFileName).toFile();
        try {
            FileUtils.writeByteArrayToFile(file, Base64.getDecoder().decode(iosDriver.stopRecordingScreen()));
        } catch (IOException e) {
            throw new IllegalStateException("Unable to write " + videoFileName + " to " + videoPath, e);
        }
    }

    private IOSDriver startRecording(IOSDriver driver) {
        if (checkVideoRecord()) {
            driver.startRecordingScreen();
        }
        return driver;
    }

    @Override
    public void resetApp() {
        iosDriver.removeApp(bundleId);
        iosDriver.installApp(appBinaryPath);
        iosDriver.activateApp(bundleId);
    }

    @Override
    public void close() {
        iosDriver.closeApp();
    }

    public void hideIosKeyboard(HideIosKeyboardButton hideIosKeyboardButton) {
        click(hideIosKeyboardButton.getValue(), MobileLookup.ID);
    }

    public void executeScrollScript(String name, String direction) {
        Map<String, Object> args = new HashMap<>();
        args.put("direction", direction);
        args.put("name", name);
        Optional.of(getDriver())
                .filter(JavascriptExecutor.class::isInstance)
                .map(JavascriptExecutor.class::cast)
                .ifPresent(script -> {
                    try {
                        script.executeScript("mobile: scroll", args);
                    } catch (NoSuchElementException e) {
                        throw new HarnessException("Element " + name + " is not present");
                    }
                });
    }

    //TODO add in the future
    public void pressIosKeys() {

    }
}
