package tui

import (
	"fmt"
	"get-container/cmd/hytop/backend"
	"get-container/cmd/hytop/tchart"
	"get-container/utils"
	"math"
	"strings"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
	linkedlist "github.com/emirpasic/gods/v2/lists/doublylinkedlist"
)

const (
	MPDHeader  = " DCU PID     USER     DCU-MEM   %CPU  %MEM  TIME     Docker              Command"
	MPDProcess = "This process has finished running."
)

type ModelProcessDetail struct {
	CPU, Mem, DCU, DCUMem *MyTimeChart
	Pid                   int32
	dcu                   int
	Header, Cmd, docker   string
	width, height         int        // 界面的尺寸
	modelMsg              *ModelMsg  //
	maxVal                [4]float64 // 分别代表CPU、Mem、DCU、DCUMem的最大值
	maxValThreshold       [4]float64
	cpuPoints             *linkedlist.List[tchart.TimePoint]
	cpuPointsNum          int
	// axeYStr               [4]string        // 纵坐标字符串
	h, w                  [2]int           // h[0]代表上边两个表的高度、h[1]代表下边两个表的高度，w[0]表示左边两个表的宽度，w[1]表示右边两个表的宽度
	lines                 [5]string        // 5条横线
	DCUMemTotal, MemTotal utils.MemorySize // 内存总量
	MemMax                utils.MemorySize // 内存使用最大值

	DCUMemUsed                utils.MemorySize
	CPUPercent, MemPercent    float64
	DCUPercent, DCUMemPercent float64
	t                         string

	color1, color2 []lipgloss.Color // 分别给上方和下方两个图表的渐变颜色
}

func NewModelProcessDetail(width, height int, pid int32) *ModelProcessDetail {
	result := ModelProcessDetail{}
	if (width-3)%2 == 0 {
		result.w[0] = (width - 3) / 2
		result.w[1] = result.w[0]
	} else {
		result.w[0] = (width - 3) / 2
		result.w[1] = result.w[0] + 1
	}
	if (height-8)%2 == 0 {
		result.h[0] = (height - 8) / 2
		result.h[1] = result.h[0]
	} else {
		result.h[0] = (height - 8) / 2
		result.h[1] = result.h[0] + 1
	}

	var lines [5]string
	lines[0] = myBorder.TopLeft + strings.Repeat(myBorder.Top, width-2) + myBorder.TopRight
	lines[1] = "╞" + strings.Repeat("═", width-2) + "╡"
	lines[2] = "╞" + strings.Repeat("═", result.w[0]) + "╤" + strings.Repeat("═", result.w[1]) + "╡"
	lines[3] = myBorder.MiddleLeft + genXAxis(result.w[0]) + myBorder.Middle + genXAxis(result.w[1]) + myBorder.MiddleRight
	lines[4] = myBorder.BottomLeft + strings.Repeat("═", result.w[0]) + "╧" + strings.Repeat("═", result.w[1]) + myBorder.BottomRight
	result.lines = lines
	result.Pid = pid
	result.cpuPoints = linkedlist.New[tchart.TimePoint]()
	result.height = height
	result.width = width
	style := lipgloss.NewStyle()
	uah := fmt.Sprintf("%s@%s ", style.Foreground(lipgloss.Color("#edff2cff")).Render(backend.User), style.Foreground(lipgloss.Color("#a3ff2bff")).Render(backend.HostName))
	sb := strings.Builder{}
	sb.WriteString(result.lines[0])
	sb.WriteByte('\n')
	sb.WriteString(myBorder.Left)
	sb.WriteString(" Process:")
	sb.WriteString(strings.Repeat(" ", width-2-lipgloss.Width(uah)-lipgloss.Width(" Process:")))
	sb.WriteString(uah)
	sb.WriteString(myBorder.Left)
	sb.WriteByte('\n')
	sb.WriteString(myBorder.Left)
	sb.WriteString(MPDHeader)
	sb.WriteString(strings.Repeat(" ", width-2-lipgloss.Width(MPDHeader)))
	sb.WriteString(myBorder.Left)
	sb.WriteByte('\n')
	sb.WriteString(result.lines[1])
	sb.WriteByte('\n')
	result.Header = sb.String()

	result.color1 = GenGradientColor(lipgloss.Color("#ff0000"), lipgloss.Color("#00ff00ff"), result.h[0])
	result.color2 = GenGradientColor(lipgloss.Color("#ff0000"), lipgloss.Color("#00ff00ff"), result.h[1])

	result.CPU = NewTimeChart(result.w[0], result.h[0], 0, 100, []lipgloss.Color{lipgloss.Color("#00ff00ff")})
	result.Mem = NewTimeChart(result.w[0], result.h[1], 0, 100, result.color2)
	result.DCU = NewTimeChart(result.w[1], result.h[1], 0, 100, result.color2)
	result.DCUMem = NewTimeChart(result.w[1], result.h[0], 0, 100, result.color1)
	result.cpuPointsNum = result.w[0]*2 + 1

	for k := range result.maxVal {
		result.maxVal[k] = 0
	}
	for k := range result.maxValThreshold {
		result.maxValThreshold[k] = 100
	}
	return &result
}

