package tui

import (
	"fmt"
	"get-container/cmd/hytop/backend"
	"maps"
	"slices"
	"strings"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
)

type ModelProcessInfo struct {
	Title            [2]string
	DoubleMiddleLine string
	MiddleLine       string
	BottomLine       string
	NoProceseLine    string // 没有进程时显示的行
	Cache            map[int][]backend.DCUProcessInfo
	width            int
	style            lipgloss.Style
	modMsg           *ModelMsg
	actionMsg        *ActionMsg
}

const (
	Processes             = " Processes:"
	ModelProcessInfoTitle = " DCU     PID     USER  DCU-MEM SDMA  %CPU  %MEM      TIME Docker               Command"
	ModelProcessInfoNoPro = "  No running processes found"
)

func NewModelProcessInfo(width int) *ModelProcessInfo {
	result := ModelProcessInfo{width: width, Cache: make(map[int][]backend.DCUProcessInfo), style: lipgloss.NewStyle()}
	return &result
}

func (m *ModelProcessInfo) Init() tea.Cmd {
	sb := strings.Builder{}
	sb.WriteString(myBorder.Left)
	sb.WriteString(Processes)

	uah := fmt.Sprintf("%s@%s ", m.style.Foreground(lipgloss.Color("#edff2cff")).SetString(backend.User).String(),
		m.style.Foreground(lipgloss.Color("#a3ff2bff")).SetString(backend.HostName).String())
	space := strings.Repeat(" ", m.width-lipgloss.Width(Processes)-lipgloss.Width(uah)-2)

	sb.WriteString(space)
	sb.WriteString(uah)
	sb.WriteString(myBorder.Right)
	sb.WriteByte('\n')
	sb.WriteString(myBorder.Left)
	sb.WriteString(ModelProcessInfoTitle)
	sb.WriteString(strings.Repeat(" ", m.width-2-lipgloss.Width(ModelProcessInfoTitle)))
	sb.WriteString(myBorder.Right)
	sb.WriteByte('\n')
	m.Title[0] = sb.String()
	sb.Reset()

	sb.WriteString(myBorder.Left)
	sb.WriteString(Processes)
	uah = fmt.Sprintf("%s@%s ", backend.User, backend.HostName)
	space = strings.Repeat(" ", m.width-lipgloss.Width(Processes)-lipgloss.Width(uah)-2)
	sb.WriteString(space)
	sb.WriteString(uah)
	sb.WriteString(myBorder.Right)
	sb.WriteByte('\n')
	sb.WriteString(myBorder.Left)
	sb.WriteString(ModelProcessInfoTitle)
	sb.WriteString(strings.Repeat(" ", m.width-2-lipgloss.Width(ModelProcessInfoTitle)))
	sb.WriteString(myBorder.Right)
	m.Title[1] = LowLeightStyle.Render(sb.String()) + "\n"
	m.DoubleMiddleLine = "╞" + strings.Repeat("═", m.width-2) + "╡\n"
	m.MiddleLine = myBorder.MiddleLeft + strings.Repeat("─", m.width-2) + myBorder.MiddleRight + "\n"
	m.BottomLine = myBorder.BottomLeft + strings.Repeat("═", m.width-2) + myBorder.BottomRight
	m.NoProceseLine = myBorder.Left + ModelProcessInfoNoPro + strings.Repeat(" ", m.width-2-lipgloss.Width(ModelProcessInfoNoPro)) + myBorder.Right + "\n"
	return nil
}

