package net.northking.cctp.se.device.bean;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.TouchAction;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.touch.WaitOptions;
import io.appium.java_client.touch.offset.PointOption;
import lombok.extern.slf4j.Slf4j;
import net.northking.cctp.common.http.ResultWrapper;
import net.northking.cctp.element.core.DeviceDriver;
import net.northking.cctp.element.core.exception.ExecuteException;
import net.northking.cctp.element.core.exception.ParamMistakeException;
import net.northking.cctp.mobile.driver.constans.AutomationRequestCmd;
import net.northking.cctp.mobile.driver.driver.MobileDriver;
import net.northking.cctp.mobile.driver.entity.CmdAutomationRequest;
import net.northking.cctp.mobile.driver.entity.CmdAutomationResponse;
import net.northking.cctp.se.config.AtuServerConfig;
import net.northking.cctp.se.config.MobileAutomationSelectorConfig;
import net.northking.cctp.se.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.openqa.selenium.SessionNotCreatedException;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import static io.appium.java_client.remote.AndroidMobileCapabilityType.*;
import static io.appium.java_client.remote.IOSMobileCapabilityType.USE_PREBUILT_WDA;
import static io.appium.java_client.remote.IOSMobileCapabilityType.WEB_DRIVER_AGENT_URL;
import static io.appium.java_client.remote.MobileCapabilityType.*;
import static org.openqa.selenium.remote.CapabilityType.PLATFORM_NAME;

@Slf4j
public class MobileDeviceConnection extends AbstractDeviceConnection {

    private static final String APP_DOWNLOAD_URL = "http://%s/engine/downloadApp";

    private static final String RECORD_DEVICE_PER = "http://%s/engine/recordDevicePer";

    private static final String STOP_RECORD_DEVICE_PER = "http://%s/engine/stopRecordDevicePer";

    private final Integer defaultUpperPort = 8808;

    private String remoteAddress;

    private static final int VALUE_NEW_COMMAND_TIMEOUT = 240;

    private static final int PER_CONNECT_TIMEOUT = 1000;
    private static final int PER_READ_TIMEOUT = 1000;

    private static final String USE_XCTESTRUN_FILE = "useXctestrunFile";

    private static final String SKIP_LOG_CAPTURE = "skipLogCapture";

    private MobileDriverKeepThread keepThread;

    public static final String PACKAGE_NAME = "appPackage";
    public static final String APP_NAME = "appName";
    public static final String IS_REINSTALL = "forceReInstall";
    public static final String IS_CLEAN_DATA = "forceCleanData";
    public static final String IS_APP_RESTART = "restartApp";
    public static final String IS_RECORD_DEVICE_PER = "debug";

    private DevicePerInfo devicePerInfo;

    private boolean isRecord;

    public MobileDeviceConnection(String deviceToken, DeviceInfo deviceInfo) throws Exception {
        super(deviceToken, deviceInfo);
    }

    @Override
    protected void initDriver() {
        log.info("start init device[{}] driver..........", deviceInfo.getDeviceId());
        this.remoteAddress = deviceInfo.getConnectAddress() + ":" + (null == deviceInfo.getPort() ? defaultUpperPort : deviceInfo.getPort());
        try {
            this.deviceDriver = connectDevice();
            initKeepDriverThread();
        } catch (ExecuteException e) {
            throw e;
        } catch (Exception e) {
            log.error("设备【{}】创建driver失败", deviceInfo.getDeviceId(), e);
            throw new ExecuteException("创建设备连接失败");
        }
    }

    private void initKeepDriverThread() {
        this.keepThread = new MobileDriverKeepThread(this.deviceDriver, VALUE_NEW_COMMAND_TIMEOUT - 10, deviceInfo.getDeviceId());
        this.keepThread.start();
    }

    private DeviceDriver connectDevice() {
        Map<String, String> otherInfo = deviceInfo.getOtherInfo();
        String appiumHost = deviceInfo.getConnectAddress();
        Integer appiumPort = Integer.parseInt(otherInfo.get("appiumPort"));
        String appiumContextPath = otherInfo.get("appiumContextPath");
        URL appiumServerUrl = null;
        try {
            appiumServerUrl = new URL(String.format("http://%s:%d%s", appiumHost, appiumPort, appiumContextPath));
        } catch (MalformedURLException e) {
            log.error("appium-server地址有误：{}", String.format("http://%s:%d%s", appiumHost, appiumPort, appiumContextPath));
        }
        DeviceDriver driver = null;
        if ("0".equalsIgnoreCase(deviceInfo.getPlatform()) || "android".equalsIgnoreCase(deviceInfo.getPlatform())) {
            driver = createAndroidDriver(appiumServerUrl, deviceInfo.getDeviceId(), null);
        } else if ("1".equalsIgnoreCase(deviceInfo.getPlatform()) || "ios".equalsIgnoreCase(deviceInfo.getPlatform())) {
            driver = createIosDriver(appiumServerUrl, deviceInfo, null);
        }
        if (driver != null) {
            log.info("Driver for device[{}] created successfully.", deviceInfo.getDeviceId());
        } else {
            log.error("Driver获取为空，设备连接失败");
            throw new ExecuteException("设备驱动创建失败，请稍后再试");
        }
        return driver;
    }

