package net.northking.cctp.upperComputer.automation.handler;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import net.northking.cctp.upperComputer.automation.constants.AutomationRequestCmd;
import net.northking.cctp.upperComputer.automation.constants.UpperParamKey;
import net.northking.cctp.upperComputer.automation.entity.CmdAutomationRequest;
import net.northking.cctp.upperComputer.automation.entity.CmdAutomationResponse;
import net.northking.cctp.upperComputer.automation.entity.ScreenInfo;
import net.northking.cctp.upperComputer.automation.utils.CloudTestOcrHelper;
import net.northking.cctp.upperComputer.automation.utils.OcrHelper;
import net.northking.cctp.upperComputer.config.HttpRequestPathConfig;
import net.northking.cctp.upperComputer.config.MobileProperty;
import net.northking.cctp.upperComputer.driver.ios.command.data.DragXYData;
import net.northking.cctp.upperComputer.driver.ios.command.data.TapXYData;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.exception.ExecuteException;
import net.northking.cctp.upperComputer.utils.HttpUtils;
import net.northking.cctp.upperComputer.utils.SpringUtils;
import net.northking.cctp.upperComputer.utils.deviceHepler.DeviceHelper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.websocket.Session;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;


public abstract class AbstractAutomationHandler implements AutomationMessageHandler{

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

    protected String serial;

    protected Session engineSession;

    protected DeviceHelper deviceHandleHelper;

    protected ScreenInfo screenInfo;

    protected OcrHelper ocrHelper;

    public AbstractAutomationHandler() {
        ocrHelper = new CloudTestOcrHelper();
    }

    @Override
    public void automationHandle(CmdAutomationRequest request, Session engineSession) {
        String cmd = request.getCmd();
        logger.debug("收到设备【{}】自动化指令:【{}】",serial,cmd);
        if (AutomationRequestCmd.OPEN_MOBILE_NOTIFICATION_BAR.equals(cmd)) { //打开手机通知栏
            openMobileNotificationBar(request);
        } else if (AutomationRequestCmd.CLOSE_MOBILE_NOTIFICATION_BAR.equals(cmd)){//关闭手机通知栏
            closeMobileNotificationBar(request);
        } else if (AutomationRequestCmd.INIT_APP.equals(cmd)) {
            initApplication(request);
        } else if (AutomationRequestCmd.SEARCH_ELEMENT_BY_NODE.equals(cmd)) {
            searchNodeByNodeTree(request);
        } else if (AutomationRequestCmd.SEARCH_ELEMENT_BY_POINT.equals(cmd)) {
            searchNodeByPoint(request);
        } else if (AutomationRequestCmd.SEARCH_ELEMENT_BY_OCR.equals(cmd)) {
            searchNodeByOcr(request);
        } else if (AutomationRequestCmd.SEARCH_ELEMENT_BY_IMAGE.equals(cmd)) {
            searchNodeByImage(request);
        } else if (AutomationRequestCmd.GET_ELEMENT_VALUE_BY_NODE.equals(cmd)) {
            getElementValueByNode(request);
        }else if (AutomationRequestCmd.GET_ELEMENT_VALUE_BY_OCR.equals(cmd)) {
            getElementValueByOcr(request);
        }else if (AutomationRequestCmd.GET_VERIFICATION_CODE_BY_NODE.equals(cmd)) {
            getVerificationCodeByNode(request);
        }else if (AutomationRequestCmd.GET_VERIFICATION_CODE_BY_OCR.equals(cmd)) {
            getVerificationCodeByOcr(request);
        }else if (AutomationRequestCmd.LONG_PRESS.equals(cmd)) {
            longPress(request);   //暂时没用
        }else if (AutomationRequestCmd.SWIPE.equals(cmd)) {
            swipe(request);
        }else if (AutomationRequestCmd.INPUT_TEXT.equals(cmd)) {
            inputText(request);
        }else if (AutomationRequestCmd.CLICK_POINT.equals(cmd)) {
            clickPoint(request);
        }else if (AutomationRequestCmd.HANDLE_ELEMENT_BY_NODE.equals(cmd)) {
            handleNodeByNodeTree(request);
        }else if (AutomationRequestCmd.HANDLE_ELEMENT_BY_POINT.equals(cmd)) {
            handleNodeByPoint(request);
        }else if (AutomationRequestCmd.HANDLE_ELEMENT_BY_OCR.equals(cmd)) {
            handleNodeByOcr(request);
        }else if (AutomationRequestCmd.HANDLE_ELEMENT_BY_IMAGE.equals(cmd)) {
            handleNodeByImage(request);
        } else if (AutomationRequestCmd.CLICK_TEXT_BY_OCR.equals(cmd)) {
            clickTextByOcr(request);
        } else if (AutomationRequestCmd.CLICK_TEXT_BY_OCR_NEW.equals(cmd)) {
            clickTextByOcrNew(request);
        } else if (AutomationRequestCmd.IF_TEXT.equals(cmd)) {
            ifText(request);
        } else if (AutomationRequestCmd.HANDLE_APP.equals(cmd)) {
            handleApp(request);
        } else if (AutomationRequestCmd.PRESS_HOME_KEY.equals(cmd)) {
            pressHomeKey(request);
        } else if (AutomationRequestCmd.INPUT_PASSWORD_BY_OCR.equals(cmd)) {
            inputPasswordByOcr(request);
        } else if (AutomationRequestCmd.GET_ELEMENT_MONEY_TEXT.equals(cmd)) {
            getElementMoneyText(request);
        } else if (AutomationRequestCmd.SCREEN_SHOT.equals(cmd)) {
            screenShot(request);
        } else if (AutomationRequestCmd.GET_ELEMENT_VALUE_BY_PATH_OCR.equals(cmd)) {
            getElementValueByPathOcr(request);
        } else if (AutomationRequestCmd.GET_TEXT_DIRECTION_TEXT.equals(cmd)) {
            getTextDirectionText(request);
        } else if (AutomationRequestCmd.PRESS_AND_SWIPE.equals(cmd)) {
            pressAndSwipe(request);
        } else if (AutomationRequestCmd.SWIPE_TO_FIND_TARGET_ELEMENT.equals(cmd)) {
            swipeForSwipeToFindTargetElement(request);
        } else if (AutomationRequestCmd.ELEMENT_SWIPE.equals(cmd)) {
            elementSwipe(request);
        }
    }

