package tui

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

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

const (
	ModelPsTreeHeader = "    PID USER      DEVICE %CPU  %MEM  TIME      COMMAND"
)

var (
	ModelPsTreeStyle     = lipgloss.NewStyle().Foreground(lipgloss.Color("#000000")).Background(lipgloss.Color("#00fffb7a"))
	ModelPsTreeStyleKey  = lipgloss.NewStyle().Foreground(lipgloss.Color("#842cffff")).Background(lipgloss.Color("#00fffb7a")).Italic(true)
	ModelPsTreeStyleInfo = lipgloss.NewStyle().Foreground(lipgloss.Color("#e82e2eff")).Background(lipgloss.Color("#00fffb7a"))

	ModelPsTreeStyleL     = lipgloss.NewStyle().Foreground(lipgloss.Color("#656565b0")).Background(lipgloss.Color("#00fffb7a"))
	ModelPsTreeStyleKeyL  = lipgloss.NewStyle().Foreground(lipgloss.Color("#6a32b992")).Background(lipgloss.Color("#00fffb7a")).Italic(true)
	ModelPsTreeStyleInfoL = lipgloss.NewStyle().Foreground(lipgloss.Color("#e82e2e80")).Background(lipgloss.Color("#00fffb7a"))

	ModelPsFullHeightStyle = lipgloss.NewStyle()
)

type ModelPsTree struct {
	width, height int
	modelMsg      *ModelMsg
	actionMsg     *ActionMsg
	header        [2]string
	mapPidToDCU   map[int32]int // key为进程ID，value为dcu index

	treeWidth int // 树形图的最大宽度
}

func NewModelPsTree(width, height int) *ModelPsTree {
	return &ModelPsTree{
		width:       width,
		height:      height,
		mapPidToDCU: make(map[int32]int),
	}
}

func (m *ModelPsTree) Init() tea.Cmd {
	// 生成标题字符串
	sb := strings.Builder{}

	h1 := ModelPsTreeStyle.SetString(ModelPsTreeHeader).String()
	sb.WriteString(ModelPsTreeStyle.SetString("(Press ").String())
	sb.WriteString(ModelPsTreeStyleKey.SetString("^C").String())
	sb.WriteString(ModelPsTreeStyle.SetString("(").String())
	sb.WriteString(ModelPsTreeStyleInfo.SetString("INT").String())
	sb.WriteString(ModelPsTreeStyle.SetString(")/").String())
	sb.WriteString(ModelPsTreeStyleKey.SetString("T").String())
	sb.WriteString(ModelPsTreeStyle.SetString("(").String())
	sb.WriteString(ModelPsTreeStyleInfo.SetString("TERM").String())
	sb.WriteString(ModelPsTreeStyle.SetString(")/").String())
	sb.WriteString(ModelPsTreeStyleKey.SetString("K").String())
	sb.WriteString(ModelPsTreeStyle.SetString("(").String())
	sb.WriteString(ModelPsTreeStyleInfo.SetString("KILL").String())
	sb.WriteString(ModelPsTreeStyle.SetString(") to send signals)").String())
	h2 := sb.String()
	sb.Reset()
	w := m.width - lipgloss.Width(h1) - lipgloss.Width(h2)
	h := ModelPsTreeStyle.SetString(strings.Repeat(" ", max(0, w))).String()
	m.header[0] = h1 + h + h2

	h1 = ModelPsTreeStyleL.SetString(ModelPsTreeHeader).String()
	sb.WriteString(ModelPsTreeStyleL.SetString("(Press ").String())
	sb.WriteString(ModelPsTreeStyleKeyL.SetString("^C").String())
	sb.WriteString(ModelPsTreeStyleL.SetString("(").String())
	sb.WriteString(ModelPsTreeStyleInfoL.SetString("INT").String())
	sb.WriteString(ModelPsTreeStyleL.SetString(")/").String())
	sb.WriteString(ModelPsTreeStyleKeyL.SetString("T").String())
	sb.WriteString(ModelPsTreeStyleL.SetString("(").String())
	sb.WriteString(ModelPsTreeStyleInfoL.SetString("TERM").String())
	sb.WriteString(ModelPsTreeStyleL.SetString(")/").String())
	sb.WriteString(ModelPsTreeStyleKeyL.SetString("K").String())
	sb.WriteString(ModelPsTreeStyleL.SetString("(").String())
	sb.WriteString(ModelPsTreeStyleInfoL.SetString("KILL").String())
	sb.WriteString(ModelPsTreeStyleL.SetString(") to send signals)").String())
	h2 = sb.String()
	h = ModelPsTreeStyleL.SetString(strings.Repeat(" ", w)).String()
	m.header[1] = h1 + h + h2

	m.treeWidth = m.width - lipgloss.Width(ModelPsTreeHeader) + lipgloss.Width("COMMAND")

	ModelPsFullHeightStyle = ModelPsFullHeightStyle.Height(m.height).Width(m.width).MaxHeight(m.height).MaxWidth(m.width).Align(lipgloss.Center, lipgloss.Top)
	return nil
}