    private DeviceDriver createIosDriver(URL appiumServerUrl, DeviceInfo deviceInfo, Map<String, String> others) {
        MobileAutomationSelectorConfig selectorConfig = SpringUtils.getBean(MobileAutomationSelectorConfig.class);
        log.info("拿到的自动化类型:{}", selectorConfig.getType());
        if ("appium".equals(selectorConfig.getType())) {  //appium的方式
            IosMobileDeviceDriver driver = null;
            DesiredCapabilities capabilities = initIosCapabilities(deviceInfo, others);
            try {
                driver = new IosMobileDeviceDriver(appiumServerUrl, capabilities);
            } catch (SessionNotCreatedException e) {
                log.error("设备未连接到上位机", e);
                throw new ExecuteException("设备未接到上位机");
            } catch (WebDriverException e) {
                log.warn("设备【{}】Driver创建失败，重新试一次", deviceInfo.getDeviceId(), e);
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException interruptedException) {
                    log.warn("设备【{}】移除应用时driver出现问题,重新执行。。。。。", deviceInfo.getDeviceId());
                    throw new ExecuteException("执行取消");
                }
                driver = new IosMobileDeviceDriver(appiumServerUrl, capabilities);
            } catch (Exception e) {
                log.error("Driver获取失败，设备连接失败", e);
                throw new ExecuteException("设备驱动创建失败，请稍后再试");
            }
            return driver;
        } else {  //自研方式
            MobileDeviceDriver driver = null;
            try {
                driver = new MobileDeviceDriver(this.remoteAddress, deviceInfo.getDeviceId(), "ios");
            } catch (Exception e) {
                log.error("设备【{}】创建MobileDriver失败，地址：{}", deviceInfo.getDeviceId(), this.remoteAddress, e);
                throw new ExecuteException("设备驱动创建失败，请稍后再试");
            }
            return driver;
        }
    }

    private DesiredCapabilities initIosCapabilities(DeviceInfo deviceInfo, Map<String, String> others) {
        DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
        desiredCapabilities.setCapability(DEVICE_NAME, deviceInfo.getDeviceId());  //设备id
        desiredCapabilities.setCapability(UDID, deviceInfo.getDeviceId());  //设备id
        desiredCapabilities.setCapability(PLATFORM_NAME, "iOS");  //ios
        desiredCapabilities.setCapability(PLATFORM_VERSION, deviceInfo.getSystemVersion());
        desiredCapabilities.setCapability("appium:noSign", true);
        desiredCapabilities.setCapability("appium:skipServerInstallation", false);
        desiredCapabilities.setCapability("appium:skipUnlock", true);
        boolean noReset = true;
        boolean unicodeKeyboard = false;
        boolean resetKeyboard = true;
        if (others != null) {
            String value = others.get(UNICODE_KEYBOARD);
            if (StringUtils.isNotBlank(value)) {
                unicodeKeyboard = value.equalsIgnoreCase("true");
            }
            value = others.get(NO_RESET);
            if (StringUtils.isNotBlank(value)) {
                noReset = value.equalsIgnoreCase("true");
            }
            value = others.get(RESET_KEYBOARD);
            if (StringUtils.isNotBlank(value)) {
                resetKeyboard = value.equalsIgnoreCase("true");
            }
        }
        String wdaUrl = deviceInfo.getWdaAddress();
        if (others != null) {
            String value = others.get(WEB_DRIVER_AGENT_URL);
            if (StringUtils.isNotBlank(value)) {
                wdaUrl = value;
            }
            value = others.get(NO_RESET);
            if (StringUtils.isNotBlank(value)) {
                noReset = value.equalsIgnoreCase("true");
            }
        }
        log.debug("Set ", wdaUrl);
        desiredCapabilities.setCapability(WEB_DRIVER_AGENT_URL, wdaUrl);
        desiredCapabilities.setCapability(NO_RESET, noReset);
        desiredCapabilities.setCapability(NEW_COMMAND_TIMEOUT, VALUE_NEW_COMMAND_TIMEOUT);
        desiredCapabilities.setCapability(USE_PREBUILT_WDA, false);
        desiredCapabilities.setCapability(USE_XCTESTRUN_FILE, false);
        desiredCapabilities.setCapability(SKIP_LOG_CAPTURE, true);
        desiredCapabilities.setCapability(UNICODE_KEYBOARD, unicodeKeyboard);
        desiredCapabilities.setCapability(RESET_KEYBOARD, resetKeyboard);
        log.debug("set wdaUrl:{},deviceName-[{}],udid-[{}],platformName-[{}],appium:noSign-[{}],appium:skipServerInstallation-[{}],appium:skipUnlock-[{}]," +
                        "unicodeKeyboard-[{}],noReset-[{}],resetKeyboard-[{}],newCommandTimeout-[{}],", wdaUrl, deviceInfo.getDeviceId(),
                "android", true, false, true, unicodeKeyboard, noReset, resetKeyboard, VALUE_NEW_COMMAND_TIMEOUT);
        return desiredCapabilities;
    }

    private AndroidMobileDeviceDriver createAndroidDriver(URL appiumServerUrl, String upperUrl, String deviceId, Map<String, String> others) {
        DesiredCapabilities capabilities = initAndroidCapabilities(deviceId, others);
        AndroidMobileDeviceDriver driver = null;
        try {
            driver = new AndroidMobileDeviceDriver(appiumServerUrl, capabilities);
        } catch (SessionNotCreatedException e) {
            log.error("Driver创建失败，设备连接失败", e);
            String errorMessage = String.format("the local port #%s is busy", deviceInfo.getForwardPort());
            String epermMsg = "eperm";  // EPERM 大概率是系统拒绝了这次连接，释放一下端口重新连接
            if (e.getMessage().contains(errorMessage) || e.getMessage().toLowerCase().contains(epermMsg)) {
                log.warn("设备【{}】adb转发的端口【{}】被占用了，释放了重新来", deviceId, deviceInfo.getForwardPort());
                boolean success = AndroidUtil.releaseAdbForwardPort(this.remoteAddress, deviceId);
                if (success) {
                    driver = new AndroidMobileDeviceDriver(appiumServerUrl, capabilities);
                } else {
                    log.warn("设备【{}】adb转发的端口【{}】被占用了，没有释放成功", deviceId, deviceInfo.getForwardPort());
                }
            }
        } catch (WebDriverException e) {
            log.warn("设备【{}】Driver创建失败，重新试一次", deviceId, e);
            try {
                Thread.sleep(1500);
            } catch (InterruptedException interruptedException) {
                log.warn("设备【{}】移除应用时driver出现问题,重新执行。。。。。", deviceInfo.getDeviceId());
                throw new ExecuteException("执行取消");
            }
            driver = new AndroidMobileDeviceDriver(appiumServerUrl, capabilities);
        } catch (Exception e) {
            log.error("设备驱动创建失败", e);
        }
        return driver;
    }

