package status

import (
	"fmt"
	"github.com/shirou/gopsutil/v3/mem"
)

type MemoryMetric struct {
	TotalMemory     uint64
	AvailableMemory uint64
	UsedMemory      uint64
	UsedPercent     float64 `json:"usedPercent"`
	FreeMemory      uint64  `json:"free"`
}

type MemoryInfo struct {
	MemoryMetric
	Buffers        uint64 `json:"buffers"`
	Cached         uint64 `json:"cached"`
	WriteBack      uint64 `json:"writeBack"`
	Dirty          uint64 `json:"dirty"`
	WriteBackTmp   uint64 `json:"writeBackTmp"`
	Shared         uint64 `json:"shared"`
	Slab           uint64 `json:"slab"`
	Sreclaimable   uint64 `json:"sreclaimable"`
	Sunreclaim     uint64 `json:"sunreclaim"`
	PageTables     uint64 `json:"pageTables"`
	SwapCached     uint64 `json:"swapCached"`
	CommitLimit    uint64 `json:"commitLimit"`
	CommittedAS    uint64 `json:"committedAS"`
	HighTotal      uint64 `json:"highTotal"`
	HighFree       uint64 `json:"highFree"`
	LowTotal       uint64 `json:"lowTotal"`
	LowFree        uint64 `json:"lowFree"`
	SwapTotal      uint64 `json:"swapTotal"`
	SwapFree       uint64 `json:"swapFree"`
	Mapped         uint64 `json:"mapped"`
	VmallocTotal   uint64 `json:"vmallocTotal"`
	VmallocUsed    uint64 `json:"vmallocUsed"`
	VmallocChunk   uint64 `json:"vmallocChunk"`
	HugePagesTotal uint64 `json:"hugePagesTotal"`
	HugePagesFree  uint64 `json:"hugePagesFree"`
	HugePagesRsvd  uint64 `json:"hugePagesRsvd"`
	HugePagesSurp  uint64 `json:"hugePagesSurp"`
	HugePageSize   uint64 `json:"hugePageSize"`
}

func GetMemoryMetric() (*MemoryMetric, error) {
	virtualMemoryStat, err := mem.VirtualMemory()
	if err != nil {
		return nil, err
	}

	return &MemoryMetric{
		TotalMemory:     virtualMemoryStat.Total,
		AvailableMemory: virtualMemoryStat.Available,
		UsedMemory:      virtualMemoryStat.Used,
		UsedPercent:     virtualMemoryStat.UsedPercent,
		FreeMemory:      virtualMemoryStat.Free,
	}, nil

}

func GetMemoryInfo() (*MemoryInfo, error) {

	virtualMemoryStat, err := mem.VirtualMemory()
	if err != nil {
		return nil, err
	}

	return &MemoryInfo{
		MemoryMetric: MemoryMetric{
			TotalMemory:     virtualMemoryStat.Total,
			AvailableMemory: virtualMemoryStat.Available,
			UsedMemory:      virtualMemoryStat.Used,
			UsedPercent:     virtualMemoryStat.UsedPercent,
			FreeMemory:      virtualMemoryStat.Free,
		},
		Buffers:        virtualMemoryStat.Buffers,
		Cached:         virtualMemoryStat.Cached,
		WriteBack:      virtualMemoryStat.WriteBack,
		Dirty:          virtualMemoryStat.Dirty,
		WriteBackTmp:   virtualMemoryStat.WriteBackTmp,
		Shared:         virtualMemoryStat.Shared,
		Slab:           virtualMemoryStat.Slab,
		Sreclaimable:   virtualMemoryStat.Sreclaimable,
		Sunreclaim:     virtualMemoryStat.Sunreclaim,
		PageTables:     virtualMemoryStat.PageTables,
		SwapCached:     virtualMemoryStat.SwapCached,
		CommitLimit:    virtualMemoryStat.CommitLimit,
		CommittedAS:    virtualMemoryStat.CommittedAS,
		HighTotal:      virtualMemoryStat.HighTotal,
		HighFree:       virtualMemoryStat.HighFree,
		LowTotal:       virtualMemoryStat.LowTotal,
		LowFree:        virtualMemoryStat.LowFree,
		SwapTotal:      virtualMemoryStat.SwapTotal,
		SwapFree:       virtualMemoryStat.SwapFree,
		Mapped:         virtualMemoryStat.Mapped,
		VmallocTotal:   virtualMemoryStat.VmallocTotal,
		VmallocUsed:    virtualMemoryStat.VmallocUsed,
		VmallocChunk:   virtualMemoryStat.VmallocChunk,
		HugePagesTotal: virtualMemoryStat.HugePagesTotal,
		HugePagesFree:  virtualMemoryStat.HugePagesFree,
		HugePagesRsvd:  virtualMemoryStat.HugePagesRsvd,
		HugePagesSurp:  virtualMemoryStat.HugePagesSurp,
		HugePageSize:   virtualMemoryStat.HugePageSize,
	}, nil
}

func FormatMemorySize(size uint64) string {
	const unit = 1024
	if size < unit {
		return fmt.Sprintf("%d B", size)
	}
	div, exp := int64(unit), 0
	for n := size / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %cB", float64(size)/float64(div), "KMGTPE"[exp])
}