    protected void sendResultToEngine(CmdAutomationResponse response){
        String result = JSONObject.toJSONString(response);
        logger.debug("步骤token:【{}】执行完成,返回结果:{}",response.getStepToken(),result);
        if (null != engineSession && engineSession.isOpen()) {
            synchronized (engineSession){
                try {
                    engineSession.getBasicRemote().sendText(result);
                } catch (IOException e) {
                    logger.warn("步骤token:【{}】执行完成,返回结果时连接出现异常", response.getStepToken(),e);
                }
            }
        } else {
            logger.warn("步骤token:【{}】执行完成,返回结果时引擎连接已经关闭", response.getStepToken());
        }

    }

    protected Map<String, Object> getAppDetail(String appId) throws URISyntaxException {
        Map<String, Object> data = null;
        String path = SpringUtils.getProperties("nk.mobile-computer.publicFindAppInfo");
        String server = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
        JSONObject result = HttpUtils.doGet(new URI(server + path + "?appId=" + appId), JSONObject.class);
        logger.debug("查询app的结果：{}", JSONObject.toJSONString(result));
        if (result.getBoolean("success")) {
            data = result.getJSONObject("data");
        }
        return data;
    }

    /**
     * 安装app
     *
     * @param appId
     * @param appName
     */
    protected boolean installApp(String appId, String appName) {
        String appPath = "";
        logger.info("Begin to download app[{}] to upperComputer...", appName);
        try {
            appPath = deviceHandleHelper.downloadAppToLocal(appId, appName);
        } catch (Exception e) {
            logger.error("下载app[{}]出错，app名字：{}", appId, appName, e);
            return false;
        }
        if (StringUtils.isBlank(appPath)) {
            logger.info("download app[{}] finish,but app is null ............", appName);
            return false;
        }
        logger.info("download app[{}] finish,start install ............", appName);
        boolean installSuccess = false;
        try {
            installSuccess = deviceHandleHelper.installApp(this.serial, appPath);
        } catch (Exception e) {
            logger.error("安装app[{}]出错，app名字：{}", appId, appName, e);
            return false;
        }
        logger.info("App[{}] on device[{}] installed finish,success:{}", appName, this.serial, installSuccess);
        return installSuccess;
    }

    protected String getFileBase64(File file) {
        String encode = Base64.encode(file);
        return encode;
    }

