package io.jpom.controller.monitor;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.JsonMessage;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.jpom.common.BaseAgentController;
import io.jpom.common.commander.AbstractProjectCommander;
import io.jpom.common.commander.AbstractSystemCommander;
import io.jpom.model.data.ProjectInfoModel;
import io.jpom.model.system.NetstatModel;
import io.jpom.model.system.ProcessModel;
import io.jpom.system.AgentConfigBean;
import io.jpom.util.CommandUtil;
import io.jpom.util.JvmUtil;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.math.BigDecimal;
import java.util.List;

/**
 * 内存查看
 *
 * @author Administrator
 */
@RestController
@RequestMapping(value = "/manage/")
public class InternalController extends BaseAgentController {

    /**
     * 获取内存信息
     *
     * @param tag 程序运行标识
     * @return json
     * @throws Exception 异常
     */
    @RequestMapping(value = "internal_data", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String getInternal(String tag, String copyId) throws Exception {
        String tagId = ProjectInfoModel.JavaCopyItem.getTagId(tag, copyId);
        int pid = AbstractProjectCommander.getInstance().getPid(tagId);
        if (pid <= 0) {
            return JsonMessage.getString(400, "");
        }
        JSONObject jsonObject = new JSONObject();
        ProcessModel item = AbstractSystemCommander.getInstance().getPidInfo(pid);
        jsonObject.put("process", item);
        JSONObject beanMem = getBeanMem(tagId);
        jsonObject.put("beanMem", beanMem);
        //获取端口信息
        List<NetstatModel> netstatModels = AbstractProjectCommander.getInstance().listNetstat(pid, false);
        jsonObject.put("netstat", netstatModels);
        return JsonMessage.getString(200, "", jsonObject);
    }

    /**
     * 查询监控线程列表
     *
     * @param tag 程序运行标识
     * @return json
     * @throws Exception 异常
     */
    @RequestMapping(value = "threadInfos", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String getThreadInfos(String tag, String copyId) throws Exception {
        int limit = getParameterInt("limit", 10);
        int page = getParameterInt("page", 1);

        ThreadMXBean bean = JvmUtil.getThreadMXBean(ProjectInfoModel.JavaCopyItem.getTagId(tag, copyId));
        if (bean == null) {
            return JsonMessage.getString(400, "未获取到对应信息");
        }
        //启用线程争用监视
        bean.setThreadContentionMonitoringEnabled(true);
        ThreadInfo[] threadInfos = bean.dumpAllThreads(false, false);
        if (threadInfos == null || threadInfos.length <= 0) {
            return JsonMessage.getString(404, "没有获取到任何线程");
        }

        JSONArray array = new JSONArray();
        int index = PageUtil.getStart(page, limit);
        int len = limit + index;
        int length = threadInfos.length;
        if (len > length) {
            len = length;
        }
        for (int i = index; i < len; i++) {
            ThreadInfo threadInfo = threadInfos[i];
            Thread.State threadState = threadInfo.getThreadState();
            JSONObject object = new JSONObject();
            object.put("id", threadInfo.getThreadId());
            object.put("name", threadInfo.getThreadName());
            object.put("status", threadState);
            object.put("waitedCount", threadInfo.getWaitedCount());
            object.put("waitedTime", threadInfo.getWaitedTime());
            object.put("blockedCount", threadInfo.getBlockedCount());
            object.put("blockedTime", threadInfo.getBlockedTime());
            object.put("isInNative", threadInfo.isInNative());
            object.put("isSuspended", threadInfo.isSuspended());
            array.add(object);
        }
        JSONObject object = new JSONObject();
        object.put("count", length);
        object.put("data", array);
        return JsonMessage.getString(200, "", object);
    }

    /**
     * 获取jvm内存
     *
     * @param tag tag
     * @return JSONObject
     */
    private JSONObject getBeanMem(String tag) {
        try {
            MemoryMXBean bean = JvmUtil.getMemoryMXBean(tag);
            if (bean == null) {
                return null;
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("mount", bean.getObjectPendingFinalizationCount());
            //堆内存
            MemoryUsage memory = bean.getHeapMemoryUsage();
            //非堆内存
            MemoryUsage nonHeapMemoryUsage = bean.getNonHeapMemoryUsage();
            long used = memory.getUsed();
            long max = memory.getMax();
            //未定义
            if (-1 == max) {
                max = memory.getCommitted();
            }
            //计算使用内存占最大内存的百分比
            double v = new BigDecimal(used).divide(new BigDecimal(max), 2, BigDecimal.ROUND_HALF_UP).doubleValue() * 100;
            jsonObject.put("heapUsed", FileUtil.readableFileSize(used));
            jsonObject.put("heapProportion", String.format("%.2f", v) + "%");
            jsonObject.put("heapCommitted", FileUtil.readableFileSize(memory.getCommitted()));
            long nonUsed = nonHeapMemoryUsage.getUsed();
            long nonMax = nonHeapMemoryUsage.getMax();
            long nonCommitted = nonHeapMemoryUsage.getCommitted();
            if (-1 == nonMax) {
                nonMax = nonCommitted;
            }
            jsonObject.put("nonHeapUsed", FileUtil.readableFileSize(nonUsed));
            double proportion = new BigDecimal(nonUsed).divide(new BigDecimal(nonMax), 2, BigDecimal.ROUND_HALF_UP).doubleValue() * 100;
            jsonObject.put("nonHeapProportion", String.format("%.2f", proportion) + "%");
            jsonObject.put("nonHeapCommitted", FileUtil.readableFileSize(nonCommitted));
            return jsonObject;
        } catch (Exception e) {
            DefaultSystemLog.getLog().error(e.getMessage(), e);
        }
        return null;
    }


    /**
     * 导出堆栈信息
     *
     * @param tag 程序运行标识
     * @return json
     */
    @RequestMapping(value = "internal_stack", method = RequestMethod.GET)
    @ResponseBody
    public String stack(String tag, String copyId) {
        tag = ProjectInfoModel.JavaCopyItem.getTagId(tag, copyId);
        //
        String fileName = AgentConfigBean.getInstance().getTempPathName() + "/" + tag + "_java_cpu.txt";
        fileName = FileUtil.normalize(fileName);
        try {
            int pid = AbstractProjectCommander.getInstance().getPid(tag);
            if (pid <= 0) {
                return JsonMessage.getString(400, "未运行");
            }
            String command = String.format("jstack -F %s >> %s ", pid, fileName);
            CommandUtil.execSystemCommand(command);
            downLoad(getResponse(), fileName);
        } catch (Exception e) {
            DefaultSystemLog.getLog().error(e.getMessage(), e);
//            getResponse().sendRedirect("internal?tag=" + tag);
        }
        return JsonMessage.getString(200, "");
    }

    /**
     * 导出内存信息
     *
     * @param tag 程序运行标识
     * @return json
     */
    @RequestMapping(value = "internal_ram", method = RequestMethod.GET)
    @ResponseBody
    public String ram(String tag, String copyId) {
        tag = ProjectInfoModel.JavaCopyItem.getTagId(tag, copyId);
        //
        String fileName = AgentConfigBean.getInstance().getTempPathName() + "/" + tag + "_java_ram.txt";
        fileName = FileUtil.normalize(fileName);
        try {
            int pid = AbstractProjectCommander.getInstance().getPid(tag);
            if (pid <= 0) {
                return JsonMessage.getString(400, "未运行");
            }
            String command = String.format("jmap -histo:live %s >> %s", pid, fileName);
            CommandUtil.execSystemCommand(command);
            downLoad(getResponse(), fileName);
        } catch (Exception e) {
            DefaultSystemLog.getLog().error(e.getMessage(), e);
//            getResponse().sendRedirect("internal?tag=" + tag);
        }
        return JsonMessage.getString(200, "");
    }

    /**
     * 下载文件
     *
     * @param response response
     * @param fileName 文件名字
     */
    private void downLoad(HttpServletResponse response, String fileName) {
        //获取项目根路径
        File file = new File(fileName);
        ServletUtil.write(response, file);
        FileUtil.del(file);
    }
}