package net.northking.cctp.upperComputer.service;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.appium.java_client.AppiumDriver;
import net.northking.cctp.upperComputer.config.MobileProperty;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.entity.DebuggerDeviceInfo;
import net.northking.cctp.upperComputer.entity.PhoneEntity;
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
import net.northking.cctp.upperComputer.exception.AppiumException;
import net.northking.cctp.upperComputer.exception.ExecuteException;
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
import net.northking.cctp.upperComputer.service.thread.IOSDeviceAllInfoThread;
import net.northking.cctp.upperComputer.service.thread.IOSDeviceInfoByPackageThread;
import net.northking.cctp.upperComputer.deviceManager.IOSDeviceManager;
import net.northking.cctp.upperComputer.utils.HttpUtils;
import net.northking.cctp.upperComputer.utils.ScreenShotUtils;
import net.northking.cctp.upperComputer.webSocket.thread.IosScreenResponseThread;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.WebElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service("iosService")
public class IosDebuggerServiceImpl extends AbstractDebuggerService {

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

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

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

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

    private final String LAUNCH_APP_FAIL_FLAG = "invalid code signature"; //应用未信任启动失败标志
    @Autowired
    @Override
    public void setMobileProperty(MobileProperty mobileProperty) {
        super.setMobileProperty(mobileProperty);
    }

    @Override
    public Object getUiTree(String deviceId) {
        PhoneEntity phoneEntity = IOSDeviceManager.getInstance().getPhoneEntity(deviceId);
        if (null == phoneEntity) {
            throw new ParamMistakeException("未读取到设备信息，请确认设备是否接上");
        }
        int port8100 = phoneEntity.getPort8100();
        URI uri = null;
        try {
            uri = new URI("http://127.0.0.1:" + port8100 + "/source");
        } catch (URISyntaxException e) {
            logger.error("请求ios的ui树路径有误", e);
            throw new AppiumException("请求的ui树失败");
        }
        JSONObject jsonObject = null;
        try {
            jsonObject = HttpUtils.doGetUiTree(uri, JSONObject.class);
        } catch (Exception e) {
            logger.warn("请求设备【{}】ui层级树失败",deviceId,e);
        }
        if (null != jsonObject) {
            String xmlResponse = jsonObject.getJSONObject("value").getString("value");
            try {
                return xmlResponse;
            } catch (Exception e) {
                logger.error("xml正则替换异常", e);
            }
        }
        return null;

    }

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

    @Override
    public boolean initApp(DebuggerDeviceInfo info) {
        return false;
    }

    @Override
    public String getScreenShot(DebuggerDeviceInfo info) {
        File shotFile = deviceHandleHelper.getScreenShotFile(info.getDeviceId(),info.getX(),info.getY(),info.getLength(),info.getWidth(),info.getScreenLength(),info.getScreenWidth());
        String path = null;
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.set("Authorization", "Bearer " + info.getUserToken());
            String taskId = info.getCaseId();
            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) {
        return false;
    }

    @Override
    public boolean cleanData(String deviceId, String packageName) {
        return false;
    }

    @Override
    public String getPackageVersion(String deviceId, String packageName) {
        return deviceHandleHelper.getOldPackageCode(deviceId, packageName);
    }

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

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