func (m *ModelProcessDetail) Init() tea.Cmd {
	return nil
}

func (m *ModelProcessDetail) Update(imsg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := imsg.(type) {
	case *ModelMsg:
		m.modelMsg = msg
		m.handleModelMsg(msg)
	}
	return m, nil
}

func (m *ModelProcessDetail) handleModelMsg(msg *ModelMsg) {
	find := false
	var process *backend.DCUProcessInfo
	for dcu, pids := range msg.DCUPidInfo {
		for _, v := range pids {
			if v.Info.Pid == m.Pid {
				find = true
				process = &v
				if m.Cmd == "" {
					m.Cmd = v.Info.Cmd
					m.dcu = dcu
				}
				if v.Info.ContInfo != nil {
					m.docker = v.Info.ContInfo.Name
				}
				break
			}
		}
		if find {
			break
		}
	}
	var cpu, mem, dcu, dcumem float64
	if process == nil {
		m.Cmd = MPDProcess
		m.CPUPercent = 0
		m.MemPercent = 0
		cpu = 0
		mem = 0
	} else {
		cpu = process.Info.CPU
		mem = float64(process.Info.Mem)
		m.CPUPercent = cpu
		m.MemPercent = mem
		m.t = process.Info.Time
	}
	qinfo, lock := msg.DCUInfo.GetQuitInfo()
	dcuInfo, have := qinfo[m.dcu]
	if !have {
		dcu = 0
		dcumem = 0
		m.DCUPercent = 0
		m.DCUMemPercent = 0
	} else {
		dcumem = float64(dcuInfo.MemUsedPerent.OrElse(0))
		dcu = float64(dcuInfo.DCUUTil.OrElse(0))
		m.DCUPercent = dcu
		m.DCUMemPercent = dcumem
	}
	m.DCUMemTotal = utils.MemorySize{Unit: utils.Byte, Num: dcuInfo.MemTotal.OrElse(0)}
	m.DCUMemUsed = utils.MemorySize{Unit: utils.Byte, Num: dcuInfo.MemUsed.OrElse(0)}
	lock.Unlock()
	m.MemTotal = utils.MemorySize{Unit: utils.Byte, Num: msg.systemInfo.MemTotal}

	m.maxVal[0] = max(m.maxVal[0], cpu)
	m.maxVal[1] = max(m.maxVal[1], mem)
	m.maxVal[2] = max(m.maxVal[2], dcu)
	m.maxVal[3] = max(m.maxVal[3], dcumem)

	m.DCU.Update(MyTimeChartMsg{Reset: false, Points: []tchart.TimePoint{{Time: msg.t, Value: dcu}}})
	m.DCUMem.Update(MyTimeChartMsg{Reset: false, Points: []tchart.TimePoint{{Time: msg.t, Value: dcumem}}})
	m.Mem.Update(MyTimeChartMsg{Reset: false, Points: []tchart.TimePoint{{Time: msg.t, Value: mem}}})

	m.cpuPoints.Add(tchart.TimePoint{Time: msg.t, Value: cpu})
	if m.cpuPoints.Size() > m.cpuPointsNum {
		m.cpuPoints.Remove(0)
	}
	if m.CPU.max < cpu {
		// 需要更新
		if int(math.Ceil(cpu))%50 == 0 {
			m.maxValThreshold[0] = math.Ceil(cpu)
		} else {
			m.maxValThreshold[0] = float64(int(math.Ceil(cpu))/50)*50 + 50
		}
		m.CPU = NewTimeChart(m.w[0], m.h[0], 0, m.maxValThreshold[0], []lipgloss.Color{lipgloss.Color("#00ff00ff")})
		m.CPU.Update(MyTimeChartMsg{Reset: false, Points: m.cpuPoints.Values()})
	} else {
		m.CPU.Update(MyTimeChartMsg{Reset: false, Points: []tchart.TimePoint{{Time: msg.t, Value: cpu}}})
	}
}

