package autotask

import (
	c "ciyon/zciyon"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

func funcindex() map[string]func(map[string]any) string {
	return map[string]func(map[string]any) string{
		"web\\admin\\autotask\\base::dayclean": dayclean,
		"web\\admin\\autotask\\base::srvstats": srvstats,
	}
}
func srvstats(systemrow map[string]any) string {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("srvstats panic:", r)
			c.Log.Error("AUTO", "srvstats panic:"+r.(string))
		}
	}()
	func_succ := c.CiyVars.Func_succ
	func_fail := c.CiyVars.Func_fail
	func_commit := c.CiyVars.Func_commit
	func_rollback := c.CiyVars.Func_rollback
	func_runms := 0
	if func_succ+func_fail > 0 {
		func_runms = c.CiyVars.Func_runms / (func_commit + func_rollback + 1)
	}
	c.CiyVars.Func_succ = 0
	c.CiyVars.Func_fail = 0
	c.CiyVars.Func_commit = 0
	c.CiyVars.Func_rollback = 0
	c.CiyVars.Func_runms = 0
	disk := c.Ciy_sys_getdisk("/")
	disk2 := c.Ciy_sys_getdisk("/data")
	disk_sysfree := disk["free"]
	disk_datafree := disk2["free"]
	starttime := c.Timems()
	fpath := c.CiyWebDir + "/ud/_" + c.Tostr(c.Tostamp()) + ".bin"
	fsize := 1024 * 1024 // 1MB
	fcontent := make([]byte, fsize)
	for i := 0; i < fsize; i++ {
		fcontent[i] = byte(i%70 + 30)
	}
	f, err := os.Create(fpath)
	if err != nil {
		c.Log.Error("FILE", "创建临时文件失败"+fpath+":"+err.Error())
	} else {
		f.Write(fcontent)
		f.Close()
		rcontent, err := os.ReadFile(fpath)
		if err != nil {
			c.Log.Error("FILE", "读取临时文件失败")
		} else {
			for i := 0; i < fsize; i++ {
				if fcontent[i] != rcontent[i] {
					c.Log.Error("FILE", "写入临时文件校验失败")
					break
				}
			}
			os.Remove(fpath)
		}
	}
	disk_ioms := c.Timems() - starttime

	output := c.RunCmd("ps", "aux", "-A")
	cmddb := []string{"mysql", "mysqld"}
	cmdweb := []string{"nginx", "php", "WorkerMan", "mosquitto", "gitea", "/zgo"}
	cpu := map[string]float64{"db": 0, "web": 0, "oth": 0}
	mem := map[string]float64{"db": 0, "web": 0, "oth": 0}

	for _, line := range output {
		if strings.Contains(line, "COMMAND") {
			continue
		}
		_nextspace(&line)
		_nextspace(&line)
		cpup := c.Tofloat(_nextspace(&line))
		memp := c.Tofloat(_nextspace(&line))
		_nextspace(&line) // 虚拟内存
		_nextspace(&line) // 物理内存 包括共享内存
		_nextspace(&line)
		_nextspace(&line)
		_nextspace(&line)
		_nextspace(&line)
		if _arrin(line, cmddb) {
			cpu["db"] += cpup
			mem["db"] += memp
		} else if _arrin(line, cmdweb) {
			cpu["web"] += cpup
			mem["web"] += memp
		} else {
			cpu["oth"] += cpup
			mem["oth"] += memp
		}
	}

	output = c.RunCmd("free", "-m")
	mem_free := 0
	matches := regexp.MustCompile(`Mem:\s+(\d+)`).FindStringSubmatch(output[1])
	if len(matches) > 1 {
		mem_free = c.Toint(c.Tofloat(matches[1]) * (100 - cpu["db"] - cpu["web"] - cpu["oth"]) * 1024 * 1024 / 100)
	}

	output = c.RunCmd("sh", "-c", "netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'")
	net_tcp := 0
	for _, line := range output {
		ls := strings.Split(line, " ")
		if ls[0] == "LISTEN" {
			continue
		}
		if len(ls) < 2 {
			continue
		}
		net_tcp += c.Toint(ls[1])
	}
	starttime = c.Timems()
	resp, err := http.Get("https://www.baidu.com")
	if err == nil {
		resp.Body.Close()
	}
	net_ioms := c.Timems() - starttime

	dbrows, err := c.CiyDB.Getraw("show global status")
	if err != nil {
		c.Log.Error("FILE", "执行show global status失败")
	}
	dbstat := map[string]float64{}
	for _, row := range dbrows {
		name := c.Tostr(row["Variable_name"])
		val := c.Tofloat(row["Value"])
		dbstat[name] = val
	}
	db_keyhit := dbstat["Handler_read_key"] / (dbstat["Com_select"] + 1) * 100
	db_dbhit := dbstat["Innodb_buffer_pool_reads"] / (dbstat["Innodb_buffer_pool_read_requests"] + 1) * 100
	db_tmptable := dbstat["Created_tmp_disk_tables"] / (dbstat["Created_tmp_tables"] + 1) * 100

	updata := map[string]any{}
	updata["func_succ"] = func_succ
	updata["func_fail"] = func_fail
	updata["func_commit"] = func_commit
	updata["func_rollback"] = func_rollback
	updata["func_runms"] = func_runms
	updata["disk_sysfree"] = disk_sysfree
	updata["disk_datafree"] = disk_datafree
	updata["disk_ioms"] = disk_ioms
	updata["cpu_free"] = 100 - cpu["db"] - cpu["web"] - cpu["oth"]
	updata["cpu_db"] = cpu["db"]
	updata["cpu_web"] = cpu["web"]
	updata["cpu_oth"] = cpu["oth"]
	updata["mem_free"] = mem_free
	updata["mem_db"] = mem["db"]
	updata["mem_web"] = mem["web"]
	updata["mem_oth"] = mem["oth"]
	updata["net_tcp"] = net_tcp
	updata["net_ioms"] = net_ioms
	updata["db_query"] = dbstat["Questions"]
	updata["db_commit"] = dbstat["Com_commit"]
	updata["db_rollback"] = dbstat["Com_rollback"]
	updata["db_keyhit"] = db_keyhit
	updata["db_dbhit"] = db_dbhit
	updata["db_tmptable"] = db_tmptable
	updata["db_lock"] = dbstat["Innodb_row_lock_current_waits"]
	updata["addtimes"] = c.Tostamp()
	csql := c.NewCiySQL("zc_stats")
	c.CiyDB.Insert(csql, updata)

	retmsg := "succ: " + c.Tostr(func_succ)
	if func_rollback > 0 {
		retmsg += ", rollback: " + c.Tostr(func_rollback)
	}
	return retmsg
}
func dayclean(systemrow map[string]any) string {
	maxAge := 3 * 24 * time.Hour
	cnt := 0
	errcnt := 0
	filepath.Walk(c.CiyWebDir+"/ud/tmp", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			_tasklog(systemrow, "出现错误:"+err.Error())
			return err
		}
		if info.IsDir() {
			return nil
		}
		if time.Since(info.ModTime()) > maxAge {
			err := os.Remove(path)
			cnt++
			if err != nil {
				errcnt++
				fmt.Printf("failed to delete file: %s\n", err)
			}
		}
		return nil
	})
	retmsg := "clean: " + c.Tostr(cnt)
	if errcnt > 0 {
		retmsg += ", err: " + c.Tostr(errcnt)
	}
	return retmsg
}

func _nextspace(line *string) string {
	ind := strings.Index(*line, " ")
	if ind == -1 {
		*line = ""
		return *line
	}
	va := (*line)[:ind]
	*line = (*line)[ind:]
	*line = strings.TrimSpace(*line)
	return va
}
func _arrin(cmd string, arr []string) bool {
	for _, v := range arr {
		if strings.Contains(cmd, v) {
			return true
		}
	}
	return false
}
