package tui

import (
	"fmt"
	"get-container/cmd/hytop/backend"
	"get-container/cmd/hytop/tchart"
	"get-container/utils"
	"image/color"
	"maps"
	"strings"
	"sync"
	"time"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
	"github.com/emirpasic/gods/v2/maps/linkedhashmap"
	"github.com/emirpasic/gods/v2/trees/binaryheap"
	"github.com/lucasb-eyer/go-colorful"
	"github.com/muesli/gamut"
)

const (
	SysLoadHeight = 5   // 固定图表高度
	SysLoadWidth  = 77  // 固定图表宽度，不包含左右的边框
	SysLoadCap    = 600 // 记录
)

// ModelSysLoad 系统负载组件
type ModelSysLoad struct {
	SysMem     *MyTimeChart
	SysCPU     *MyTimeChart
	DCU        *MyTimeChart
	DCUMem     *MyTimeChart
	Cache      map[int]backend.DCUInfo
	SysInfo    *linkedhashmap.Map[time.Time, SysLoadInfo]
	Keys       *binaryheap.Heap[time.Time]
	current    *SysLoadInfo
	topLine    string
	bottomLine string
	style      lipgloss.Style
	width      int // 组件总宽度
	colors     []color.Color
}

type SysLoadInfo struct {
	T                               time.Time
	DCUUsage                        map[int]float32
	DCUMemUsage                     map[int]float32
	Load1, Load5, Load15            float64
	MemTotal, SwapTotal             uint64
	MemUsed, SwapUsed               uint64
	MemUsedPercent, SwapUsedPercent float64
	CPUPercent                      float64
	DCUUsageAvg                     float32
	DCUMemUsageAvg                  float32
}

