package tui

import (
	"fmt"
	"get-container/cmd/dcutop/backend"
	"get-container/cmd/dcutop/tchart"
	"get-container/utils"
	"maps"
	"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"
)

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

// 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
	line    string
	style   lipgloss.Style
}

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.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
	})
	result.SysMem = New(SysLoadWidth, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#0400ffff")})
	result.SysCPU = New(SysLoadWidth, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#8400ffff")})
	result.DCU = New(width, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#00ff00ff")})
	result.DCUMem = New(width, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#eeff00ff")})
	result.line = genXAxis(SysLoadWidth) + myBorder.Middle + genXAxis(width)
	result.style = lipgloss.NewStyle()
	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 {
	load := fmt.Sprintf("Load Average: %.2f %.2f %.2f", m.current.Load1, m.current.Load5, m.current.Load15)
	mem := fmt.Sprintf("MEM: %s (%.1f%%)", "todo", 12.3)
	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)
	dcuMem = StackPosition(dcuMem, m.DCUMem.View(), 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.Render(mem)
	dcuMem = m.style.Border(myBorder, false, true, false, false).UnsetBorderRight().Render(dcuMem)
	dcu = m.style.Render(dcu)

	up := lipgloss.JoinHorizontal(lipgloss.Top, load, dcuMem)
	down := lipgloss.JoinHorizontal(lipgloss.Top, mem, dcu)
	return lipgloss.JoinVertical(lipgloss.Left, up, m.line, down)
}

// 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.DCUMemUsageAvg += 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)
	}()
	// todo
	wg.Wait()
}
