package runtime_metrics

import (
	"encoding/json"
	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/host"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/process"
	"netease_control_center/globals"
	"netease_control_center/utils"
	"runtime"
	"strconv"
	"strings"
)

var (
	pNames = []string{"netease_control_center", "netease_voice", "ihwplayer"}
)

type Appinfo struct {
	BuildVersion string `json:"appinfo.build_version"`
	Hv           string `json:"appinfo.hv"`
	Version      string `json:"appinfo.version"`
	GoVersion    string `json:"appinfo.go_version"`
}

func (this *Appinfo) Collect() {
	this.BuildVersion = globals.BuildVersion
	this.Hv = *globals.Hv
	this.Version = *globals.GitTag
	this.GoVersion = runtime.Version()
}

type MemInfo struct {
	All    uint64 `json:"meminfo.all"`
	InUse  uint64 `json:"meminfo.in_use"`
	Buffer uint64 `json:"meminfo.buffer"`
	Cache  uint64 `json:"meminfo.cache"`
	Share  uint64 `json:"meminfo.share"`
}

func (this *MemInfo) Collect() {
	if vm, err := mem.VirtualMemory(); err == nil {
		this.All = vm.Total
		this.InUse = vm.Used
		this.Buffer = vm.Buffers
		this.Cache = vm.Cached
		this.Share = vm.Shared
	}
}

type CpuInfo struct {
	Percent []float64 `json:"cpuinfo.percent"`
}

func (this *CpuInfo) Collect() {
	if per, err := cpu.Percent(0, true); err == nil && len(per) > 0 {
		this.Percent = per
	}
}

type ProcessInfo struct {
	Name       string  `json:"process.name"`
	Cpu        float64 `json:"process.cpu"`
	CreateTime int64   `json:"process.create_time"`
	MemRss     uint64  `json:"process.mem_rss"`
	MemShared  uint64  `json:"process.mem_shared"`
	FdNum      int32   `json:"process.fd_num"`
	ThreadNum  int32   `json:"process.thread_num"`
}

func CollectProcesses() (ret []*ProcessInfo) {
	if processes, err := process.Processes(); err == nil {
		for _, p := range processes {
			var pname string
			if pname, err = p.Name(); err != nil {
				continue
			}
			var cpupercent float64
			var mempercent float32

			cpupercent, _ = p.CPUPercent()
			mempercent, _ = p.MemoryPercent()

			//check name
			if strings.Contains(pname, "netease") == false && strings.Contains(pname, "ihw") == false && cpupercent < 5 && mempercent < 5 {
				if ret, err := utils.Contain(pname, pNames); ret == false || err != nil {
					continue
				}
			}

			tmpnode := new(ProcessInfo)

			tmpnode.Name = pname
			tmpnode.Cpu = cpupercent
			tmpnode.CreateTime, _ = p.CreateTime()
			if mem, memerr := p.MemoryInfoEx(); memerr == nil {
				tmpnode.MemRss = mem.RSS
				tmpnode.MemShared = mem.Shared
			}
			tmpnode.FdNum, _ = p.NumFDs()
			tmpnode.ThreadNum, _ = p.NumThreads()
			ret = append(ret, tmpnode)
		}
	}
	return
}

func CollectProcesses2() (ret map[string]interface{}) {
	ret = make(map[string]interface{})
	if processes, err := process.Processes(); err == nil {
		for _, p := range processes {
			var pname string
			if pname, err = p.Name(); err != nil {
				continue
			}
			var cpupercent float64
			var mempercent float32

			cpupercent, _ = p.CPUPercent()
			mempercent, _ = p.MemoryPercent()

			//check name
			if strings.Contains(pname, "netease") == false && strings.Contains(pname, "ihw") == false && cpupercent < 5 && mempercent < 5 {
				if ret, err := utils.Contain(pname, pNames); ret == false || err != nil {
					continue
				}
			}

			ret[pname+".cpu"] = cpupercent
			ret[pname+".fdno"] = p.Pid

			ret[pname+".fdnum"], _ = p.NumFDs()
			ret[pname+".threadnum"], _ = p.NumThreads()
			ret[pname+".createtime"], _ = p.CreateTime()

			if mem, memerr := p.MemoryInfoEx(); memerr == nil {
				ret[pname+".memrss"] = mem.RSS
				ret[pname+".memshare"] = mem.Shared
			}
		}
	}
	return
}

type NeteaseSystem struct {
	Appinfo
	CpuInfo
	MemInfo
	Processes []*ProcessInfo `json:"processes"`
}

type InfluxdbFormat struct {
	Name string `json:"name"`
	Tags struct {
		Appinfo
	} `json:"tags"`
	Process map[string]interface{} `json:"values"`
}

func GetNeteaseDeviceInfo() []byte {
	var cpu CpuInfo
	var mem MemInfo

	var ccstatus struct {
		CC InfluxdbFormat `json:"cc"`
	}

	ccstatus.CC.Name = globals.BTNAME
	if tmps := strings.Split(globals.BTNAME, "-"); len(tmps) >= 2 {
		ccstatus.CC.Name = tmps[1]
	} else {
		ccstatus.CC.Name = "cc"
	}
	cpu.Collect()
	mem.Collect()
	ccstatus.CC.Tags.Appinfo.Collect()
	ccstatus.CC.Process = CollectProcesses2()

	var totalcpu float64
	for k, v := range cpu.Percent {
		totalcpu += v
		ccstatus.CC.Process["cpu"+strconv.Itoa(k)+".percent"] = v
	}
	if len(cpu.Percent) > 0 {
		totalcpu = totalcpu / float64(len(cpu.Percent))
	}
	ccstatus.CC.Process["cpu.percent"] = totalcpu
	ccstatus.CC.Process["mem.all"] = mem.All
	ccstatus.CC.Process["mem.inuse"] = mem.InUse
	ccstatus.CC.Process["mem.buffer"] = mem.Buffer
	ccstatus.CC.Process["mem.cache"] = mem.Cache
	ccstatus.CC.Process["mem.share"] = mem.Share
	ccstatus.CC.Process["uptime"], _ = host.Uptime()
	if data, err := json.Marshal(ccstatus); err == nil {
		return data
	}

	return []byte{}
}