    protected String getOcrAreaToBase64(CmdAutomationRequest request) {
        Map<String, Object> data = request.getData();
        Integer x = (Integer) data.get(UpperParamKey.X);
        Integer y = (Integer) data.get(UpperParamKey.Y);
        Integer screenWidth = (Integer) data.get(UpperParamKey.SCREEN_WIDTH);
        Integer screenHeight = (Integer) data.get(UpperParamKey.SCREEN_HEIGHT);
        Integer cutWidth = (Integer) data.get(UpperParamKey.WIDTH);
        Integer cutHeight = (Integer) data.get(UpperParamKey.HEIGHT);
        String ocrText = (String) data.get(UpperParamKey.OCR_TEXT);
        logger.debug("拿到的截图参数----->>>>>x:{},y:{},screenWidth:{},screenHeight:{},cutWidth:{},cutHeight:{},查找的文本:{}", x, y, screenWidth, screenHeight, cutWidth, cutHeight, ocrText);
        File file = deviceHandleHelper.getScreenShotFile(serial, x, y, cutWidth, cutHeight, screenWidth, screenHeight, screenInfo.getWidth(), screenInfo.getHeight());
        logger.debug("执行步骤token：{}，ocr截图：{}", request.getStepToken(), file.getAbsolutePath());
        String base64Str = getFileBase64(file);
        return base64Str;
    }

    protected String getClickTextOcrAreaToBase64(CmdAutomationRequest request) {
        Map<String, Object> data = request.getData();
        String ocrText = (String) data.get(UpperParamKey.OCR_TEXT);
        logger.debug("拿到的截图参数----->>>>>查找的文本:{}", ocrText);
        File file = deviceHandleHelper.getScreenShotFile(serial);
        if (null == file) {
            logger.error("设备【{}】截图为空", serial);
            throw new ExecuteException("设备截图失败");
        }
        logger.debug("执行步骤token：{}，ocr截图：{}", request.getStepToken(), file.getAbsolutePath());
        return getFileBase64(file);
    }


    protected TapXYData queryImageArea(CmdAutomationRequest request) {
        TapXYData result = null;
        Map<String, Object> data = request.getData();
        String picName = (String) data.get(UpperParamKey.IMG_URL);
        String resolution_s = (String) data.get(UpperParamKey.RESOLUTION_S);
        String resolution_l = (screenInfo.getWidth() + "x" + screenInfo.getHeight());
        logger.info("以图找图分辨率参数resolution_s:{}, resolution_l:{}", resolution_s, resolution_l);
        File sourceFile = deviceHandleHelper.getScreenShotFile(serial);     // 通过helper进行截图。
        if (null == sourceFile) {
            return result;
        } else {
            result = ocrHelper.doImgFindImg(sourceFile, resolution_s, resolution_l, picName, request.getStepToken());
        }
        return result;
    }

    protected TapXYData queryPoint(CmdAutomationRequest request) {
        TapXYData tapXYData = null;
        Map<String, Object> data = request.getData();
        Integer x = (Integer) data.get(UpperParamKey.X);
        Integer y = (Integer) data.get(UpperParamKey.Y);
        Integer screenWidth = (Integer) data.get(UpperParamKey.SCREEN_WIDTH);
        Integer screenHeight = (Integer) data.get(UpperParamKey.SCREEN_HEIGHT);
        boolean overTurn = false;
        if (screenWidth > screenHeight) {
            overTurn = true;
        }
        int width = screenInfo.getWidth();
        int height = screenInfo.getHeight();
        int realityWidth = width;
        int realityHeight = height;
        if (overTurn) {  //宽高反了的情况，调换
            if (width < height) {
                realityWidth = height;
                realityHeight = width;
            }
        } else {
            if (width > height) {
                realityWidth = height;
                realityHeight = width;
            }
        }
        Double realityX = x.doubleValue() / screenWidth.doubleValue() * realityWidth;
        Double realityY = y.doubleValue() / screenHeight.doubleValue() * realityHeight;
        if (realityX <= 0 || realityX >= realityWidth || realityY <= 0 || realityY >= realityHeight) {
            logger.warn("步骤id【{}】坐标方式超出了屏幕范围", request.getStepToken());
        }
        int clickTrueX = new Double(realityX).intValue();
        int clickTrueY = new Double(realityY).intValue();
        tapXYData = new TapXYData();
        tapXYData.setX(clickTrueX);
        tapXYData.setY(clickTrueY);
        logger.debug("实际点击的位置------->>>>>>x:{},y:{}", clickTrueX, clickTrueY);
        return tapXYData;
    }

