package tui

import (
	"fmt"
	"get-container/utils"
	"regexp"
	"strconv"
	"strings"

	"github.com/charmbracelet/bubbles/progress"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
	"github.com/lucasb-eyer/go-colorful"
	"github.com/muesli/gamut"
	"github.com/muesli/termenv"
)

type ModelDCUInfo struct {
	width, height          int            // 屏幕尺寸
	pro                    progress.Model // 进度条
	proLowLeight           progress.Model // 低亮度进度条
	proWidth               int            // 进度条宽度
	modMsg                 *ModelMsg      // 缓存的模型信息
	actionMsg              *ActionMsg     // 缓存的动作信息
	darkModule             bool           // 是否为黑暗模式
	top, middle, bottom    string         // 缓存的边框
	topL, middleL, bottomL string         // 缓存的低亮度边框
	subLin                 int            // 边框更新标志
}

const (
	TopLine    = `╞═══════════════════════════════╪══════════════════════╪══════════════════════╪`
	MiddleLine = `├───────────────────────────────┼──────────────────────┼──────────────────────┼`
	BottomLine = `╞═══════════════════════════════╧══════════════════════╧══════════════════════╪`

	OtherWidth       = 8
	StaticWidth      = 79 // 固定表格的宽度
	ProgressMinWidth = 15 // 进度条最小宽度
)

var (
	ReDCUName = regexp.MustCompile(`(?i)^[A-Z0-9-_]*`)
)

func NewModelDCUInfo(w, h int) *ModelDCUInfo {
	return &ModelDCUInfo{width: w, height: h, darkModule: false}
}

func (m *ModelDCUInfo) Init() tea.Cmd {
	if m.width < StaticWidth+ProgressMinWidth+OtherWidth {
		return tea.Quit
	}
	m.proWidth = ProgressMinWidth
	if m.width > StaticWidth+ProgressMinWidth+OtherWidth {
		m.proWidth = m.width - OtherWidth - StaticWidth
	}
	m.pro = progress.New(progress.WithColorProfile(termenv.TrueColor), progress.WithGradient("#0000ffff", "#ff0000ff"), progress.WithWidth(m.proWidth-5), progress.WithoutPercentage())
	c1, _ := colorful.MakeColor(gamut.Darker(lipgloss.Color("#0000ffff"), 0.8))
	c2, _ := colorful.MakeColor(gamut.Darker(lipgloss.Color("#ff0000ff"), 0.8))
	m.proLowLeight = progress.New(progress.WithColorProfile(termenv.TrueColor), progress.WithGradient(c1.Hex(), c2.Hex()), progress.WithWidth(m.proWidth-5), progress.WithoutPercentage())
	return nil
}

func (m *ModelDCUInfo) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := inputMsg.(type) {
	case *ModelMsg:
		m.modMsg = msg
		return m, nil
	case tea.WindowSizeMsg:
		m.height = msg.Height
		m.width = msg.Width
		return m, nil
	case *ActionMsg:
		m.actionMsg = msg
		if msg.Action != nil && msg.VM == VMMain {
			m.darkModule = true
		} else {
			m.darkModule = false
		}
		return m, nil
	}
	return m, nil
}

