package net.geedge.api.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.log.Log;
import net.geedge.api.entity.EnvApiYml;
import net.geedge.common.APIException;
import net.geedge.common.Constant;
import net.geedge.common.RCode;
import net.geedge.common.T;

import java.io.File;
import java.util.Map;

public class PlaybookRunnable implements Runnable {
    private final static Log log = Log.get();

    private AdbUtil adbUtil;
    private EnvApiYml envApiYml;
    private String tid;
    private File apkFile;
    private String packageName;
    private File scriptPath;
    private String type;
    private boolean reInstall;
    private boolean clearCache;
    private boolean unInstall;
    private boolean interrupt;

    public PlaybookRunnable(EnvApiYml envApiYml, File apkFile, File scriptPath, String tid, String packageName, String type, Boolean reInstall, Boolean clearCache, Boolean unInstall) {
        this.envApiYml = envApiYml;
        this.tid = tid;
        this.apkFile = apkFile;
        this.packageName = packageName;
        this.scriptPath = scriptPath;
        this.type = type;
        this.reInstall = reInstall;
        this.clearCache = clearCache;
        this.unInstall = unInstall;
        this.interrupt = false;
    }

    @Override
    public void run() {
        Thread.currentThread().setName("exec-playbook-thread-" + tid);
        Constant.ACTIVE_TASKS.add(this);
        File logFile = FileUtil.file(Constant.TEMP_PATH, tid, "result.log");
        File statusFile = FileUtil.file(Constant.TEMP_PATH, tid, "result.json");
        AdbUtil.CommandResult tcpdumpPackage = null;
        AdbUtil.CommandResult tcpdumpAll = null;
        try {
            Map resultMap = T.MapUtil.builder()
                    .put("status", "running")
                    .build();
            T.FileUtil.writeString(T.JSONUtil.toJsonStr(resultMap), statusFile, "UTF-8");

            if (interrupt) return;
            T.FileUtil.appendString(String.format("Running with %s:%s Android Simulator \n", envApiYml.getAdb().getHost(), envApiYml.getAdb().getPort()), logFile, "UTF-8");
            adbUtil = new AdbUtil(envApiYml.getAdb(), new CommandExec(logFile));

            // Check if the package is installed
            if (interrupt) return;
            boolean packageIsInstall = adbUtil.findPackageInstall(packageName);
            if (packageIsInstall) {
                if (!reInstall) {
                    // install apk
                    if (interrupt) return;
                    AdbUtil.CommandResult install = adbUtil.install(apkFile.getAbsolutePath(), true, true);
                    if (0 != install.exitCode()) {
                        T.FileUtil.appendString(String.format("ERROR: Install apk failed: exit code %s \n", install.exitCode()), logFile, "UTF-8");
                        throw new APIException(install.output());
                    }
                }
            } else {
                // install apk
                if (interrupt) return;
                AdbUtil.CommandResult install = adbUtil.install(apkFile.getAbsolutePath(), true, true);
                if (0 != install.exitCode()) {
                    T.FileUtil.appendString(String.format("ERROR: Install apk failed: exit code %s \n", install.exitCode()), logFile, "UTF-8");
                    throw new APIException(install.output());
                }
            }

            //Close other apps
            if (interrupt) return;
            adbUtil.closeOtherApps();

            // clear app data
            if (interrupt) return;
            if (clearCache) {
                AdbUtil.CommandResult clearData = adbUtil.clearAppData(packageName);
                if (0 != clearData.exitCode()) {
                    T.FileUtil.appendString(String.format("ERROR: Clear %s data error: exit code %s \n", packageName, clearData.exitCode()), logFile, "UTF-8");
                    throw new APIException(clearData.output());
                }
            }

            // Launch the app
            if (interrupt) return;
            adbUtil.startApp(packageName);

            // star tcpdump: package name
            if (interrupt) return;
            tcpdumpPackage = adbUtil.startTcpdump(packageName);
            if (0 != tcpdumpPackage.exitCode()) {
                T.FileUtil.appendString(String.format("ERROR: Start tcpdump %s failed: exit code %s \n", packageName, tcpdumpPackage.exitCode()), logFile, "UTF-8");
                throw new APIException(String.format("tcpdump %s error", packageName));
            }

            // star tcpdump: all
            if (interrupt) return;
            tcpdumpAll = adbUtil.startTcpdump(T.StrUtil.EMPTY);
            if (0 != tcpdumpAll.exitCode()) {
                T.FileUtil.appendString(String.format("ERROR: Start tcpdump all failed: exit code %s \n", tcpdumpAll.exitCode()), logFile, "UTF-8");
                throw new APIException("tcpdump all error");
            }

            // exec playbook
            if (interrupt) return;
            AdbUtil.CommandResult airtestResult = adbUtil.execPlaybook(scriptPath.getPath(), tid, packageName, type, logFile);
            if (0 != airtestResult.exitCode()) {
                T.FileUtil.appendString(String.format("ERROR: Exec playbook failed: exit code %s \n", airtestResult.exitCode()), logFile, "UTF-8");
                throw new APIException("playbook exec error");
            }

            // stop package tcpdump
            if (interrupt) return;
            stopTcpdump(tcpdumpPackage, logFile, packageName);

            // stop all tcpdump
            if (interrupt) return;
            stopTcpdump(tcpdumpAll, logFile, T.StrUtil.EMPTY);

            resultMap = T.MapUtil.builder()
                    .put("status", "done")
                    .build();
            T.FileUtil.writeString(T.JSONUtil.toJsonStr(resultMap), statusFile, "UTF-8");
        } catch (Exception e) {
            log.error(e);
            Map resultMap = T.MapUtil.builder()
                    .put("status", "error")
                    .build();
            T.FileUtil.writeString(T.JSONUtil.toJsonStr(resultMap), statusFile, "UTF-8");
        } finally {
            if (T.StrUtil.isNotEmpty(tcpdumpPackage.output())) {
                AdbUtil.CommandResult packageTcpdump = adbUtil.stopTcpdump(tcpdumpPackage.output());
                adbUtil.execShellCommand(String.format("shell rm -rf %s", packageTcpdump.output()));
            }
            if (T.StrUtil.isNotEmpty(tcpdumpAll.output())) {
                AdbUtil.CommandResult allTcpdump = adbUtil.stopTcpdump(tcpdumpAll.output());
                adbUtil.execShellCommand(String.format("shell rm -rf %s", allTcpdump.output()));
            }

            adbUtil.stopApp(packageName);

            if (unInstall) {
                adbUtil.uninstall(packageName);
            }
            T.FileUtil.appendString(String.format("Job execution ends"), logFile, "UTF-8");
            Constant.ACTIVE_TASKS.remove(this);
        }
    }