    public DragXYData calculateSwipePoint(String direction, Integer size) {
        int width = screenInfo.getWidth() / screenInfo.getScale();
        int height = screenInfo.getHeight() / screenInfo.getScale();
        int x1, y1, x2, y2;
        if ("up".equalsIgnoreCase(direction)) {
            x1 = x2 = width / 2;
            y1 = height * 7 / 10;
            if (null == size || size <= 0) {
                y2 = y1 - height / 2;
            } else {
                y2 = y1 - size;
            }
        } else if ("down".equalsIgnoreCase(direction)) {
            x1 = x2 = width / 2;
            y1 = height * 3 / 10;
            if (null == size || size <= 0) {
                y2 = y1 + height / 2;
            } else {
                y2 = y1 + size;
            }
        } else if ("left".equalsIgnoreCase(direction)) {
            y1 = y2 = height / 2;
            x1 = width * 7 / 10;
            if (null == size || size <= 0) {
                x2 = x1 - width / 2;
            } else {
                x2 = x1 - size;
            }
        } else if ("right".equalsIgnoreCase(direction)) {
            y1 = y2 = height / 2;
            x1 = width * 3 / 10;
            if (null == size || size <= 0) {
                x2 = x1 + width / 2;
            } else {
                x2 = x1 + size;
            }
        } else {
            throw new ExecuteException("不支持的滑动方向");
        }
        DragXYData dragXYData = new DragXYData();
        dragXYData.setX(x1);
        dragXYData.setToX(x2);
        dragXYData.setY(y1);
        dragXYData.setToY(y2);
        dragXYData.setDuration(0);
        return dragXYData;
    }

    @Override
    public void handleApp(CmdAutomationRequest request) {
        Map<String, Object> data = request.getData();
        String appPackage = (String) data.get(UpperParamKey.APP_PACKAGE);
        String type = (String) data.get(UpperParamKey.TYPE);
        CmdAutomationResponse response = CmdAutomationResponse.builderFailure(request, "app处理失败").withData(false);
        IosDebuggerServiceImpl iosService = SpringUtils.getBean(IosDebuggerServiceImpl.class);
        try {
            if ("0".equalsIgnoreCase(type)) {
                if (!deviceHandleHelper.isAppInstalled(this.serial, appPackage)) {
                    response = CmdAutomationResponse.builderFailure(request, "app未安装");
                    return;
                }
                logger.info("关闭harmony的app:{}", appPackage);
                boolean success = deviceHandleHelper.terminateApp(serial, appPackage);
                if (!success) {
                    response = CmdAutomationResponse.builderFailure(request, "app关闭失败").withData(false);
                } else {
                    response = CmdAutomationResponse.builderSuccess(request, "app关闭成功").withData(true);
                }
            } else if ("1".equalsIgnoreCase(type)) {
                logger.info("启动harmony的app:{}", appPackage);
                if (!deviceHandleHelper.isAppInstalled(this.serial, appPackage)) {
                    response = CmdAutomationResponse.builderFailure(request, "app未安装");
                    return;
                }
                boolean success = false;
                int count = 0;
                do {
                    success = deviceHandleHelper.activateApp(this.serial, appPackage);
                    if (!success) {
                        logger.info("app启动失败，尝试重试启动：第 {} 次", ++count);
                    }
                } while (!success && count < 5);
                if (!success) {
                    response = CmdAutomationResponse.builderFailure(request, "app启动失败").withData(false);
                } else {
                    response = CmdAutomationResponse.builderSuccess(request, "app启动成功").withData(true);
                }
            } else {
                logger.info("重启harmony的app:{}", appPackage);
                if (!deviceHandleHelper.isAppInstalled(this.serial, appPackage)) {
                    response = CmdAutomationResponse.builderFailure(request, "app未安装");
                    return;
                }
                boolean success = deviceHandleHelper.terminateApp(serial, appPackage);
                if (!success) {
                    response = CmdAutomationResponse.builderFailure(request, "app关闭失败").withData(false);
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ie) {
                    logger.error(ie.getMessage());
                }
                boolean successActive = deviceHandleHelper.activateApp(this.serial, appPackage);
                if (!successActive) {
                    response = CmdAutomationResponse.builderFailure(request, "app启动失败").withData(false);
                } else {
                    response = CmdAutomationResponse.builderSuccess(request, "app启动成功").withData(true);
                }
            }
        } catch (Exception e) {
            logger.error("处理app失败，原因：", e);
            response = CmdAutomationResponse.builderFailure(request, e.getMessage());
        } finally {
            sendResultToEngine(response);
        }
    }