    @Override
    public String uploadShotAllScreen(DebuggerDeviceInfo info) {
        File file = ScreenShotUtils.getIOSMobileScreenShot(info.getDeviceId());
        String path = null;
        try {
            //租户id
            String tenantId = info.getTenantId();
            String taskId = info.getTaskId();
            logger.debug("开始上传截图，任务信息：{}",JSON.toJSONString(info));
            Attachment upload = HttpUtils.upload(mobileProperty.getServerAddr() + mobileProperty.getPublicUploadAddr(), file.getAbsolutePath(), tenantId, taskId, FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode());
            if (null != upload && org.apache.commons.lang3.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;
    }

    @Override
    public boolean startRecord(DebuggerDeviceInfo info) {
        IosScreenResponseThread screenThread = IOSDeviceManager.getInstance().getScreenThread(info.getDeviceId());
        String taskId = info.getTaskId();
        String resolution = info.getResolution();
        String[] split = resolution.split("\\*");
        int width = Integer.parseInt(split[0]);
        int height = Integer.parseInt(split[1]);
        int isBigger = height - width;
        if (isBigger < 0) {  //高比宽小，换一下
            int temp = width;
            width = height;
            height = temp;
        }
        if (null == screenThread || screenThread.isInterrupted() || !screenThread.isAlive()) {   //没人连手机
            PhoneEntity phoneEntity = IOSDeviceManager.getInstance().getPhoneEntity(info.getDeviceId());
            screenThread = new IosScreenResponseThread(phoneEntity, taskId);
            screenThread.start();
            IOSDeviceManager.getInstance().saveDeviceScreenThread(info.getDeviceId(),screenThread);
        }
        screenThread.startRecordScreen("default", width, height,"2", taskId);
        return true;
    }

    @Override
    public String endRecord(DebuggerDeviceInfo info) {
        IosScreenResponseThread screenThread = IOSDeviceManager.getInstance().getScreenThread(info.getDeviceId());
        if (null != screenThread && screenThread.isAlive() && !screenThread.isInterrupted()) {
            String tenantId = info.getTenantId();
            //增加是否保存录屏文件的参数
            String result = screenThread.stopRecord(tenantId,info.getSave());
            return result;
        }
        return null;
    }

    @Override
    public boolean isAppInstalled(String deviceId, String appPackage) {
        return deviceHandleHelper.isAppInstalled(deviceId, appPackage);
    }

    @Override
    public boolean removeApp(String deviceId, String appPackage) {
        return deviceHandleHelper.removeApp(deviceId, appPackage);
    }


    /**
     * 安装应用
      * @param deviceId
     * @param appPath
     * @return
     */
    @Override
    public boolean installApp(String deviceId, String appPath) {
        return deviceHandleHelper.installApp(deviceId, appPath);
    }

    @Override
    public boolean activeApp(String deviceId, String appPackage) {
        return deviceHandleHelper.activateApp(deviceId, appPackage);
    }

    @Override
    public String getCurrentMessage(String deviceId) {
        return null;
    }

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

    @Override
    public boolean terminateApp(String deviceId, String appPackage) {
        String[] cmd = {"tidevice", "-u", deviceId, "kill", appPackage};
        ProcessBuilder builder = new ProcessBuilder(Arrays.asList(cmd)).redirectErrorStream(true);
        Process process = null;
        try {
            process = builder.start();
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringBuilder stringBuilder = new StringBuilder();
            String line = null;
            while ((line = in.readLine()) != null) {
                stringBuilder.append(line);
            }
            String result = stringBuilder.toString();
            if (result.trim().startsWith("Kill pid:")) {
                logger.debug("手机【{}】应用【{}】已关闭，关闭消息：{}", deviceId, appPackage, result);
                return true;
            } else {
                logger.debug("手机【{}】应用【{}】关闭失败，关闭消息：{}", deviceId, appPackage, result);
                return false;
            }
        } catch (IOException e) {
            logger.error("手机【{}】应用【{}】关闭出现问题",deviceId,appPackage,e);
            return false;
        }
    }

    @Override
    public boolean recordDevicePer(DebuggerDeviceInfo info) {
        IOSDeviceInfoByPackageThread iosDeviceInfoByPackageThread = queryDeviceInfoByPackageMap.get(info.getCaseId());
        if (null == iosDeviceInfoByPackageThread || !iosDeviceInfoByPackageThread.isAlive() || iosDeviceInfoByPackageThread.isInterrupted()) {
            logger.debug("开始记录任务id【{}】的应用的参数。。。。。。。。。。", info.getCaseId());
            iosDeviceInfoByPackageThread = new IOSDeviceInfoByPackageThread(info);
            iosDeviceInfoByPackageThread.start();
            queryDeviceInfoByPackageMap.put(info.getCaseId(), iosDeviceInfoByPackageThread);
        } else {
            logger.debug("任务id【{}】的应用的参数线程存在，无需创建。。。。。。。。。。", info.getCaseId());
        }
        IOSDeviceAllInfoThread deviceAllInfoThread = queryDeviceInfoMap.get(info.getCaseId());
        if (null == deviceAllInfoThread || !deviceAllInfoThread.isAlive() || deviceAllInfoThread.isInterrupted()) {
            logger.debug("开始记录任务id【{}】的设备的参数。。。。。。。。。。", info.getCaseId());
            deviceAllInfoThread = new IOSDeviceAllInfoThread(info,iosDeviceInfoByPackageThread.getDeviceCpuNum());
            deviceAllInfoThread.start();
            queryDeviceInfoMap.put(info.getCaseId(), deviceAllInfoThread);
        } else {
            logger.debug("任务id【{}】的设备的参数线程存在，无需创建。。。。。。。。。。", info.getCaseId());
        }
        return true;
    }
}