func (m *ModelDCUInfo) View() string {
	if m.modMsg == nil {
		return ""
	}
	lineWidth := 0
	qmap, qlock := m.modMsg.DCUInfo.GetQuitInfo()
	smap, slock := m.modMsg.DCUInfo.GetSlowInfo()
	strBuilder := strings.Builder{}
	strCache := strings.Builder{}
	var targetDCU int = -1
	if m.actionMsg != nil {
		targetDCU = m.actionMsg.TargetDCUIndex
	}
	var borderStr string = myBorder.Left
	if m.darkModule {
		borderStr = LowLeightStyle.Render(borderStr)
	}
	infos := make([]string, 0)

	for i := range 32 {
		qinfo, haveq := qmap[i]
		sinfo, haves := smap[i]
		if !(haveq || haves) {
			continue
		}
		isLowStyle := (targetDCU != qinfo.Id && targetDCU != -1) || m.darkModule
		strBuilder.WriteString(borderStr)

		strCache.WriteByte(' ')
		strCache.WriteString(FormatStr(strconv.Itoa(qinfo.Id), 4, lipgloss.Left))
		strCache.WriteByte(' ')
		strCache.WriteString(FormatStr(ReDCUName.FindString(qinfo.Name.OrElse("Unknow")), 8, lipgloss.Left))
		strCache.WriteByte(' ')
		strCache.WriteString(FormatStr(qinfo.PerformanceLevel.OrElse("Unknow"), 15, lipgloss.Right))
		strCache.WriteByte(' ')

		if isLowStyle {
			str := strCache.String()
			strBuilder.WriteString(LowLeightStyle.Render(str))
		} else {
			strBuilder.WriteString(strCache.String())
		}
		strCache.Reset()
		strBuilder.WriteString(borderStr)

		strCache.WriteByte(' ')
		strCache.WriteString(FormatStr(qinfo.BusId.OrElse("Unknow"), 13, lipgloss.Left))
		strCache.WriteByte(' ')
		strCache.WriteString(FormatStr("Off", 6, lipgloss.Right))
		strCache.WriteByte(' ')
		if isLowStyle {
			str := strCache.String()
			strBuilder.WriteString(LowLeightStyle.Render(str))
		} else {
			strBuilder.WriteString(strCache.String())
		}
		strCache.Reset()

		strCache.WriteByte(' ')
		strCache.WriteString(FormatStr("Off", 16, lipgloss.Left))
		strBuilder.WriteString(borderStr)
		strCache.WriteByte(' ')
		if sinfo.Ecc.Load() {
			strCache.WriteString(FormatStr("On", 3, lipgloss.Right))
		} else {
			strCache.WriteString(FormatStr("Off", 3, lipgloss.Right))
		}
		strCache.WriteByte(' ')
		if isLowStyle {
			str := strCache.String()
			strBuilder.WriteString(LowLeightStyle.Render(str))
		} else {
			strBuilder.WriteString(strCache.String())
		}
		strCache.Reset()
		strBuilder.WriteString(borderStr)

		if isLowStyle {
			strBuilder.WriteString(LowLeightStyle.Render(" DCU: "))
			strBuilder.WriteString(m.proLowLeight.ViewAs(float64(qinfo.DCUUTil.OrElse(0.0) / 100)))
			strBuilder.WriteString(LowLeightStyle.Render(fmt.Sprintf(" %3d%% ", int(qinfo.DCUUTil.OrElse(0.0)))))
		} else {
			strBuilder.WriteString(" DCU: ")
			strBuilder.WriteString(m.pro.ViewAs(float64(qinfo.DCUUTil.OrElse(0.0) / 100)))
			strBuilder.WriteString(fmt.Sprintf(" %3d%% ", int(qinfo.DCUUTil.OrElse(0.0))))
		}
		strBuilder.WriteString(borderStr)
		strBuilder.WriteByte('\n')
		strBuilder.WriteString(borderStr)

		strCache.WriteByte(' ')
		strCache.WriteString(FormatStr(qinfo.Fan.OrElse("N/A"), 4, lipgloss.Left))
		strCache.WriteByte(' ')
		if qinfo.Temp.IsSome() {
			temp := qinfo.Temp.MustGet()
			if temp == 0 {
				strCache.WriteString(FormatStr("N/A", 6, lipgloss.Left))
			} else {
				strCache.WriteString(FormatStr(fmt.Sprintf("%.1f°C", temp), 6, lipgloss.Left))
			}
		} else {
			strCache.WriteString(FormatStr("N/A", 6, lipgloss.Left))
		}
		strCache.WriteByte(' ')
		strCache.WriteString(FormatStr(fmt.Sprintf("%.1fW / %.1fW", qinfo.PwrAvg.OrElse(0.0), qinfo.PwrCap.OrElse(0.0)), 17, lipgloss.Right))
		strCache.WriteByte(' ')

		if isLowStyle {
			str := strCache.String()
			strBuilder.WriteString(LowLeightStyle.Render(str))
		} else {
			strBuilder.WriteString(strCache.String())
		}
		strCache.Reset()
		strBuilder.WriteString(borderStr)

		if isLowStyle {
			strCache.WriteByte(' ')
			strCache.WriteString(FormatStr(fmt.Sprintf("%dMiB / %dMiB", qinfo.MemUsed.OrElse(0)/uint64(utils.MiB), qinfo.MemTotal.OrElse(0)/uint64(utils.MiB)), 20, lipgloss.Right))
			strCache.WriteByte(' ')
			strBuilder.WriteString(LowLeightStyle.Render(strCache.String()))
			strCache.Reset()
		} else {
			strBuilder.WriteByte(' ')
			strBuilder.WriteString(FormatStr(fmt.Sprintf("%dMiB / %dMiB", qinfo.MemUsed.OrElse(0)/uint64(utils.MiB), qinfo.MemTotal.OrElse(0)/uint64(utils.MiB)), 20, lipgloss.Right))
			strBuilder.WriteByte(' ')
		}

		strBuilder.WriteString(borderStr)

		if isLowStyle {
			strCache.WriteByte(' ')
			strCache.WriteString(FormatStr(fmt.Sprintf("%.1f%%", qinfo.DCUUTil.OrElse(0)), 6, lipgloss.Left))
			strCache.WriteByte(' ')
			strCache.WriteString(FormatStr(sinfo.PwrMode.Load().(string), 13, lipgloss.Right))
			strCache.WriteByte(' ')
			strBuilder.WriteString(LowLeightStyle.Render(strCache.String()))
			strCache.Reset()
		} else {
			strBuilder.WriteByte(' ')
			strBuilder.WriteString(FormatStr(fmt.Sprintf("%.1f%%", qinfo.DCUUTil.OrElse(0)), 6, lipgloss.Left))
			strBuilder.WriteByte(' ')
			strBuilder.WriteString(FormatStr(sinfo.PwrMode.Load().(string), 13, lipgloss.Right))
			strBuilder.WriteByte(' ')
		}

		strBuilder.WriteString(borderStr)

		if isLowStyle {
			strCache.WriteString(" Mem: ")
			strCache.WriteString(m.proLowLeight.ViewAs(float64(qinfo.MemUsed.OrElse(0)) / float64(qinfo.MemTotal.OrElse(1))))
			strCache.WriteString(LowLeightStyle.Render(fmt.Sprintf(" %3d%% ", int(100*float64(qinfo.MemUsed.OrElse(0))/float64(qinfo.MemTotal.OrElse(1))))))
			strBuilder.WriteString(LowLeightStyle.Render(strCache.String()))
			strCache.Reset()
		} else {
			strBuilder.WriteString(" Mem: ")
			strBuilder.WriteString(m.pro.ViewAs(float64(qinfo.MemUsed.OrElse(0)) / float64(qinfo.MemTotal.OrElse(1))))
			strBuilder.WriteByte(' ')
			strBuilder.WriteString(fmt.Sprintf("%3d%%", int(100*float64(qinfo.MemUsed.OrElse(0))/float64(qinfo.MemTotal.OrElse(1)))))
			strBuilder.WriteByte(' ')
		}
		strBuilder.WriteString(borderStr)
		strBuilder.WriteByte('\n')
		infos = append(infos, strBuilder.String())
		strBuilder.Reset()
	}
	slock.Unlock()
	qlock.Unlock()
	if len(infos) > 0 {
		lineWidth = lipgloss.Width(infos[0])
	}
	subLen := max(lineWidth-StaticWidth-1, 0)
	if subLen != m.subLin {
		top := TopLine + strings.Repeat("═", subLen) + "╕"
		middle := MiddleLine + strings.Repeat("─", subLen) + "┤"
		bottom := BottomLine + strings.Repeat("═", subLen) + "╡"
		m.topL = LowLeightStyle.Render(top) + "\n"
		m.middleL = LowLeightStyle.Render(middle) + "\n"
		m.bottomL = LowLeightStyle.Render(bottom) + "\n"
		m.top = top + "\n"
		m.middle = middle + "\n"
		m.bottom = bottom + "\n"
	}
	if m.darkModule {
		return m.topL + strings.Join(infos, m.middleL) + m.bottomL
	} else {
		return m.top + strings.Join(infos, m.middle) + m.bottom
	}
}