//    private AndroidMobileDeviceDriver createAndroidDriver(URL appiumServerUrl, String deviceId, Map<String, String> others) {
//        AndroidMobileDeviceDriver driver = null;
//        DesiredCapabilities capabilities = initAndroidCapabilities(deviceId, others);
//        try {
//            driver = new AndroidMobileDeviceDriver(appiumServerUrl, capabilities);
//            return driver;
//        } catch (SessionNotCreatedException e) {
//            log.error("设备未连接到上位机", e);
//            throw new ExecuteException("设备无法初始化，请将设备重新拔插后再试");
//        } catch (WebDriverException e) {
//            log.warn("设备【{}】Driver创建失败，重新试一次", deviceId, e);
//            try {
//                Thread.sleep(1500);
//            } catch (InterruptedException interruptedException) {
//                log.warn("设备【{}】移除应用时driver出现问题,重新执行。。。。。", deviceInfo.getDeviceId());
//                throw new ExecuteException("执行取消");
//            }
//            driver = new AndroidMobileDeviceDriver(appiumServerUrl, capabilities);
//        } catch (Exception e) {
//            log.error("设备驱动创建失败", e);
//        }
//        return driver;
//    }

    private DesiredCapabilities initAndroidCapabilities(String deviceId, Map<String, String> others) {
        DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
        desiredCapabilities.setCapability(DEVICE_NAME, deviceId);  //设备id
        desiredCapabilities.setCapability(UDID, deviceId);  //设备id
        desiredCapabilities.setCapability(PLATFORM_NAME, "android");  //android
        desiredCapabilities.setCapability("appium:noSign", true);
        desiredCapabilities.setCapability("appium:skipServerInstallation", false);
        desiredCapabilities.setCapability("appium:skipUnlock", true);
        desiredCapabilities.setCapability("appium:systemPort", deviceInfo.getForwardPort());
        boolean noReset = true;
        boolean unicodeKeyboard = false;
        boolean resetKeyboard = true;
        if (others != null) {
            String value = others.get(UNICODE_KEYBOARD);
            if (StringUtils.isNotBlank(value)) {
                unicodeKeyboard = value.equalsIgnoreCase("true");
            }
            value = others.get(NO_RESET);
            if (StringUtils.isNotBlank(value)) {
                noReset = value.equalsIgnoreCase("true");
            }
            value = others.get(RESET_KEYBOARD);
            if (StringUtils.isNotBlank(value)) {
                resetKeyboard = value.equalsIgnoreCase("true");
            }
        }
        desiredCapabilities.setCapability(UNICODE_KEYBOARD, unicodeKeyboard);
        desiredCapabilities.setCapability(RESET_KEYBOARD, resetKeyboard);
        desiredCapabilities.setCapability(NO_RESET, noReset);
        desiredCapabilities.setCapability(NEW_COMMAND_TIMEOUT, VALUE_NEW_COMMAND_TIMEOUT);
        log.debug("set deviceName-[{}],udid-[{}],platformName-[{}],appium:noSign-[{}],appium:skipServerInstallation-[{}],appium:skipUnlock-[{}]," +
                        "unicodeKeyboard-[{}],noReset-[{}],resetKeyboard-[{}],newCommandTimeout-[{}],", deviceId, deviceId,
                "android", true, false, true, unicodeKeyboard, noReset, resetKeyboard, VALUE_NEW_COMMAND_TIMEOUT);
        return desiredCapabilities;
    }

    /**
     * <p>初始化应用</p>
     *
     * @param config
     */
    @Override
    public void initConfig(Map<String, Object> config) {
        if (null == this.deviceDriver) {
            throw new ExecuteException("设备连接有误，请重新连接设备");
        }
        if (this.deviceDriver instanceof MobileDriver) {
            log.debug("设备【{}】采取自研自动化....................",this.deviceInfo.getDeviceId());
            initApplicationByCustom(config);   //todo:性能数据未获取
        } else {
            log.debug("设备【{}】采取appium自动化....................",this.deviceInfo.getDeviceId());
            initApplicationByAppium(config);
        }
    }

    //自己设计的自动化方式
    private void initApplicationByCustom(Map<String, Object> config) {
        MobileDriver mobileDriver = (MobileDriver) this.deviceDriver;
        String token = UUID.randomUUID().toString();
        CmdAutomationRequest builder = CmdAutomationRequest.builder(AutomationRequestCmd.INIT_APP, token, config);
        mobileDriver.send(JSON.toJSONString(builder));
        try {
            boolean success = false;
            mobileDriver.getSocketClient().semaphore = new Semaphore(0);
            if (mobileDriver.getSocketClient().semaphore.tryAcquire(300, TimeUnit.SECONDS)) {
                CmdAutomationResponse response = mobileDriver.getSocketClient().getResponse();
                if (token.equals(response.getStepToken())) {
                    success = response.isSuccess();
                } else {
                    log.error("收到回来的消息token:{}，发过去的token:{}。对不上，收到的内容：{}", response.getStepToken(), token, JSON.toJSONString(response));
                }
                log.debug("应用初始化结果：{}", success);
                if (!success) {
                    throw new ExecuteException("应用初始化失败");
                }
            } else {
                throw new ExecuteException("初始化应用超时");
            }
        } catch (InterruptedException e) {
            log.warn("用户取消初始化应用");
            throw new ExecuteException("取消应用初始化");
        }
    }
    //采用appium的方式
    private void initApplicationByAppium(Map<String, Object> config) {
        boolean recordDevicePer = (boolean) config.get("debug");
        if (!recordDevicePer) {
            //开启监控设备的性能
            this.isRecord = true;
            devicePerInfo = new DevicePerInfo();
            AppPerInfo appPerInfo = new AppPerInfo();
            devicePerInfo.setAppPerInfo(appPerInfo);
            devicePerInfo.setCaseId((String) config.get("caseId"));
        }
        String deviceId = this.deviceInfo.getDeviceId();
        boolean reInstall = false;
        boolean cleanData = false;
        if (null != config.get("forceCleanData")) {
            cleanData = (boolean) config.get("forceCleanData");
        }
        if (null != config.get("forceReInstall")) {
            reInstall = (boolean) config.get("forceReInstall");
        }
        String appId = (String) config.get("appId");
        if (appId == null) {
            throw new ExecuteException("无匹配该平台的应用，无法初始化");
        }
        Map<String, Object> appDetail = getAppDetail(appId);
        if (null == appDetail) {
            throw new ExecuteException("app应用已被删除，无法初始化");
        }
        String appUrl = (String) appDetail.get("appUrl");
        String appPackage = (String) appDetail.get("packageName");
        String appVersion = (String) appDetail.get("buildVersion");
        String appName = appUrl.substring(appUrl.lastIndexOf("_") + 1);
        long t2 = System.currentTimeMillis();
        try {
            if (reInstall) {
                if (isAppInstalled(appPackage)) {
                    cleanData(deviceId, appPackage);
                    log.info("Begin to uninstall app[{}] from device[{}]...", appPackage, deviceId);
                    removeApp(appPackage);
                    log.info("App[{}] from device[{}] uninstalled.", appPackage, deviceId);
                } else {
                    log.info("App[{}] on device[{}] is not installed.", appPackage, deviceId);
                }
            } else {
                if (isAppInstalled(appPackage)) {
                    if (cleanData) {
                        cleanData(deviceId, appPackage);
                    }
                }
            }
            if (!isAppInstalled(appPackage)) {
                installApp(deviceId, appId, appName, appPackage);
            } else {
                log.info("device[{}] already install app[{}]...", deviceId, appPackage);
                String oldPackageCode = AndroidUtil.getOldPackageCode(deviceInfo.getConnectAddress() + ":" + (null == deviceInfo.getPort() ? defaultUpperPort : deviceInfo.getPort()), deviceId, appPackage, this.deviceInfo.getPlatform());
                if (!appVersion.equalsIgnoreCase(oldPackageCode)) {
                    log.info("install app version[{}],app in device version[{}]  ===========> reInstall", appVersion, oldPackageCode);
                    removeApp(appPackage);
                    log.info("app version[{}] remove from device[{}] successfully", appVersion, deviceId);
                    installApp(deviceId, appId, appName, appPackage);
                } else {
                    log.info("App[{}] on device[{}] already exists ===========> active", appPackage, deviceId);
                }
            }
            long t3 = System.currentTimeMillis();
            log.debug("卸载安装应用全过程耗时：{}", (t3 - t2) / 1000);
            if (!isAppInstalled(appPackage)) {
                log.error("设备【{}】安装应用【{}】失败", deviceId, appPackage);
                throw new ExecuteException("安装应用失败");
            }
            activateApp(appPackage);
        } catch (Exception e) {
            log.error("应用初始化失败", e);
            if (e instanceof ExecuteException) {
                throw e;
            }
            throw new ExecuteException("初始化应用失败");
        }
        try {
            if (!recordDevicePer) {
                Map<String, Object> params = new HashMap<>();
                params.put("tenantId", config.get("tenantId"));
                params.put("caseId", config.get("caseId"));
                params.put("appPackage", appPackage);
                //发送记录设备性能通知
                recordDevicePer(params);
            }
        } catch (Exception e) {
            log.error("记录设备性能数据失败", e);
            //性能数据报错不影响执行
//            throw new ExecuteException("记录设备性能数据失败");
        }
        log.info("自动化执行环境【app:{},device:{}】准备就绪", appPackage, deviceId);
    }

    private Map<String, Object> getAppDetail(String appId) {
        Map<String, Object> data = null;
        try {
            AtuServerConfig config = SpringUtils.getBean(AtuServerConfig.class);
            RestTemplate restTemplate = new RestTemplate();
            String url = config.getServerUrl() + config.getAppInfoUrl() + "?appId=" + appId;
            ResponseEntity<ResultWrapper> result = restTemplate.getForEntity(url, ResultWrapper.class);
            if (HttpStatus.OK.equals(result.getStatusCode())) {
                ResultWrapper wrapper = result.getBody();
                if (wrapper.isSuccess()) {
                    data = (Map<String, Object>) wrapper.getData();
                }
            }
        } catch (Exception e2) {
            log.error("app信息获取失败：", e2);
        }
        return data;
    }

    private void activateApp(String appPackage) {
        if (Thread.currentThread().isInterrupted()) {
            log.warn("设备【{}】启动app时前停止。。。。。。。。。。", deviceInfo.getDeviceId());
            throw new ExecuteException("执行停止");
        }
        StopWatch watchAppActive = null;
        if (isRecord) {
            watchAppActive = new StopWatch();
            watchAppActive.start();
        }
        if (this.deviceDriver instanceof MobileDriver) {
            log.debug("设备【{}】采取自研自动化....................",this.deviceInfo.getDeviceId());
            String uuid = this.deviceInfo.getDeviceId();
            IosUtil.activeApp(uuid, appPackage, remoteAddress);
        } else {
            log.debug("设备【{}】采取appium自动化....................",this.deviceInfo.getDeviceId());
            AppiumDriver<WebElement> driver = (AppiumDriver<WebElement>) this.deviceDriver;
            try {
                String uuid = (String) driver.getCapabilities().getCapability(UDID);
                if (driver instanceof AndroidDriver) {
                    AndroidUtil.activeApp(uuid, appPackage, remoteAddress);
                } else if (driver instanceof IOSDriver) {
                    IosUtil.activeApp(uuid, appPackage, remoteAddress);
                }
            } finally {
                if (isRecord && null != watchAppActive) {
                    watchAppActive.stop();
                    long time = watchAppActive.getTime(TimeUnit.MILLISECONDS);
                    log.debug("启动app耗时：{}ms", time);
                    devicePerInfo.getAppPerInfo().setAppActiveTime(time);
                }
            }
        }

    }

    private boolean removeApp(String appPackage) {
        if (Thread.currentThread().isInterrupted()) {
            log.warn("启动app时前停止。。。。。。。。。。");
            throw new ExecuteException("执行停止");
        }
        if ("0".equals(deviceInfo.getPlatform())) {  //android
            AppiumDriver<WebElement> driver = (AppiumDriver<WebElement>) this.deviceDriver;
            boolean removeApp = false;
            try {
                removeApp = driver.removeApp(appPackage);
            } catch (WebDriverException e) {
                log.warn("设备【{}】移除应用时driver出现问题,重新执行。。。。。", deviceInfo.getDeviceId());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException interruptedException) {
                    log.warn("设备【{}】移除应用前停止。。。。。", deviceInfo.getDeviceId());
                    throw new ExecuteException("执行取消");
                }
                removeApp = driver.removeApp(appPackage);
            }
            return removeApp;
        } else {   //ios
            return IosUtil.removeApp(this.remoteAddress, deviceInfo.getDeviceId(), appPackage);
        }
    }

    ;

    private boolean isAppInstalled(String appPackage) {
        if (Thread.currentThread().isInterrupted()) {
            log.warn("设备【{}】判断是否安装app前停止。。。。。。。。。。", deviceInfo.getDeviceId());
            throw new ExecuteException("执行停止");
        }
        if ("0".equals(deviceInfo.getPlatform())) {  //android
            AppiumDriver<WebElement> driver = (AppiumDriver<WebElement>) this.deviceDriver;
            boolean appInstalled = false;
            try {
                appInstalled = driver.isAppInstalled(appPackage);
            } catch (WebDriverException e) {
                log.warn("设备【{}】在判断包【{}】是否安装时，driver出现问题,重新执行。。。。。", deviceInfo.getDeviceId(), appPackage);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException interruptedException) {
                    log.warn("设备【{}】判断是否安装app前停止。。。。。。。。。。", deviceInfo.getDeviceId());
                    throw new ExecuteException("执行停止");
                }
                appInstalled = driver.isAppInstalled(appPackage);
            }
            return appInstalled;
        } else {   //ios
            return IosUtil.isAppInstalled(this.remoteAddress, deviceInfo.getDeviceId(), appPackage);
        }
    }

    /**
     * <p>清除数据</p>
     *
     * @param deviceId
     * @param appPackage
     * @return
     */
    private boolean cleanData(String deviceId, String appPackage) {
        if (Thread.currentThread().isInterrupted()) {
            log.warn("启动app时前停止。。。。。。。。。。");
            throw new ExecuteException("执行停止");
        }
        if ("0".equals(deviceInfo.getPlatform())) {
            return AndroidUtil.cleanAppData(deviceInfo.getConnectAddress() + ":" + (null == deviceInfo.getPort() ? defaultUpperPort : deviceInfo.getPort()), deviceId, appPackage);
        }
        return true;
    }


    /**
     * 安装app
     *
     * @param deviceId
     * @param appId
     * @param appName
     * @param packageName
     */
    private void installApp(String deviceId, String appId, String appName, String packageName) {
        StopWatch watchAppInstall = null;
        if (isRecord) {
            watchAppInstall = new StopWatch();
            watchAppInstall.start();
        }
        String appPath = "";
        log.info("Begin to download app[{}] to upperComputer...", appName);
        SimpleClientHttpRequestFactory downloadFactory = new SimpleClientHttpRequestFactory();
        downloadFactory.setConnectTimeout(15000);
        downloadFactory.setReadTimeout(600000);
        RestTemplate downLoadRestTemplate = new RestTemplate(downloadFactory);
        DebuggerDeviceInfo info = new DebuggerDeviceInfo();
        info.setAppId(appId);
//        String appType = this.deviceDriver instanceof AndroidDriver ? "apk" : "ipa";//平台
        info.setAppName(appName);
        HttpEntity<DebuggerDeviceInfo> entity = new HttpEntity<>(info);
        if (Thread.currentThread().isInterrupted()) {
            log.warn("下载app时前停止。。。。。。。。。。");
            throw new ExecuteException("执行停止");
        }
        long t1 = System.currentTimeMillis();
        try {
            ResponseEntity<String> responseEntity = downLoadRestTemplate.exchange(String.format(APP_DOWNLOAD_URL, deviceInfo.getConnectAddress() + ":" + (null == deviceInfo.getPort() ? defaultUpperPort : deviceInfo.getPort())), HttpMethod.POST, entity, String.class);
            appPath = responseEntity.getBody();
        } catch (Exception e) {
            log.error("应用包【{}】下载失败", appName, e);
            throw new ExecuteException("程序包下载失败");
        }
        long t2 = System.currentTimeMillis();
        log.debug("单独下载应用耗时：{}", (t2 - t1) / 1000);
        log.info("download app finish,start install ............");
        if (Thread.currentThread().isInterrupted()) {
            log.warn("安装app时前停止。。。。。。。。。。");
            throw new ExecuteException("执行停止");
        }
        try {
            boolean isOk = false;
            boolean isIOS = this.deviceDriver instanceof IOSDriver;
            boolean isAndroid = this.deviceDriver instanceof AndroidDriver;
            String platform = null;
            if (null != deviceInfo) {
                platform = deviceInfo.getPlatform();
            }
            if (null == platform) {
                if (isAndroid) {
                    platform = "0";
                } else if (isIOS) {
                    platform = "1";
                }
                log.debug("设备信息未获取到平台，直接根据驱动判断平台(1-IOS 0-安卓):{}", platform);
            }
            try {
                if (isAndroid) {
                    isOk = AndroidUtil.installApp(this.remoteAddress, deviceId, appPath, platform);
                } else if (isIOS) {
                    isOk = IosUtil.installApp(this.remoteAddress, deviceId, appPath, platform);
                } else {
                    log.warn("driver类型未知，安装应用失败！");
                }
                long t3 = System.currentTimeMillis();
                log.debug("单独安装应用耗时：{}", (t3 - t2) / 1000);
            } catch (Exception e) {
                log.warn("设备【{}】安装应用【{}】出现异常,重试一次。。。", deviceId, packageName, e);
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException interruptedException) {
                    log.warn("设备【{}】安装app【{}】时用户取消操作。。。。。", deviceId, packageName);
                    return;
                }
                if (isAndroid) {
                    isOk = AndroidUtil.installApp(this.remoteAddress, deviceId, appPath, platform);
                } else if (isIOS) {
                    isOk = IosUtil.installApp(this.remoteAddress, deviceId, appPath, platform);
                } else {
                    log.warn("driver类型未知，安装应用失败！");
                }
            }
//            if (this.deviceDriver instanceof AndroidDriver) {
//                AppiumDriver<WebElement> driver = (AppiumDriver<WebElement>) this.deviceDriver;
//                try {
//                    driver.installApp(appPath);
//                } catch (WebDriverException e) {
//                    log.warn("设备【{}】安装应用【{}】出现异常,重试一次。。。", deviceId, packageName, e);
//                    try {
//                        Thread.sleep(1500);
//                    } catch (InterruptedException interruptedException) {
//                        log.warn("设备【{}】安装app【{}】时用户取消操作。。。。。", deviceId, packageName);
//                        return;
//                    }
//                    driver.installApp(appPath);
//                }
//            } else if (this.deviceDriver instanceof IOSDriver) {
//                boolean isOk = false;
//                try {
//                    isOk = IosUtil.installApp(this.remoteAddress, deviceId, appPath);
//                } catch (Exception e) {
//                    log.warn("设备【{}】安装应用【{}】出现异常,重试一次。。。", deviceId, packageName, e);
//                    try {
//                        Thread.sleep(1500);
//                    } catch (InterruptedException interruptedException) {
//                        log.warn("设备【{}】安装app【{}】时用户取消操作。。。。。", deviceId, packageName);
//                        return;
//                    }
//                    isOk = IosUtil.installApp(this.remoteAddress, deviceId, appPath);
//                }
//            }
        } finally {
            if (isRecord && null != watchAppInstall) {
                watchAppInstall.stop();
                long time = watchAppInstall.getTime(TimeUnit.MILLISECONDS);
                log.debug("下载安装app总耗时：{}ms", time);
                devicePerInfo.getAppPerInfo().setAppInstallTime(time);
            }
        }
        log.info("App[{}] on device[{}] installed successfully.", packageName, deviceId);
    }


    @Override
    public void startRecord() {
        if ("0".equals(deviceInfo.getPlatform())) {
            AndroidUtil.startRecord(this.remoteAddress, deviceInfo.getDeviceId(), deviceInfo.getOtherInfo().get("taskId"),deviceInfo.getOtherInfo().get("tenantId"));
        } else {
            IosUtil.startRecord(this.remoteAddress, deviceInfo.getDeviceId(), deviceInfo.getOtherInfo().get("taskId"), deviceInfo.getOtherInfo().get("tenantId"),deviceInfo.getResolution());
        }
    }

    @Override
    public String stopRecord(Map<String, Object> initData) {
        String result = null;
        if (ObjectUtil.isNull(initData.get("tenantId"))) {
            throw new RuntimeException("租户编号不能为空");
        }
        if (ObjectUtil.isNull(initData.get("save"))) {
            throw new RuntimeException("视频保存标识不能为空");
        }
        if ("0".equals(deviceInfo.getPlatform())) {
            result = AndroidUtil.stopRecord(this.remoteAddress, deviceInfo.getDeviceId(), initData);
        } else {
            result = IosUtil.stopRecord(this.remoteAddress, deviceInfo.getDeviceId(), initData);
        }
        return result;
    }

    @Override
    public void startLog() {

    }

    @Override
    public String stopLog() {
        return null;
    }

    @Override
    public String snapshot(Map<String, Object> initData) {
        return MobileUtil.snapshot(this.remoteAddress, deviceInfo.getPlatform(), initData);
    }

