package net.northking.cctp.upperComputer.service;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.img.ImgUtil;
import com.alibaba.fastjson.JSON;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import net.northking.cctp.upperComputer.config.MobileProperty;
import net.northking.cctp.upperComputer.driver.adb.Adb;
import net.northking.cctp.upperComputer.driver.adb.AdbDevice;
import net.northking.cctp.upperComputer.driver.adb.AdbTransport;
import net.northking.cctp.upperComputer.driver.agent.AndroidAgentSession;
import net.northking.cctp.upperComputer.driver.agent.command.AppiumSourceXmlCommand;
import net.northking.cctp.upperComputer.driver.agent.command.CloseSessionCommand;
import net.northking.cctp.upperComputer.driver.agent.command.GetSmsListCommand;
import net.northking.cctp.upperComputer.driver.agent.command.TakeScreenshotCommand;
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.entity.DebuggerDeviceInfo;
import net.northking.cctp.upperComputer.exception.AppiumException;
import net.northking.cctp.upperComputer.exception.ExecuteException;
import net.northking.cctp.upperComputer.deviceManager.AndroidDeviceManager;
import net.northking.cctp.upperComputer.service.thread.AndroidDeviceAllInfoThread;
import net.northking.cctp.upperComputer.service.thread.AndroidDeviceInfoByPackageThread;
import net.northking.cctp.upperComputer.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import static io.appium.java_client.remote.AndroidMobileCapabilityType.RESET_KEYBOARD;
import static io.appium.java_client.remote.AndroidMobileCapabilityType.UNICODE_KEYBOARD;
import static io.appium.java_client.remote.MobileCapabilityType.*;

@Service("androidService")
@RefreshScope
public class AndroidDebuggerServiceImpl extends AbstractDebuggerService {

    private final ConcurrentHashMap<String, AppiumDriver<WebElement>> driverMap = new ConcurrentHashMap<>();

    private ConcurrentHashMap<String, AndroidAgentSession> xmlAgentSessionMap = new ConcurrentHashMap<>();

    private final Logger logger = LoggerFactory.getLogger(AndroidDebuggerServiceImpl.class);

    private final String VALUE_NEW_COMMAND_TIMEOUT = "3600";

    private ConcurrentHashMap<String, AndroidDeviceInfoByPackageThread> queryDeviceInfoByPackageMap = new ConcurrentHashMap<>();

    private ConcurrentHashMap<String, AndroidDeviceAllInfoThread> queryDeviceInfoMap = new ConcurrentHashMap<>();

    private Adb adb;

    @Value("${appium.server.host:127.0.0.1}")
    private String appiumHost;

    @Value("${appium.server.port:4723}")
    private Integer appiumPort;

    @Value("${appium.server.contextPath:/wd/hub}")
    private String appiumContextPath;

    @Autowired
    @Override
    public void setMobileProperty(MobileProperty mobileProperty) {
        super.setMobileProperty(mobileProperty);
    }

    @PostConstruct
    public void createAdb(){
        try {
            adb = new Adb();
        } catch (IOException e) {
            logger.warn("adb创建失败",e);
        }
        logger.info("调试服务的adb创建成功........................");
    }

