package service

import (
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"
	"time"

	"github.com/1Panel-dev/1Panel/backend/app/dto"
	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/host"
	"github.com/shirou/gopsutil/load"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/net"
)

type DashboardService struct{}

type IDashboardService interface {
	LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
	LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
}

func NewIDashboardService() IDashboardService {
	return &DashboardService{}
}
func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error) {
	var baseInfo dto.DashboardBase
	hostInfo, err := host.Info()
	if err != nil {
		return nil, err
	}
	baseInfo.Hostname = hostInfo.Hostname
	baseInfo.OS = hostInfo.OS
	baseInfo.Platform = hostInfo.Platform
	baseInfo.PlatformFamily = hostInfo.PlatformFamily
	baseInfo.PlatformVersion = hostInfo.PlatformVersion
	baseInfo.KernelArch = hostInfo.KernelArch
	baseInfo.KernelVersion = hostInfo.KernelVersion
	ss, _ := json.Marshal(hostInfo)
	baseInfo.VirtualizationSystem = string(ss)

	cmd := exec.Command("uptime", "-s")
	stdout, err := cmd.CombinedOutput()
	if err == nil {
		baseInfo.Uptime = string(stdout)
		uptime, err := time.Parse("2006-01-02 15:04:05", strings.ReplaceAll(string(stdout), "\n", ""))
		if err == nil {
			hours := int(time.Since(uptime).Hours())
			minutes := int(time.Since(uptime).Minutes())
			baseInfo.TimeSinceUptime = fmt.Sprintf("%ddays %dhours %dmimutes", hours/24, hours%24, minutes-hours*60)
		}
	}

	apps, err := appRepo.GetBy()
	if err != nil {
		return nil, err
	}
	for _, app := range apps {
		switch app.Key {
		case "dateease":
			baseInfo.DateeaseID = app.ID
		case "halo":
			baseInfo.HaloID = app.ID
		case "metersphere":
			baseInfo.MeterSphereID = app.ID
		case "jumpserver":
			baseInfo.JumpServerID = app.ID
		case "kubeoperator":
			baseInfo.KubeoperatorID = app.ID
		case "kubepi":
			baseInfo.KubepiID = app.ID
		}
	}

	appInstall, err := appInstallRepo.GetBy()
	if err != nil {
		return nil, err
	}
	baseInfo.AppInstalldNumber = len(appInstall)
	dbs, err := mysqlRepo.List()
	if err != nil {
		return nil, err
	}
	baseInfo.DatabaseNumber = len(dbs)
	website, err := websiteRepo.GetBy()
	if err != nil {
		return nil, err
	}
	baseInfo.WebsiteNumber = len(website)
	cornjobs, err := cronjobRepo.List()
	if err != nil {
		return nil, err
	}
	baseInfo.CronjobNumber = len(cornjobs)

	cpuInfo, err := cpu.Info()
	if err != nil {
		return nil, err
	}
	baseInfo.CPUModelName = cpuInfo[0].ModelName
	baseInfo.CPUCores, _ = cpu.Counts(false)
	baseInfo.CPULogicalCores, _ = cpu.Counts(true)

	baseInfo.CurrentInfo = *u.LoadCurrentInfo(ioOption, netOption)
	return &baseInfo, nil
}

func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent {
	var currentInfo dto.DashboardCurrent
	hostInfo, _ := host.Info()
	currentInfo.Procs = hostInfo.Procs

	currentInfo.CPUTotal, _ = cpu.Counts(true)
	totalPercent, _ := cpu.Percent(0, false)
	if len(totalPercent) == 1 {
		currentInfo.CPUUsedPercent = totalPercent[0]
		currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
	}
	currentInfo.CPUPercent, _ = cpu.Percent(0, true)

	loadInfo, _ := load.Avg()
	currentInfo.Load1 = loadInfo.Load1
	currentInfo.Load5 = loadInfo.Load5
	currentInfo.Load15 = loadInfo.Load15
	currentInfo.LoadUsagePercent = loadInfo.Load1 / (float64(currentInfo.CPUTotal*2) * 0.75) * 100

	memoryInfo, _ := mem.VirtualMemory()
	currentInfo.MemoryTotal = memoryInfo.Total
	currentInfo.MemoryAvailable = memoryInfo.Available
	currentInfo.MemoryUsed = memoryInfo.Used
	currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent

	state, _ := disk.Usage("/")
	currentInfo.Total = state.Total
	currentInfo.Free = state.Free
	currentInfo.Used = state.Used
	currentInfo.UsedPercent = state.UsedPercent
	currentInfo.InodesTotal = state.InodesTotal
	currentInfo.InodesUsed = state.InodesUsed
	currentInfo.InodesFree = state.InodesFree
	currentInfo.InodesUsedPercent = state.InodesUsedPercent

	if ioOption == "all" {
		diskInfo, _ := disk.IOCounters()
		for _, state := range diskInfo {
			currentInfo.IOReadBytes += state.ReadBytes
			currentInfo.IOWriteBytes += state.WriteBytes
			currentInfo.IOCount += (state.ReadCount + state.WriteCount)
			currentInfo.IOTime += state.ReadTime / 1000 / 1000
			if state.WriteTime > state.ReadTime {
				currentInfo.IOTime += state.WriteTime / 1000 / 1000
			}
		}
	} else {
		diskInfo, _ := disk.IOCounters(ioOption)
		for _, state := range diskInfo {
			currentInfo.IOReadBytes += state.ReadBytes
			currentInfo.IOWriteBytes += state.WriteBytes
			currentInfo.IOTime += state.ReadTime / 1000 / 1000
			if state.WriteTime > state.ReadTime {
				currentInfo.IOTime += state.WriteTime / 1000 / 1000
			}
		}
	}

	if netOption == "all" {
		netInfo, _ := net.IOCounters(false)
		if len(netInfo) != 0 {
			currentInfo.NetBytesSent = netInfo[0].BytesSent
			currentInfo.NetBytesRecv = netInfo[0].BytesRecv
		}
	} else {
		netInfo, _ := net.IOCounters(true)
		for _, state := range netInfo {
			if state.Name == netOption {
				currentInfo.NetBytesSent = state.BytesSent
				currentInfo.NetBytesRecv = state.BytesRecv
			}
		}
	}

	currentInfo.ShotTime = time.Now()
	return &currentInfo
}