func (m *ModelPsTree) View() string {
	pids := make([]int32, 0, len(m.mapPidToDCU))
	for k := range m.mapPidToDCU {
		pids = append(pids, k)
	}
	slices.Sort(pids)
	pinfo, root := utils.GetPsTree(pids)
	if len(pinfo) > 0 && root != nil {
		darkMode := false
		if m.actionMsg.Action != nil && m.actionMsg.VM == VMTree {
			darkMode = true
		}
		tr := tree.New()
		tr.Root(root.Cmd)
		pidSort := genTree(tr, root, make([]int32, 0, 128))
		treeStr := lipgloss.NewStyle().MaxWidth(m.treeWidth).Render(tr.String())
		style := lipgloss.NewStyle().Inline(true)
		sb := strings.Builder{}
		for _, v := range pidSort {
			p, have := pinfo[v]
			if !have {
				sb.WriteByte('\n')
				continue
			}
			sb.WriteString(FormatStr(fmt.Sprintf("%d", p.Pid), 7, lipgloss.Right))
			sb.WriteByte(' ')
			sb.WriteString(style.MaxWidth(9).Width(9).Align(lipgloss.Left).Render(p.User))
			sb.WriteByte(' ')
			if dcu, have := m.mapPidToDCU[p.Pid]; have {
				sb.WriteString(style.MaxWidth(6).Width(6).Align(lipgloss.Left).Render(fmt.Sprintf("DCU%d", dcu)))
			} else {
				sb.WriteString(style.MaxWidth(6).Width(6).Align(lipgloss.Left).Render("Host"))
			}
			sb.WriteByte(' ')
			sb.WriteString(style.MaxWidth(5).Width(5).Align(lipgloss.Left).Render(fmt.Sprintf("%.1f", p.CPU)))
			sb.WriteByte(' ')
			sb.WriteString(style.MaxWidth(5).Width(5).Align(lipgloss.Left).Render(fmt.Sprintf("%.1f", p.Mem)))
			sb.WriteByte(' ')
			sb.WriteString(style.MaxWidth(9).Width(9).Align(lipgloss.Left).Render(backend.DurationStr(p.Time)))
			sb.WriteByte(' ')
			sb.WriteByte('\n')
		}
		infoStr := strings.Trim(sb.String(), "\n")
		if m.actionMsg != nil {
			if m.actionMsg.PidTreePids != nil {
				m.actionMsg.PidTreePids = m.actionMsg.PidTreePids[:0]
				m.actionMsg.PidTreePids = append(m.actionMsg.PidTreePids, pidSort...)
			} else {
				m.actionMsg.PidTreePids = make([]int32, 0, len(pidSort))
				m.actionMsg.PidTreePids = append(m.actionMsg.PidTreePids, pidSort...)
			}
		}
		sl := len(m.actionMsg.SelectPids)
		downStr := lipgloss.JoinHorizontal(lipgloss.Top, infoStr, treeStr)
		if darkMode {
			downStr = LowLeightStyle.Render(downStr)
			finalStr := lipgloss.JoinVertical(lipgloss.Left, m.header[1], downStr)
			return ModelPsFullHeightStyle.Render(finalStr)
		}
		lines := strings.Split(downStr, "\n")
		for index, pid := range pidSort {
			selected, pointed := false, false
			if sl > 0 {
				a, b := m.actionMsg.SelectPids[pid]
				selected = a && b
			}
			if m.actionMsg.PointPid != nil {
				pointed = *m.actionMsg.PointPid == pid
			}
			if selected && pointed {
				lines[index] = HeightSelectedStyle.Render(lines[index])
			} else if selected {
				lines[index] = SelectedStyle.Render(lines[index])
			} else if pointed {
				lines[index] = HeightLightStyle.Render(lines[index])
			}
		}
		downStr = strings.Join(lines, "\n")
		finalStr := lipgloss.JoinVertical(lipgloss.Left, m.header[0], downStr)
		return ModelPsFullHeightStyle.Render(finalStr)
	} else {
		return m.header[0] + "\n There is no DCU process running"
	}
}

func (m *ModelPsTree) Update(imsg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := imsg.(type) {
	case *ModelMsg:
		m.modelMsg = msg
		clear(m.mapPidToDCU)
		for k, v := range msg.DCUPidInfo {
			for _, val := range v {
				m.mapPidToDCU[val.Info.Pid] = k
			}
		}
	case *ActionMsg:
		m.actionMsg = msg
	}
	return m, nil
}

func genTree(t *tree.Tree, root *utils.ProcessInfo, ctx []int32) []int32 {
	ctx = append(ctx, root.Pid)
	pids := make([]int32, 0, len(root.Child))
	for k := range root.Child {
		pids = append(pids, k)
	}
	slices.Sort(pids)
	for _, v := range pids {
		c := root.Child[v]
		tt := tree.New().Root(c.Cmd)
		t.Child(tt)
		if len(c.Child) > 0 {
			ctx = genTree(tt, c, ctx)
		} else {
			ctx = append(ctx, c.Pid)
		}
	}
	return ctx
}