func (m *ModelProcessDetail) View() string {
	sb := strings.Builder{}
	sb.WriteString(m.Header)
	sb.WriteString(myBorder.Left)
	sb.WriteString(FormatStr(fmt.Sprintf("%d", m.dcu), 4, lipgloss.Right))
	sb.WriteByte(' ')
	sb.WriteString(FormatStr(fmt.Sprintf("%d", m.Pid), 7, lipgloss.Left))
	sb.WriteByte(' ')
	sb.WriteString(FormatStr(backend.User, 8, lipgloss.Left))
	sb.WriteByte(' ')
	sb.WriteString(FormatStr(m.DCUMemUsed.HumanReadStr(1), 9, lipgloss.Left))
	sb.WriteByte(' ')
	sb.WriteString(FormatStr(fmt.Sprintf("%.1f", m.CPUPercent), 5, lipgloss.Left))
	sb.WriteByte(' ')
	sb.WriteString(FormatStr(fmt.Sprintf("%.1f", m.MemPercent), 5, lipgloss.Left))
	sb.WriteByte(' ')
	sb.WriteString(FormatStr(m.t, 8, lipgloss.Left))
	sb.WriteByte(' ')
	if m.docker == "" {
		sb.WriteString(FormatStr(" - ", 19, lipgloss.Left))
	} else {
		sb.WriteString(FormatStr(m.docker, 19, lipgloss.Left))
	}
	sb.WriteByte(' ')
	sb.WriteString(FormatStr(m.Cmd, m.width-3-lipgloss.Width(MPDHeader)+lipgloss.Width("Command"), lipgloss.Left))
	sb.WriteByte(' ')
	sb.WriteString(myBorder.Left)
	sb.WriteByte('\n')
	sb.WriteString(m.lines[2])
	sb.WriteByte('\n')
	style := lipgloss.NewStyle()

	cpuTitle := fmt.Sprintf(" Max CPU: %.1f%%\n     CPU: %.1f%%", m.maxVal[0], m.CPUPercent)
	memUsed := utils.MemorySize{Unit: utils.Byte, Num: (uint64(m.MemPercent*10000) * m.MemTotal.Num) / 1000000}
	memMax := utils.MemorySize{Unit: utils.Byte, Num: (uint64(m.maxVal[1]*10000) * m.MemTotal.Num) / 1000000}
	memTitle := fmt.Sprintf("     Host-Mem: %s (%.1f%%)\n Max Host-Mem: %s (%.1f%%) / %s", memUsed.HumanReadStr(1), m.MemPercent, memMax.HumanReadStr(1), m.maxVal[1], m.MemTotal.HumanReadStr(1))
	dcuTitle := fmt.Sprintf("     DCU: %.1f%%\n Max DCU: %.1f%%", m.DCUPercent, m.maxVal[2])

	dcuMemMax := utils.MemorySize{Unit: utils.Byte, Num: (uint64(m.maxVal[3]*100) * m.DCUMemTotal.Num / 10000)}

	dcuMemTitle := fmt.Sprintf(" Max DCU-Mem: %s (%.1f%%) / %s\n     DCU-Mem: %s (%.1f%%)", dcuMemMax.HumanReadStr(1), m.maxVal[3], m.DCUMemTotal.HumanReadStr(1), m.DCUMemUsed.HumanReadStr(1), m.DCUMemPercent)

	CPU := style.Border(lipgloss.NormalBorder(), false, true, false).Render(StackPosition(cpuTitle, m.CPU.View(), lipgloss.Top, lipgloss.Left))

	CPU = StackPosition(fmt.Sprintf("├%s", LowLeightStyle.Render(fmt.Sprintf("%d%%", int(m.maxValThreshold[0]/2)))), CPU, lipgloss.Center, lipgloss.Left)
	DCUM := style.Border(lipgloss.NormalBorder(), false, true, false, false).Render(StackPosition(dcuMemTitle, m.DCUMem.View(), lipgloss.Top, lipgloss.Left))
	Mem := style.Border(lipgloss.NormalBorder(), false, true, false).Render(StackPosition(memTitle, m.Mem.View(), lipgloss.Bottom, lipgloss.Left))
	DCU := style.Border(lipgloss.NormalBorder(), false, true, false, false).Render(StackPosition(dcuTitle, m.DCU.View(), lipgloss.Bottom, lipgloss.Left))
	sb.WriteString(lipgloss.JoinVertical(lipgloss.Left, lipgloss.JoinHorizontal(lipgloss.Top, CPU, DCUM), m.lines[3], lipgloss.JoinHorizontal(lipgloss.Top, Mem, DCU), m.lines[4]))
	return sb.String()
}