    @Override
    public boolean activeApp(String deviceId, String appPackage) {
        AdbDevice currentDevice = AndroidDeviceManager.getInstance().getCurrentDevice(deviceId);
        if (null == currentDevice) {
            throw new ExecuteException("设备不在线，无法启动app");
        }
        if (adb == null) {
            try {
                adb = new Adb();
            } catch (IOException e) {
                logger.warn("adb通信出现错误",e);
                return false;
            }
        }
        AdbTransport shell = adb.shell(currentDevice, "monkey -p " + appPackage + " -v 1");
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(shell.getInputStream()));
            String line = null;
            while (null != (line = reader.readLine())) {
                logger.debug("设备【{}】启动应用打印：{}",deviceId,line);
            }
            return true;
        } catch (IOException e) {
            logger.warn("启动app出现错误",e);
        }
        return false;
    }

    @Override
    public boolean recordDevicePer(DebuggerDeviceInfo info) {
        AndroidDeviceInfoByPackageThread androidDeviceInfoByPackageThread = queryDeviceInfoByPackageMap.get(info.getCaseId());
        if (androidDeviceInfoByPackageThread == null || !androidDeviceInfoByPackageThread.isAlive() || androidDeviceInfoByPackageThread.isInterrupted()) {
            logger.debug("开始记录任务【{}】的应用的参数。。。。。。。。。。", info.getCaseId());
            androidDeviceInfoByPackageThread = new AndroidDeviceInfoByPackageThread(info);
            androidDeviceInfoByPackageThread.start();
            queryDeviceInfoByPackageMap.put(info.getCaseId(), androidDeviceInfoByPackageThread);
        } else {
            logger.debug("记录任务【{}】的应用的参数线程已经存在，无需创建新的。。。。。。。。。。", info.getCaseId());
        }
        AndroidDeviceAllInfoThread androidDeviceAllInfoThread = queryDeviceInfoMap.get(info.getCaseId());
        if (androidDeviceAllInfoThread == null || !androidDeviceAllInfoThread.isAlive() || androidDeviceAllInfoThread.isInterrupted()) {
            logger.debug("开始记录任务【{}】的设备的参数。。。。。。。。。。", info.getCaseId());
            androidDeviceAllInfoThread = new AndroidDeviceAllInfoThread(info);
            androidDeviceAllInfoThread.start();
            queryDeviceInfoMap.put(info.getCaseId(), androidDeviceAllInfoThread);
        } else {
            logger.debug("记录任务【{}】的设备的参数线程已经存在，无需创建新的。。。。。。。。。。", info.getCaseId());
        }

        return true;
    }

    @Override
    public String[] stopRecordDevicePer(DebuggerDeviceInfo info) {
        String[] result = new String[2];
        logger.debug("结束任务：{}的设备和应用的参数。。。。。。。。。。",info.getCaseId());
        AndroidDeviceInfoByPackageThread thread = queryDeviceInfoByPackageMap.remove(info.getCaseId());
        if (null != thread && !thread.isInterrupted() && thread.isAlive()) {
            String appInfoAddress = thread.stopThread();
            result[1] = appInfoAddress;
        }
        AndroidDeviceAllInfoThread allInfoThread = queryDeviceInfoMap.remove(info.getCaseId());
        if (null != allInfoThread && !allInfoThread.isInterrupted() && allInfoThread.isAlive()) {
            String deviceInfoAddress = allInfoThread.stopThread();
            result[0] = deviceInfoAddress;
        }
        return result;
    }

    @Override
    public boolean terminateApp(String deviceId, String appPackage) {
        return false;
    }

    @Override
    public Object getUiTree(String deviceId) {
        String xmlResponse = null;
        try {
            AndroidAgentSession agentSession = xmlAgentSessionMap.get(deviceId);
            if (null == agentSession) {
                AdbDevice currentDevice = AndroidDeviceManager.getInstance().getCurrentDevice(deviceId);
                if (null == currentDevice) {
                    logger.warn("设备【{}】还未准备好，等会再尝试..............",deviceId);
                    return null;
                }
                agentSession = new AndroidAgentSession(currentDevice,false);
                agentSession.start();
                xmlAgentSessionMap.put(deviceId,agentSession);
            }
            xmlResponse = agentSession.send(AppiumSourceXmlCommand.getInstance());
            if (StringUtils.isBlank(xmlResponse)) {
                logger.warn("设备【{}】的agentSession失效，重新创建一个",deviceId);
                agentSession.closeSilence();
                AdbDevice currentDevice = AndroidDeviceManager.getInstance().getCurrentDevice(deviceId);
                if (null == currentDevice) {
                    logger.warn("设备【{}】还未准备好，等会再尝试..............",deviceId);
                    xmlAgentSessionMap.remove(deviceId);
                    return null;
                }
                agentSession = new AndroidAgentSession(currentDevice,false);
                agentSession.start();
                xmlAgentSessionMap.put(deviceId,agentSession);
                xmlResponse = agentSession.send(AppiumSourceXmlCommand.getInstance());
            }
        } catch (Exception e) {
            logger.warn("获取设备【{}】xml失败", deviceId);
        }
        return xmlResponse;
    }

    @Override
    public AppiumDriver<WebElement> getDriver(DebuggerDeviceInfo info) {
        return getDriver(info.getDeviceId());
    }

    private DesiredCapabilities initAndroidCapabilities(String deviceId, Map<String, String> others) throws MalformedURLException {
        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);
        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);
        logger.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;
    }

    @Override
    public boolean installApp(String deviceId, String appPath) {
        boolean success = false;
        long startTime = System.currentTimeMillis();
        logger.info("开始安装应用，路径是：[{}] 设备是：[{}]", appPath, deviceId);
        List<String> cmds = new ArrayList<>();
        cmds.add("adb");
        cmds.add("-s");
        cmds.add(deviceId);
        cmds.add("install");
        cmds.add(appPath);
        String line = null;
        BufferedReader br = null;
        try {
            Process process = new ProcessBuilder().command(cmds).redirectErrorStream(true).start();
            process.waitFor();
            br = new BufferedReader(new InputStreamReader(process.getInputStream()));
            while ((line = br.readLine()) != null) {
                logger.debug(line);
                if (line.contains("Success")) {
                    success = true;
                    logger.debug("设备【{}】安装应用成功！",deviceId);
                }
            }
        } catch (Exception e) {
            logger.error("安装应用失败", e);
            return false;
        } finally {
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    logger.error("reader关闭失败",e);
                }
            }
            long endTime = System.currentTimeMillis();
            logger.debug("android install app time: {}s",(endTime-startTime)/1000);
        }
        return success;
    }

    @Override
    public boolean initApp(DebuggerDeviceInfo info) {
        String appPackage = info.getAppPackage();
        String deviceId = info.getDeviceId();
        String appId = info.getAppId();
        String appDownloadUrl = mobileProperty.getServerAddr() + mobileProperty.getAppDownloadAddr().replace("{appId}", appId);
        String appVersion = info.getAppVersion();
        boolean reInstall = info.isReInstall();
        //初始化应用
        AppiumDriver<WebElement> driver = getDriver(deviceId);
        try {
            if (reInstall) {
                if (driver.isAppInstalled(appPackage)) {
                    cleanData(deviceId, appPackage);
                    logger.info("Begin to uninstall app[{}] from device[{}]...", appPackage, deviceId);
                    driver.removeApp(appPackage);
                    logger.info("App[{}] from device[{}] uninstalled.", appPackage, deviceId);
                } else {
                    logger.info("App[{}] on device[{}] is not installed.", appPackage, deviceId);
                }
            } else {
                if (driver.isAppInstalled(appPackage)) {
                    if (info.isCleanData()) {
                        cleanData(deviceId, appPackage);
                    }
                }
            }
            if (!driver.isAppInstalled(appPackage)) {
                installApp(driver, deviceId, appDownloadUrl, appPackage, appVersion, "apk");
            } else {
                logger.info("device[{}] already install app[{}]...", deviceId, appPackage);
                String oldPackageCode = getOldPackageCode(driver, deviceId, appPackage);
                if (!appVersion.equalsIgnoreCase(oldPackageCode)) {
                    logger.info("install app version[{}],app in device version[{}]  ===========> reInstall", appVersion, oldPackageCode);
                    driver.removeApp(appPackage);
                    logger.info("app version[{}] remove from device[{}] successfully", appVersion, deviceId);
                    installApp(driver, deviceId, appDownloadUrl, appPackage, appVersion, "apk");
                } else {
                    logger.info("App[{}] on device[{}] already exists ===========> terminate", appPackage, deviceId);
                }
            }
            if (!driver.isAppInstalled(appPackage)) {
                logger.error("安装应用失败");
                throw new ExecuteException("安装应用失败");
            }
            logger.info("terminate app[{}]", appPackage);
            driver.activateApp(appPackage);
		/*if (cleanData || reInstall) {
			driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
			log.info("正向点击处理 ...");
			positiveClick(driver);
			driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
		}*/
        } catch (Exception e) {
            logger.error("应用初始化失败", e);
            throw new ExecuteException("初始化应用失败");
        }
        logger.info("自动化执行环境【app:{},device:{}】准备就绪", appPackage, deviceId);
        return false;
    }

    /**
     * 获取手机应用版本号
     *
     * @param driver
     * @param deviceId
     * @param appPackage
     * @return
     */
    private String getOldPackageCode(AppiumDriver<WebElement> driver, String deviceId, String appPackage) {
        String oldPackageCode = "";
        if (driver.isAppInstalled(appPackage)) {
            String shell = "";
            try {
                if (driver instanceof AndroidDriver) {
                    shell = "adb -s " + deviceId + " shell dumpsys package " + appPackage + " |grep versionName";
                    Process process = Runtime.getRuntime().exec(shell);
                    process.waitFor();
                    BufferedReader in = new BufferedReader(new InputStreamReader(
                            process.getInputStream()));
                    String line = in.readLine();
                    if (StringUtils.isNotBlank(line)) {
                        oldPackageCode = line.split("=")[1];
                    }
                } else if (driver instanceof IOSDriver) {
                    shell = "tidevice -u " + deviceId + " applist | grep " + appPackage + "  | awk '{print $3}'";
                    String[] cmds = {"/bin/sh", "-c", shell};
                    Process process = Runtime.getRuntime().exec(cmds, null, null);
                    process.waitFor();
                    BufferedReader in = new BufferedReader(new InputStreamReader(
                            process.getInputStream()));
                    oldPackageCode = in.readLine();
                }
            } catch (Exception e) {
                logger.error("获取应用版本失败", e);
            }
        }
        logger.debug("手机应用版本为:" + oldPackageCode);
        return oldPackageCode;
    }

    /**
     * 安装app
     *
     * @param driver
     * @param deviceId
     * @param appDownloadUrl
     * @param appPackage
     * @param appVersion
     */
    private void installApp(AppiumDriver<WebElement> driver, String deviceId, String appDownloadUrl, String appPackage, String appVersion, String appType) {
        logger.info("Begin to install app[{}] on device[{}]...", appPackage, deviceId);
        logger.info("download app to local starting ............");
        String appPath = downloadAppToLocal(appDownloadUrl, appPackage, appVersion, appType);
        logger.info("install app starting ......");
        try {
            if (driver instanceof AndroidDriver) {
                installApp(deviceId, appPath);
            } else if (driver instanceof IOSDriver) {
                String shell = "tidevice -u " + deviceId + " install " + appPath;
                logger.info("安装ios应用命令:{}", shell);
                Process process = Runtime.getRuntime().exec(shell);
                process.waitFor();
            }
        } catch (Exception e) {
            logger.error("安装应用失败", e);
            throw new ExecuteException("安装应用失败");
        }
        logger.info("App[{}] on device[{}] installed successfully.", appPackage, deviceId);
    }

    /**
     * @param downloadAppUrl
     * @param appName
     * @return
     */
    private String downloadAppToLocal(String downloadAppUrl, String appName, String appVersion, String type) {
        File fileDir = new File(System.getProperty("user.dir") + "/tempApp/");
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }
        String tmpFileName = String.format("%s_%s.%s",appVersion,System.currentTimeMillis(),type);
        File app = new File(String.format("%s/%s",fileDir.getAbsolutePath(),tmpFileName));
        HttpUtils.downloadFileToLocal(downloadAppUrl, fileDir.getAbsolutePath(), tmpFileName);
        /*if (app.exists()) {
            logger.info("app[{}] already exist.......", app.getName());
        } else {
            logger.info("download app url ========>{}", downloadAppUrl);
        }*/
        logger.info("应用本地地址：{}", app.getAbsolutePath());
        return app.getAbsolutePath();
    }


    @Override
    public String getScreenShot(DebuggerDeviceInfo info) {
        File shotFile = getScreenShotFile(info);
        String path = null;
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.set("Authorization", "Bearer " + info.getUserToken());
            String taskId = info.getTaskId();
            logger.debug("开始上传截图，任务信息：{}",JSON.toJSONString(info));
            String businessCode = null;
            if (StringUtils.isBlank(taskId)) {
                businessCode = FileBusinessTypeEnum.MOBILE_RECORD_SCREENSHOT.getCode();
            } else {
                businessCode = FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode();
            }
            path = HttpUtils.doUpload(mobileProperty.getServerAddr() + mobileProperty.getFileUploadAddr(), shotFile, headers, taskId, businessCode);
            logger.info("上传截图完成:{}", path);
            Map map = JSON.parseObject(path, Map.class);
            if ((boolean) map.get("success")) {
                path = (String) map.get("data");
            }
        } catch (ExecuteException e) {
            logger.error("截图失败", e);
            throw e;
        } catch (Exception e) {
            logger.error("截图失败", e);
            throw new ExecuteException("截取图片失败");
        } finally {
            if (shotFile.exists()) {
                boolean delete = shotFile.delete();
                if (!delete) {
                    logger.warn("临时文件【{}】删除失败", shotFile.getAbsolutePath());
                }
            }
        }
        return path;
    }

    @Override
    public boolean destroyDriver(String deviceId) {
        AppiumDriver<WebElement> driver = driverMap.remove(deviceId);
        if (null != driver) {
            try {
                driver.quit();
            } catch (Exception e) {
                logger.error("driver退出异常");
            }
        }
        return true;
    }

    private AppiumDriver<WebElement> reConnectDevice(String deviceId) {
        AppiumDriver<WebElement> driver = driverMap.remove(deviceId);
//        driver.close();
        try {
            driver.quit();
        } catch (Exception e) {
            logger.error("driver退出异常");
        }
        driver = connectDevice(deviceId);
        driverMap.put(deviceId, driver);
        return driver;
    }

    public AppiumDriver<WebElement> getDriver(String deviceId) {
        AppiumDriver<WebElement> driver = driverMap.get(deviceId);
        if (null == driver) {
            driver = connectDevice(deviceId);
            driverMap.put(deviceId, driver);
        }
        return driver;
    }

    private AppiumDriver<WebElement> connectDevice(String deviceId) {
       /* //请求设备信息
        CdMobileDevice deviceInfo = null;
        try {
            URI uri = new URI(mobileProperty.getServerAddr() + mobileProperty.getMobileInfoAddr().replace("{uuid}", deviceId));
            ResultWrapper resultWrapper = HttpUtils.doGet(uri, ResultWrapper.class);
            if (resultWrapper.isSuccess()) {
                String s = JSON.toJSONString(resultWrapper.getData());
                DeviceInfoDto dto = JSON.parseObject(s, DeviceInfoDto.class);
                deviceInfo = dto.getCdMobileDevice();
            }
        } catch (Exception e) {
            logger.error("请求数据失败：{}",e);
            throw new RequestException("设备服务请求失败，请稍后再试");
        }
        if (null == deviceInfo) {
            throw new ParamMistakeException("无法获取到设备信息，请确保设备是否正常");
        }
        String platform = deviceInfo.getPlatform();
        logger.debug("platformName:{},platformVersion:{},device:{}",platform,deviceInfo.getPlatformVersion(),deviceId);*/
        URL appiumServerUrl = null;
        try {
            appiumServerUrl = new URL(String.format("http://%s:%d%s", appiumHost, appiumPort, appiumContextPath));
        } catch (MalformedURLException e) {
            logger.error("appium-server地址有误：{}", String.format("http://%s:%d%s", appiumHost, appiumPort, appiumContextPath));
        }
        AppiumDriver<WebElement> driver = createAndroidDriver(appiumServerUrl, deviceId, null);
        return driver;
    }

    private AppiumDriver<WebElement> createAndroidDriver(URL appiumServerUrl, String deviceId, Map<String, String> others) {
        try {
            DesiredCapabilities capabilities = initAndroidCapabilities(deviceId, others);
            AppiumDriver<WebElement> driver = new AndroidDriver<WebElement>(appiumServerUrl, capabilities);
            logger.info("Driver for device[{}] created successfully.", deviceId);
            return driver;
        } catch (Exception e) {
            logger.error("Driver获取失败，设备连接失败", e);
            throw new AppiumException("设备驱动创建失败，请稍后再试");
        }
    }


    /**
     * <p>清除数据</p>
     *
     * @param deviceId
     * @param appPackage
     * @return
     */
    @Override
    public boolean cleanData(String deviceId, String appPackage) {
        boolean success = false;
        logger.info("Clean data for app[{}] on device[{}]", appPackage, deviceId);
        Adb adb = AndroidDeviceManager.getInstance().getAdb();
        AdbDevice adbDevice = AndroidDeviceManager.getInstance().getCurrentDevice(deviceId);
        if (null == adbDevice) {
            logger.error("设备【{}】不在线",deviceId);
            return false;
        }
        AdbTransport adbTransport = adb.shell(adbDevice, String.format("pm clear %s ", appPackage));
        String line = null;
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(adbTransport.getInputStream()));
            while ((line = reader.readLine()) != null) {
                logger.debug(line);
                if (line.contains("Success")) {
                    success = true;
                }
            }
        } catch (Exception e) {
            logger.error("清除数据失败", e);
            return false;
        } finally {
            if (null != reader) {
                try {
                    reader.close();
                } catch (IOException e) {
                    logger.error("reader关闭失败",e);
                }
            }
            if (null != adbTransport) {
                try {
                    adbTransport.close();
                } catch (IOException e) {
                    logger.error("adbTransport关闭失败",e);
                }
            }
        }
        return success;
    }

    @Override
    public String getPackageVersion(String deviceId, String packageName) {
        String oldPackageCode = "";
        String shell = "adb -s " + deviceId + " shell dumpsys package " + packageName + " |grep versionName";
        try {
            Process process = Runtime.getRuntime().exec(shell);
            process.waitFor();
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line = in.readLine();
            if (StringUtils.isNotBlank(line)) {
                oldPackageCode = line.split("=")[1];
            }
        } catch (Exception e) {
            logger.error("获取应用版本失败", e);
        }
        logger.debug("手机应用版本为:" + oldPackageCode);
        return oldPackageCode;
    }

    @Override
    public String getScreenShotToBase64(DebuggerDeviceInfo info) {
        File screenShotFile = getScreenShotFile(info);
        String imgBase64 = null;
        try {
            imgBase64 = Base64.encode(screenShotFile);
        } catch (Exception e) {
            logger.error("截图失败", e);
            throw new ExecuteException("截取图片失败");
        } finally {
            if (screenShotFile.exists()) {
                boolean delete = screenShotFile.delete();
                if (!delete) {
                    logger.warn("临时文件【{}】删除失败", screenShotFile.getAbsolutePath());
                }
            }
        }
        return imgBase64;
    }

    //todo:传横竖屏
    @Override
    public String shotAllScreen(DebuggerDeviceInfo info) {
        File file = getScreenShotInCommand(info.getDeviceId());
//        File file = ScreenShotUtils.getMobileScreenShot(info.getDeviceId(), info.getResolution(), false);
        try {
            return Base64.encode(file);
        } catch (Exception e) {
            logger.error("截图失败", e);
            throw new ExecuteException("截取屏幕失败");
        } finally {
            if (file.exists()) {
                boolean delete = file.delete();
                if (!delete) {
                    logger.warn("临时文件【{}】删除失败", file.getAbsolutePath());
                }
            }
        }
    }

    //todo:传横竖屏
    @Override
    public String uploadShotAllScreen(DebuggerDeviceInfo info) {
        File file = getScreenShotInCommand(info.getDeviceId());
        String path = null;
        try {
            //租户id
            String tenantId = info.getTenantId();
            logger.debug("开始上传截图，任务信息：{}",JSON.toJSONString(info));
            String taskId = info.getTaskId();
            Attachment upload = HttpUtils.upload(mobileProperty.getServerAddr() + mobileProperty.getPublicUploadAddr(), file.getAbsolutePath(), tenantId, taskId, FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode());
            if (null != upload && StringUtils.isNotBlank(upload.getId())) {
                logger.debug("文件上传成功，返回id：{}", upload.getId());

                path = upload.getUrlPath();
            }
        } catch (ExecuteException e) {
            logger.error("截图失败", e);
            throw e;
        } catch (Exception e) {
            logger.error("截图失败", e);
            throw new ExecuteException("截取图片失败");
        } finally {
            if (file.exists()) {
                boolean delete = file.delete();
                if (!delete) {
                    logger.warn("临时文件【{}】删除失败", file.getAbsolutePath());
                }
            }
        }
        return path;
    }

    private Map<String, Process> processMap = new HashMap<>();

    @Override
    public boolean startRecord(DebuggerDeviceInfo info) {
        logger.info("开启录屏");
        String taskId = info.getTaskId();
        if (StringUtils.isBlank(taskId)) {
            logger.warn("任务id必填");
            return false;
        }
        String videoPath = mobileProperty.getStfPath() + "/temp";
        Thread thread = new Thread(() -> {
            File file = new File(videoPath);
            if (!file.isDirectory()) {
                file.mkdirs();
            }
            logger.info("录屏前先kill掉所有scrcpy进程");
            String pullCmd = String.format("ps -ef|grep 'scrcpy -s %s -b 2M -Nr %s' |grep -v grep |awk '{print $2}' |xargs --no-run-if-empty kill -9", info.getDeviceId(), videoPath + "/" + taskId + ".mp4");
            String[] delCmd = {"/bin/sh", "-c", pullCmd};
            try {
                Process delProcess = Runtime.getRuntime().exec(delCmd, null, null);
                delProcess.waitFor();
                logger.info("录屏开启命令：" + pullCmd);
                delProcess.destroy();
            } catch (Exception e) {
                logger.error("删除scrcpy进程失败:", e);
            }
            String cmd = String.format("scrcpy -s %s -b 2M -Nr %s ", info.getDeviceId(), videoPath + "/" + taskId + ".mp4");
            logger.info(cmd);

            try {
                Process process = Runtime.getRuntime().exec(cmd);
                processMap.put(taskId, process);
                logger.info("开启scrcpy进程");
            } catch (IOException e) {
                logger.error("开启录屏异常：", e);
            }
        });
        thread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            logger.warn("线程终止。。。。。。", e);
            Thread.currentThread().interrupt();
        }
        return false;
    }

    @Override
    public String endRecord(DebuggerDeviceInfo info) {
        String taskId = info.getTaskId();
        if (StringUtils.isBlank(taskId)) {
            logger.warn("任务id必填");
            return null;
        }
        String videoPath = null;
        String filePath = mobileProperty.getStfPath() + "/temp";
        logger.info("关闭scrcpy进程");
        try {
            Process process = processMap.remove(taskId);
            if (process != null) {
                process.destroy();
                process = null;
            }
            Thread.sleep(2000);
        } catch (Exception e) {
            logger.error("关闭录屏异常", e);
            return videoPath;
        }
        Boolean save = info.getSave();
        logger.info("上传视频文件");
        String fileName = filePath + "/" + taskId + ".mp4";
        File temp = new File(fileName);
        if (save) {
            try {
                //等待文件生成
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                logger.warn("线程终止。。。。。。", e);
                Thread.currentThread().interrupt();
            }
            if (!temp.exists()) {
                logger.warn("结束录屏失败：视频文件不存在，文件路径：{}", fileName);
                return videoPath;
            }
            logger.info("视频文件路径：{}，视频文件大小：{}", temp.getAbsolutePath(), temp.length());
            try {
                //租户id
                String tenantId = info.getTenantId();

                logger.info("开始上传视频文件，任务信息：{}，android设备{}", JSON.toJSONString(info), info.getDeviceId());
                Attachment upload = HttpUtils.upload(mobileProperty.getServerAddr() + mobileProperty.getPublicUploadAddr(), temp.getAbsolutePath(), tenantId, taskId, FileBusinessTypeEnum.MOBILE_TASK_VIDEO.getCode());
                if (null != upload && StringUtils.isNotBlank(upload.getId())) {
                    logger.debug("文件上传成功，返回id：{}", upload.getId());
                    videoPath = upload.getUrlPath();
                }
            } catch (Exception e) {
                logger.error("结束录屏失败,文件上传未成功:", e);
                return videoPath;
            }
        }
        try {
            temp.deleteOnExit();
        } catch (Exception e) {
            logger.error("删除文件失败：", e);
        }
        return videoPath;
    }

    @Override
    public String getCurrentMessage(String deviceId) {
        AdbDevice currentDevice = AndroidDeviceManager.getInstance().getCurrentDevice(deviceId);
        if (null == currentDevice) {
            throw new RuntimeException("当前设备不在线");
        }
        AndroidAgentSession agentSession = new AndroidAgentSession(currentDevice, false);
        String send = null;
        try {
            agentSession.start();
            send = agentSession.send(new GetSmsListCommand(1));
            agentSession.send(CloseSessionCommand.getInstance());
            logger.info("拿到短信：{}",send);
        } catch (IOException e) {
            logger.error("agentsession启动，发送信息失败，e",e);
        }
        return send;
    }

    private File getScreenShotFile(DebuggerDeviceInfo info) {
        if (info.getLength() <= 0 || info.getWidth() <= 0) {
            throw new ExecuteException("截取图片的宽高不满足要求");
        }
        File snapShotFile = null;
        try {
            File cutFilePath = new File(System.getProperty("user.dir") + "/tempPic");
            if (!cutFilePath.exists()) {
                cutFilePath.mkdirs();
            }
            File cutFile = new File(cutFilePath.getAbsolutePath() + "/" + UUID.randomUUID().toString() + ".png");
            //转换比例
            String[] split = info.getResolution().split("\\*");
            int width = Integer.parseInt(split[0]);
            int height = Integer.parseInt(split[1]);
            boolean overTurn = false;
            Double screenWidth = info.getScreenLength().doubleValue();
            Double screenHeight = info.getScreenWidth().doubleValue();
            logger.info("前端传的屏幕宽：{},高：{}",screenWidth,screenHeight);
            if (screenWidth > screenHeight) {
                overTurn = true;
            }
            int realityWidth = width;
            int realityHeight = height;
            if (overTurn) {  //宽高反了的情况，调换
                if (width < height) {
                    realityWidth = height;
                    realityHeight = width;
                }
            } else {
                if (width > height) {
                    realityWidth = height;
                    realityHeight = width;
                }
            }
            Double realityCutWidth = info.getLength().doubleValue() / screenWidth * realityWidth;
            Double realityCutHeight = info.getWidth().doubleValue() / screenHeight * realityHeight;
            Double realityX = info.getX().doubleValue() / screenWidth * realityWidth;
            Double realityY = info.getY().doubleValue() / screenHeight * realityHeight;
            snapShotFile = getScreenShotInCommand(info.getDeviceId());
            ImgUtil.cut(snapShotFile, cutFile, new java.awt.Rectangle(realityX.intValue(), realityY.intValue(), realityCutWidth.intValue(), realityCutHeight.intValue()));
            return cutFile;
        } catch (ExecuteException e) {
            logger.error("截图失败", e);
            throw e;
        } catch (Exception e) {
            logger.error("截图失败", e);
            throw new ExecuteException("截取图片失败");
        } finally {
            if (null != snapShotFile && snapShotFile.exists()) {
                    logger.warn("临时文件【{}】删除失败", snapShotFile.getAbsolutePath());
            }
        }
    }

    private File getScreenShotInCommand(String deviceId) {
        File tmpPicFile = null;
        AndroidAgentSession agentSession = null;
        try {
            AdbDevice currentDevice = AndroidDeviceManager.getInstance().getCurrentDevice(deviceId);
            if (null == currentDevice) {
                logger.warn("设备【{}】不在线，请稍后再试",deviceId);
                throw new ExecuteException("当前设备已掉线无法截图");
            }
            agentSession = new AndroidAgentSession(currentDevice,false);
            agentSession.start();
            byte[] screenShotData = agentSession.send(TakeScreenshotCommand.getInstance());
            if (null != screenShotData && screenShotData.length > 0) { //截图
                logger.debug("收到手机【{}】截图,大小：{}", deviceId, screenShotData.length);
                File tmpPicDir = new File(System.getProperty("user.dir")+"/tempPic");
                if (!tmpPicDir.exists()) {
                    tmpPicDir.mkdir();
                }
                String tmpPicPath = tmpPicDir.getAbsolutePath() + "/" + System.currentTimeMillis() + ".png";
                tmpPicFile = new File(tmpPicPath);
                try (FileOutputStream fos = new FileOutputStream(tmpPicFile)){
                    fos.write(screenShotData);
                }catch (Exception e) {
                    logger.error("截图写入临时文件异常：",e);
                }
            }
        } catch (Exception e) {
            logger.error("设备【"+deviceId+"】截图异常", e);
        }finally {
            if (null != agentSession) {
                agentSession.closeSilence();
            }
        }
        return tmpPicFile;
    }


}