//    @Override
//    public String snapshotAllScreen(String tenantId) {
//        return AndroidUtil.snapshotAllScreen(this.remoteAddress, deviceInfo, tenantId);
//    }

    /**
     * 回放准备手机环境
     *
     * @param config 参数
     */
    @Override
    public void restart(Map<String, Object> config) {
        String type = (String) config.get("type");
        switch (type) {
            case "0":   //重启应用
                appRestart((String) config.get(PACKAGE_NAME), (String) config.get(APP_NAME));
                break;
            case "1":   //清除数据
                cleanData(this.deviceInfo.getDeviceId(), (String) config.get(PACKAGE_NAME));
                activateApp((String) config.get(PACKAGE_NAME));
                break;
            case "2":   //卸载重装
                initConfig(config);
                break;
            case "3":  //清楚数据，重启应用
                cleanData(this.deviceInfo.getDeviceId(), (String) config.get(PACKAGE_NAME));
                appRestart((String) config.get(PACKAGE_NAME), (String) config.get(APP_NAME));
                break;
            default:
                log.warn("未知回放参数：{}", type);
                break;
        }
    }


    /**
     * 重新启动App
     *
     * @param appPackage
     */
    private void appRestart(String appPackage, String appName) {
        if (Thread.currentThread().isInterrupted()) {
            log.warn("设备【{}】重启app时前停止。。。。。。。。。。", deviceInfo.getDeviceId());
            throw new ExecuteException("执行停止");
        }
        boolean appInstalled = isAppInstalled(appPackage);
        if (!appInstalled) {
            throw new ExecuteException(String.format("应用【%s】未安装", appName));
        }

        if (this.deviceDriver instanceof MobileDriver) {
            log.debug("设备【{}】采取自研自动化重新启动App....................",this.deviceInfo.getDeviceId());
            String uuid = this.deviceInfo.getDeviceId();
            IosUtil.terminateApp(uuid, appPackage, remoteAddress);
        } else {
            log.debug("设备【{}】采取appium自动化....................",this.deviceInfo.getDeviceId());
            AppiumDriver deviceDriver = (AppiumDriver) this.deviceDriver;
            if (deviceDriver instanceof AndroidDriver) {
                try {
                    deviceDriver.terminateApp(appPackage);
                } catch (WebDriverException e) {
                    log.warn("设备【{}】在关闭应用【{}】时，driver出现问题,重新执行。。。。。", deviceInfo.getDeviceId(), appPackage);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException interruptedException) {
                        log.warn("设备【{}】关闭app前停止。。。。。。。。。。", deviceInfo.getDeviceId());
                        throw new ExecuteException("执行停止");
                    }
                    deviceDriver.terminateApp(appPackage);
                }
            } else if (deviceDriver instanceof IOSDriver) {
                String uuid = (String) deviceDriver.getCapabilities().getCapability(UDID);
                IosUtil.terminateApp(uuid, appPackage, remoteAddress);
            }
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            log.warn("设备【{}】启动app前停止。。。。。。。。。。", deviceInfo.getDeviceId());
            throw new ExecuteException("执行停止");
        }
        activateApp(appPackage);
    }

    /**
     * 停止回放
     *
     * @param config 参数
     */
    @Override
    public void stopPlay(Map<String, Object> config) {
        log.info("正在停止回放。。。");
        Future future = this.deviceDriver.getFuture();
        if (null != future) {
            future.cancel(true);
        }
    }

    @Override
    public void close() {
        super.close();
        this.keepThread.interrupt();
    }

    @Override
    public boolean reconnect() {
        this.keepThread.interrupt();
        if (this.deviceDriver != null) {
            this.deviceDriver.destroy();
            log.debug("MobileDeviceConnection.reconnect:设备【{}】的driver释放",deviceInfo.getDeviceId());
            this.deviceDriver = null;
        }
        initDriver();
        if (null != this.deviceDriver) {
            return true;
        }
        return false;
    }

    @Override
    public void recordDevicePer(Map<String, Object> params) {
        //通知上位机开始获取设备和应用的性能数据
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(PER_CONNECT_TIMEOUT);
        factory.setReadTimeout(PER_READ_TIMEOUT);
        RestTemplate restTemplate = new RestTemplate(factory);
        params.put("platform", deviceInfo.getPlatform());
        params.put("deviceId", deviceInfo.getDeviceId());
        HttpEntity<Map<String, Object>> entity = new HttpEntity<>(params);
        try {
            ResponseEntity<Boolean> responseEntity = restTemplate.exchange(String.format(RECORD_DEVICE_PER, deviceInfo.getConnectAddress() + ":" + (null == deviceInfo.getPort() ? defaultUpperPort : deviceInfo.getPort())), HttpMethod.POST, entity, Boolean.class);
            Boolean success = responseEntity.getBody();
            if (success) {
                log.debug("上位机开启记录设备【{}】性能数据", deviceInfo.getDeviceId());
            } else {
                throw new ExecuteException("开启监控设备性能失败");
            }
        } catch (ExecuteException e) {
            throw e;
        } catch (Exception e) {
            log.error("开启监控设备性能失败", e);
            throw new ExecuteException("开启监控设备性能失败");
        }

    }

    @Override
    public DevicePerInfo stopRecordDevicePer() {
        log.debug("是否开启录屏：{}", isRecord);
        if (isRecord) {  //开启录屏
            //通知上位机开始获取设备和应用的性能数据
            SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
            factory.setConnectTimeout(PER_CONNECT_TIMEOUT);
            factory.setReadTimeout(PER_READ_TIMEOUT);
            RestTemplate restTemplate = new RestTemplate(factory);
            HashMap<String, Object> params = new HashMap<String, Object>();
            params.put("caseId", devicePerInfo.getCaseId());
            params.put("platform", deviceInfo.getPlatform());
            params.put("deviceId", deviceInfo.getDeviceId());
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(params);
            try {
                ResponseEntity<String[]> responseEntity = restTemplate.exchange(String.format(STOP_RECORD_DEVICE_PER, deviceInfo.getConnectAddress() + ":" + (null == deviceInfo.getPort() ? defaultUpperPort : deviceInfo.getPort())), HttpMethod.POST, entity, String[].class);
                String[] result = responseEntity.getBody();
                if (result != null && result.length == 2) {
                    log.debug("上位机停止记录设备【{}】性能数据成功，app数据地址：{}，设备数据地址：{}", deviceInfo.getDeviceId(), result[1], result[0]);
                    devicePerInfo.setDevicePerInfoAddress(result[0]);
                    devicePerInfo.getAppPerInfo().setAppPerInfoAddress(result[1]);
                } else {
                    log.debug("上位机停止记录设备【{}】性能数据失败", deviceInfo.getDeviceId());
                    devicePerInfo.setDevicePerInfoAddress("");
                    devicePerInfo.getAppPerInfo().setAppPerInfoAddress("");
                }
            } catch (Exception e) {
                log.debug("上位机停止记录设备【{}】性能数据失败", deviceInfo.getDeviceId());
                devicePerInfo.setDevicePerInfoAddress("");
                devicePerInfo.getAppPerInfo().setAppPerInfoAddress("");
            }
        }
        return devicePerInfo;
    }
    @Override
    public String snapshotAllScreen(String tenantId, String taskId) {
        return AndroidUtil.snapshotAllScreen(this.remoteAddress, deviceInfo, tenantId,taskId);

    }
}