func NewModelSysLoad(width int) *ModelSysLoad {
	result := ModelSysLoad{}
	result.width = width
	result.Cache = make(map[int]backend.DCUInfo)
	result.SysInfo = linkedhashmap.New[time.Time, SysLoadInfo]()
	result.Keys = binaryheap.NewWith(func(a, b time.Time) int {
		if a.After(b) {
			return 1
		}
		if a.Before(b) {
			return -1
		}
		return 0
	})
	subLine := width - StaticWidth - 1
	result.SysMem = NewTimeChart(SysLoadWidth, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#20ff2cff")})
	result.SysCPU = NewTimeChart(SysLoadWidth, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#ff3030ff")})
	result.DCU = NewTimeChart(subLine, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#27ffdbff")})
	result.DCUMem = NewTimeChart(subLine, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#20b1ffff")})

	result.topLine = myBorder.MiddleLeft + genXAxis(SysLoadWidth) + myBorder.Middle + genXAxis(subLine) + myBorder.MiddleRight
	result.bottomLine = "╞" + strings.Repeat(myBorder.Bottom, SysLoadWidth) + "╧" + strings.Repeat(myBorder.Bottom, subLine) + "╡"
	result.style = lipgloss.NewStyle()
	result.colors = gamut.Blends(lipgloss.Color("#ff0000"), lipgloss.Color("#00ff00ff"), SysLoadHeight)
	return &result
}

func (m *ModelSysLoad) Init() tea.Cmd {
	result := make([]tea.Cmd, 0)
	if c := m.DCU.Init(); c != nil {
		result = append(result, c)
	}
	if c := m.DCUMem.Init(); c != nil {
		result = append(result, c)
	}
	if c := m.SysMem.Init(); c != nil {
		result = append(result, c)
	}
	if c := m.SysCPU.Init(); c != nil {
		result = append(result, c)
	}
	if len(result) > 0 {
		return tea.Batch(result...)
	}
	sysInfo, _ := utils.GetSysInfo()
	s := SysLoadInfo{}
	s.Load1 = sysInfo.LoadAverage1
	s.Load5 = sysInfo.LoadAverage5
	s.Load15 = sysInfo.LoadAverage15
	s.MemTotal = sysInfo.MemTotal
	s.MemUsed = sysInfo.MemUsage
	s.SwapTotal = sysInfo.SwapTotal
	s.SwapUsed = sysInfo.SwapUsage
	s.MemUsedPercent = sysInfo.MemUsagePercent
	s.SwapUsedPercent = sysInfo.SwapUsagePercent
	s.T = time.Now()
	s.CPUPercent = sysInfo.CPUPercent
	s.DCUUsageAvg = 0
	s.DCUMemUsageAvg = 0
	m.current = &s
	return nil
}

func (m *ModelSysLoad) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := inputMsg.(type) {
	case *ModelMsg:
		clear(m.Cache)
		maps.Copy(m.Cache, msg.DCUInfo)
		m.updateInfo(msg.t)
		return m, nil
	}
	return m, nil
}

func (m *ModelSysLoad) View() string {
	memUsed := utils.MemorySize{Num: m.current.MemUsed, Unit: utils.Byte}

	load := fmt.Sprintf(" Load Average: %.2f %.2f %.2f\n CPU: %.1f%%", m.current.Load1, m.current.Load5, m.current.Load15, m.current.CPUPercent)
	mem := fmt.Sprintf(" MEM: %s (%.1f%%)", memUsed.HumanReadStr(1), m.current.MemUsedPercent)
	dcuMem := fmt.Sprintf(" AVG DCU MEM: %.1f%%", m.current.DCUMemUsageAvg)
	dcu := fmt.Sprintf(" AVG DCU UTL: %.1f%%", m.current.DCUUsageAvg)

	load = StackPosition(load, m.SysCPU.View(), lipgloss.Top, lipgloss.Left)
	mem = StackPosition(mem, m.SysMem.View(), lipgloss.Bottom, lipgloss.Left)
	s := m.DCUMem.View()
	s = lipgloss.NewStyle().Foreground(lipgloss.Color("rgba(158, 143, 28, 0.86)")).Render(s)
	dcuMem = StackPosition(dcuMem, s, lipgloss.Top, lipgloss.Left)
	dcu = StackPosition(dcu, m.DCU.View(), lipgloss.Bottom, lipgloss.Left)

	load = m.style.Border(myBorder, false, true, false).Render(load)
	mem = m.style.Border(myBorder, false, true, false).Render(mem)
	dcuMem = m.style.Border(myBorder, false, true, false, false).Render(dcuMem)
	dcu = m.style.Border(myBorder, false, true, false, false).Render(dcu)

	up := lipgloss.JoinHorizontal(lipgloss.Top, load, dcuMem)
	down := lipgloss.JoinHorizontal(lipgloss.Top, mem, dcu)
	return lipgloss.JoinVertical(lipgloss.Left, up, m.topLine, down, m.bottomLine) + "\n"
}

// updateInfo
func (m *ModelSysLoad) updateInfo(t time.Time) {
	sysInfo, _ := utils.GetSysInfo()
	s := SysLoadInfo{}
	s.Load1 = sysInfo.LoadAverage1
	s.Load5 = sysInfo.LoadAverage5
	s.Load15 = sysInfo.LoadAverage15
	s.MemTotal = sysInfo.MemTotal
	s.MemUsed = sysInfo.MemUsage
	s.SwapTotal = sysInfo.SwapTotal
	s.SwapUsed = sysInfo.SwapUsage
	s.MemUsedPercent = sysInfo.MemUsagePercent
	s.SwapUsedPercent = sysInfo.SwapUsagePercent
	s.T = t
	s.CPUPercent = sysInfo.CPUPercent
	s.DCUUsage = make(map[int]float32)
	s.DCUMemUsage = make(map[int]float32)
	s.DCUMemUsageAvg, s.DCUUsageAvg = 0, 0
	for k, v := range m.Cache {
		s.DCUMemUsageAvg += v.MemUsedPerent
		s.DCUMemUsage[k] = v.MemUsedPerent
		s.DCUUsageAvg += v.DCUUTil
		s.DCUUsage[k] = v.DCUUTil
	}
	l := len(m.Cache)
	s.DCUMemUsageAvg /= float32(l)
	s.DCUUsageAvg /= float32(l)
	m.current = &s
	wg := sync.WaitGroup{}
	wg.Add(4)
	go func() {
		defer wg.Done()
		m1, _ := m.SysMem.Update(MyTimeChartMsg{Points: map[string][]tchart.TimePoint{"default": {{Time: t, Value: s.MemUsedPercent}}}})
		m.SysMem = m1.(*MyTimeChart)
	}()
	go func() {
		defer wg.Done()
		m2, _ := m.SysCPU.Update(MyTimeChartMsg{Points: map[string][]tchart.TimePoint{"default": {{Time: t, Value: s.CPUPercent}}}})
		m.SysCPU = m2.(*MyTimeChart)
	}()
	go func() {
		defer wg.Done()
		m3, _ := m.DCU.Update(MyTimeChartMsg{Points: map[string][]tchart.TimePoint{"default": {{Time: t, Value: float64(s.DCUUsageAvg)}}}})
		m.DCU = m3.(*MyTimeChart)
	}()
	go func() {
		defer wg.Done()
		m4, _ := m.DCUMem.Update(MyTimeChartMsg{Points: map[string][]tchart.TimePoint{"default": {{Time: t, Value: float64(s.DCUMemUsageAvg)}}}})
		m.DCUMem = m4.(*MyTimeChart)
	}()
	// 存放数据
	m.SysInfo.Put(t, s)
	m.Keys.Push(t)
	if m.Keys.Size() > SysLoadCap {
		delKey, have := m.Keys.Pop()
		if have {
			m.SysInfo.Remove(delKey)
		}
	}
	wg.Wait()
}

func (m *ModelSysLoad) RanderColor(str string) string {
	lines := strings.Split(strings.Trim(str, "\n"), "\n")
	result := ""
	for k, line := range lines {
		c, _ := colorful.MakeColor(m.colors[k])
		result += lipgloss.NewStyle().Foreground(lipgloss.Color(c.Hex())).Render(line) + "\n"
	}
	return result
}
