package processor

import (
	"encoding/json"
	"fmt"
	"sort"
	"strings"
	"sync"

	"be.ems/src/framework/logger"
	"be.ems/src/framework/utils/date"
	"be.ems/src/framework/utils/parse"
	"be.ems/src/framework/vo/result"
	"be.ems/src/modules/ws/model"
	"github.com/shirou/gopsutil/v3/process"
)

// GetProcessData 获取进程数据
func GetProcessData(requestID string, data any) ([]byte, error) {
	msgByte, _ := json.Marshal(data)
	var query model.PsProcessQuery
	err := json.Unmarshal(msgByte, &query)
	if err != nil {
		logger.Warnf("ws processor GetNetConnections err: %s", err.Error())
		return nil, fmt.Errorf("query data structure error")
	}

	var processes []*process.Process
	processes, err = process.Processes()
	if err != nil {
		return nil, err
	}

	var (
		dataArr     = []model.PsProcessData{}
		resultMutex sync.Mutex
		wg          sync.WaitGroup
		numWorkers  = 4
	)

	handleData := func(proc *process.Process) {
		procData := model.PsProcessData{
			PID: proc.Pid,
		}
		if query.Pid > 0 && query.Pid != proc.Pid {
			return
		}
		procName, err := proc.Name()
		if procName == "" || err != nil {
			return
		} else {
			procData.Name = procName
		}
		if query.Name != "" && !strings.Contains(procData.Name, query.Name) {
			return
		}
		if username, err := proc.Username(); err == nil {
			procData.Username = username
		}
		if query.Username != "" && !strings.Contains(procData.Username, query.Username) {
			return
		}

		procData.PPID, _ = proc.Ppid()
		statusArray, _ := proc.Status()
		if len(statusArray) > 0 {
			procData.Status = strings.Join(statusArray, ",")
		}
		createTime, procErr := proc.CreateTime()
		if procErr == nil {
			procData.StartTime = date.ParseDateToStr(createTime, date.YYYY_MM_DD_HH_MM_SS)
		}
		procData.NumThreads, _ = proc.NumThreads()
		procData.CpuValue, _ = proc.CPUPercent()
		procData.CpuPercent = fmt.Sprintf("%.2f", procData.CpuValue) + "%"
		menInfo, procErr := proc.MemoryInfo()
		if procErr == nil {
			procData.Rss = parse.Bit(float64(menInfo.RSS))
			procData.Data = parse.Bit(float64(menInfo.Data))
			procData.VMS = parse.Bit(float64(menInfo.VMS))
			procData.HWM = parse.Bit(float64(menInfo.HWM))
			procData.Stack = parse.Bit(float64(menInfo.Stack))
			procData.Locked = parse.Bit(float64(menInfo.Locked))
			procData.Swap = parse.Bit(float64(menInfo.Swap))

			procData.RssValue = menInfo.RSS
		} else {
			procData.Rss = "--"
			procData.Data = "--"
			procData.VMS = "--"
			procData.HWM = "--"
			procData.Stack = "--"
			procData.Locked = "--"
			procData.Swap = "--"

			procData.RssValue = 0
		}
		ioStat, procErr := proc.IOCounters()
		if procErr == nil {
			procData.DiskWrite = parse.Bit(float64(ioStat.WriteBytes))
			procData.DiskRead = parse.Bit(float64(ioStat.ReadBytes))
		} else {
			procData.DiskWrite = "--"
			procData.DiskRead = "--"
		}
		procData.CmdLine, _ = proc.Cmdline()
		procData.Envs, _ = proc.Environ()

		resultMutex.Lock()
		dataArr = append(dataArr, procData)
		resultMutex.Unlock()
	}

	chunkSize := (len(processes) + numWorkers - 1) / numWorkers
	for i := 0; i < numWorkers; i++ {
		wg.Add(1)
		start := i * chunkSize
		end := (i + 1) * chunkSize
		if end > len(processes) {
			end = len(processes)
		}

		go func(start, end int) {
			defer wg.Done()
			for j := start; j < end; j++ {
				handleData(processes[j])
			}
		}(start, end)
	}

	wg.Wait()

	sort.Slice(dataArr, func(i, j int) bool {
		return dataArr[i].PID < dataArr[j].PID
	})

	resultByte, err := json.Marshal(result.Ok(map[string]any{
		"requestId": requestID,
		"data":      dataArr,
	}))
	return resultByte, err
}