    public abstract void initScreenData();

    /**
     * 给截图增加红点操作
     * @param originFile
     * @param x
     * @param y
     * @return
     */
    protected File addSignatureToImage(File originFile, Integer x, Integer y) {
        if (x == null || y == null) {
            return originFile;
        }
        if (!originFile.exists()) {
            logger.info("截图文件不存在：{}", originFile.getAbsolutePath());
            return originFile;
        }
        String fullName = originFile.getName();
        String suffix = fullName.substring(fullName.lastIndexOf(".") + 1);
        String name = fullName.substring(0, fullName.lastIndexOf("."));
        File targetFile = new File(originFile.getParentFile().getAbsolutePath() + File.separator + name + "_with_dot" + "." + suffix);
        try {
            BufferedImage bi = ImageIO.read(originFile);
            Graphics2D g2d = bi.createGraphics();   // 生成画布
            g2d.setColor(Color.RED);    // 红色
            g2d.fillOval(x, y, 20, 20); // 在图片的x，y上画上一个直径20的实心原点
            g2d.dispose();
            ImageIO.write(bi, suffix, targetFile);
        } catch (IOException e) {
            logger.error("io异常", e);
        } catch (Exception e) {
            logger.error("添加红点失败", e);
        }
        if (!targetFile.exists()) {
            targetFile = originFile;
        }
        return targetFile;
    }

    protected String doUploadExecuteScreenShotToServer(String tenantId, String absolutePath) {
        String path = null;
        MobileProperty mobileProperty = SpringUtils.getBean(MobileProperty.class);
        String serverAddr = mobileProperty.getServerAddr();
        String publicUploadAddr = mobileProperty.getPublicUploadAddr();
        Attachment upload = HttpUtils.upload(serverAddr + publicUploadAddr, absolutePath, tenantId);
        if (null != upload && org.apache.commons.lang3.StringUtils.isNotBlank(upload.getId())) {
            path = upload.getUrlPath();
            logger.debug("文件上传成功，返回id：{},path:{}", upload.getId(),path);
        }
        return path;
    }

    @Override
    public void screenShot(CmdAutomationRequest request) {
        CmdAutomationResponse response = CmdAutomationResponse.builderFailure(request, "屏幕截图失败");
        logger.debug("开始上传截图，信息：{}", JSON.toJSONString(request));
        try {
            Map<String, Object> data = request.getData();
            Integer x = (Integer) data.get(UpperParamKey.X);
            Integer y = (Integer) data.get(UpperParamKey.Y);
            File file = deviceHandleHelper.getScreenShotFile(serial);
            File addSignatureToImage = addSignatureToImage(file, x, y);
            String path = null;
            try {
                //租户id
                String tenantId = (String) data.get(UpperParamKey.TENANT_ID);
                path = doUploadExecuteScreenShotToServer(tenantId, addSignatureToImage.getAbsolutePath());
            } catch (Exception e) {
                logger.error("截图失败", e);
                response = CmdAutomationResponse.builderFailure(request, e.getMessage());
            } finally {
                if (file.exists()) {
                    boolean delete = file.delete();
                    if (!delete) {
                        logger.warn("临时文件【{}】删除失败", file.getAbsolutePath());
                    }
                }
                if (addSignatureToImage.exists()) {
                    boolean delete = addSignatureToImage.delete();
                    if (!delete) {
                        logger.warn("临时文件【{}】删除失败", addSignatureToImage.getAbsolutePath());
                    }
                }
            }
            if (StringUtils.isNotBlank(path)) {
                response = CmdAutomationResponse.builderSuccess(request, "屏幕截图成功").withData(path);
            }
        } catch (Exception e) {
            logger.error("屏幕截图失败，原因：", e);
            response = CmdAutomationResponse.builderFailure(request, e.getMessage());
        } finally {
            sendResultToEngine(response);
        }
    }

}