    public void interrupt() {
        this.interrupt = true;
        adbUtil.setInterrupt(true);
    }

    private void stopTcpdump(AdbUtil.CommandResult tcpdump, File logFile, String packageName) {
        // stop tcpdump
        AdbUtil.CommandResult stopTcpdump = adbUtil.stopTcpdump(tcpdump.output());
        if (0 != stopTcpdump.exitCode()) {
            T.FileUtil.appendString(String.format("ERROR: Stop tcpdump failed: exit code %s \n", stopTcpdump.exitCode()), logFile, "UTF-8");
            throw new APIException(stopTcpdump.output());
        }

        // pull pcap file
        String filePath = stopTcpdump.output();
        packageName = T.StrUtil.isEmpty(packageName) ? "all" : packageName;
        File localPcapFile = T.FileUtil.file(Constant.TEMP_PATH, tid, String.format("%s-%s%s", tcpdump.output(), packageName, ".pcap"));
        if (T.StrUtil.isEmpty(filePath)) {
            throw new APIException(RCode.NOT_EXISTS);
        }

        AdbUtil.CommandResult pull = adbUtil.pull(filePath, localPcapFile.getAbsolutePath());
        if (0 != pull.exitCode()) {
            T.FileUtil.appendString(String.format("ERROR: Pull pcap file failed: exit code %s \n", pull.exitCode()), logFile, "UTF-8");
            throw new APIException(pull.output());
        }

        // delete android pcap
        adbUtil.execShellCommand(String.format("shell rm -rf %s", filePath));
    }
}