func (m *ModelProcessInfo) View() string {
	darkMode := false
	if m.actionMsg != nil && m.actionMsg.Action != nil && m.actionMsg.VM == VMMain {
		darkMode = true
	}
	haveProcess := false
	var heightLightPid int32 = 0
	if m.actionMsg != nil && m.actionMsg.PointPid != nil {
		heightLightPid = *m.actionMsg.PointPid
	}
	lines := make([]string, 0)
	sb := strings.Builder{}
	sbInner := strings.Builder{}

	keys := make([]int, 0, len(m.Cache))
	for k := range m.Cache {
		keys = append(keys, k)
	}
	slices.Sort(keys)
	for _, index := range keys {
		processes := m.Cache[index]
		for _, process := range processes {
			haveProcess = true
			sb.WriteString(myBorder.Left)

			sbInner.WriteString(FormatStr(fmt.Sprintf("%d", process.DCU), 4, lipgloss.Right))
			sbInner.WriteByte(' ')
			sbInner.WriteString(FormatStr(fmt.Sprintf("%d", process.Info.Pid), 7, lipgloss.Right))
			sbInner.WriteByte(' ')
			sbInner.WriteString(FormatStr(process.Info.User, 8, lipgloss.Right))
			sbInner.WriteByte(' ')
			sbInner.WriteString(FormatStr(process.DCUMem.HumanReadStr(1), 8, lipgloss.Right))
			sbInner.WriteByte(' ')
			sbInner.WriteString(FormatStr(fmt.Sprintf("%d", process.SDMA), 4, lipgloss.Right))
			sbInner.WriteByte(' ')
			sbInner.WriteString(FormatStr(fmt.Sprintf("%.1f", process.Info.CPU), 5, lipgloss.Right))
			sbInner.WriteByte(' ')
			sbInner.WriteString(FormatStr(fmt.Sprintf("%.1f", process.Info.Mem), 5, lipgloss.Right))
			sbInner.WriteByte(' ')
			sbInner.WriteString(FormatStr(process.Info.Time, 9, lipgloss.Right))
			sbInner.WriteByte(' ')
			if process.Info.ContInfo != nil {
				sbInner.WriteString(FormatStr(process.Info.ContInfo.Name, 20, lipgloss.Left))
			} else {
				sbInner.WriteString(FormatStr("  -  ", 20, lipgloss.Left))
			}
			sbInner.WriteByte(' ')
			w := m.width - lipgloss.Width(sbInner.String()) - 3
			tw := lipgloss.Width(process.Info.Cmd)
			if w >= tw {
				sbInner.WriteString(process.Info.Cmd)
				sbInner.WriteString(strings.Repeat(" ", w-tw))
			} else {
				sbInner.WriteString(FormatStr(process.Info.Cmd, w-2, lipgloss.Left))
				sbInner.WriteString("..")
			}
			sbInner.WriteByte(' ')
			selected := false
			if m.actionMsg != nil && m.actionMsg.SelectPids != nil {
				b, have := m.actionMsg.SelectPids[process.Info.Pid]
				if have && b {
					selected = true
				}
			}
			if darkMode {
				selected = false
			}
			if heightLightPid == process.Info.Pid && !darkMode {
				if selected {
					sb.WriteString(HeightSelectedStyle.Render(sbInner.String()))
				} else {
					sb.WriteString(HeightLightStyle.Render(sbInner.String()))
				}
			} else {
				if selected {
					sb.WriteString(SelectedStyle.Render(sbInner.String()))
				} else {
					sb.WriteString(sbInner.String())
				}
			}
			sbInner.Reset()
			sb.WriteString(myBorder.Right)
			sb.WriteByte('\n')
			lines = append(lines, sb.String())
			sb.Reset()
		}
		lines = append(lines, m.MiddleLine)
	}

	if darkMode {
		sb.WriteString(m.Title[1])
		sb.WriteString(m.DoubleMiddleLine)
		if !haveProcess {
			sb.WriteString(m.NoProceseLine)
		} else {
			for _, v := range lines[:len(lines)-1] {
				sb.WriteString(v)
			}
		}
		sb.WriteString(m.BottomLine)
		return LowLeightStyle.Render(sb.String())
	} else {
		sb.WriteString(m.Title[0])
		sb.WriteString(m.DoubleMiddleLine)
		if !haveProcess {
			sb.WriteString(m.NoProceseLine)
		} else {
			for _, v := range lines[:len(lines)-1] {
				sb.WriteString(v)
			}
		}
		sb.WriteString(m.BottomLine)
		return sb.String()
	}
}

func (m *ModelProcessInfo) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := inputMsg.(type) {
	case *ModelMsg:
		clear(m.Cache)
		maps.Copy(m.Cache, msg.DCUPidInfo)
		m.modMsg = msg
		return m, nil
	case *ActionMsg:
		m.actionMsg = msg
		return m, nil
	}
	return m, nil
}
