package tui

import (
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"get-container/cmd/hytop/tchart"
	"get-container/utils"

	"github.com/NimbleMarkets/ntcharts/canvas/runes"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
	linkedlist "github.com/emirpasic/gods/v2/lists/doublylinkedlist"
	zone "github.com/lrstanley/bubblezone"
)

const (
	A = "├"
	B = "└"
)

var (
	axisFStyle = lipgloss.NewStyle().Inline(true).Foreground(lipgloss.Color("#4d4d4dff"))
)

// genXAxis 生成X轴，参数l是x轴的长度
func genXAxis(l int, s ...string) string {
	if s == nil {
		s = make([]string, 0, 1)
		s = append(s, A)
	}
	t60 := l / 30
	t30 := l >= 18
	var result string
	if t30 {
		result = s[0] + strings.Repeat("─", 14)
		result = axisFStyle.Render("30s") + result
	} else {
		return strings.Repeat("─", l)
	}
	// 长度不超过33
	if l < 33 {
		return strings.Repeat("─", l-18) + result
	}
	for i := 1; i <= t60+1; i++ {
		timeStr := strconv.Itoa(i*60) + "s"
		timeStrLen := len(timeStr)
		timeStr = axisFStyle.Render(timeStr)
		targetLen := timeStrLen + i*30
		if l < targetLen {
			// 不渲染标记，仅增加轴长度
			result = strings.Repeat("─", l-lipgloss.Width(result)) + result
			break
		}
		// 渲染标记
		result = timeStr + s[0] + strings.Repeat("─", targetLen-lipgloss.Width(result)-timeStrLen-1) + result
	}
	return result
}

func genXAxisNoStyle(l int) string {
	t60 := l / 30
	t30 := l >= 18
	var result string
	if t30 {
		result = A + strings.Repeat("─", 14)
		result = "30s" + result
	} else {
		return strings.Repeat("─", l)
	}
	// 长度不超过33
	if l < 33 {
		return strings.Repeat("─", l-18) + result
	}
	for i := 1; i <= t60+1; i++ {
		timeStr := strconv.Itoa(i*60) + "s"
		timeStrLen := len(timeStr)
		targetLen := timeStrLen + i*30
		if l < targetLen {
			// 不渲染标记，仅增加轴长度
			result = strings.Repeat("─", l-lipgloss.Width(result)) + result
			break
		}
		// 渲染标记
		result = timeStr + A + strings.Repeat("─", targetLen-lipgloss.Width(result)-timeStrLen-1) + result
	}
	return result
}

// MyTimeChartMsg 时间流表消息，用于插入数据
type MyTimeChartMsg struct {
	Points []tchart.TimePoint // 待添加的数据点
	Reset  bool               // 添加数据点前是否清除原有数据点
}

func NewTimeCharMsg(point []tchart.TimePoint, reset bool) MyTimeChartMsg {
	return MyTimeChartMsg{
		Points: point,
		Reset:  reset,
	}
}

// MyTimeChart 特化的时间流表，时间区域就是宽度的2倍，单位是秒
type MyTimeChart struct {
	chart         *tchart.Model                      // 原始图表
	zM            *zone.Manager                      // 区域管理
	points        *linkedlist.List[tchart.TimePoint] // 数据点，这里只存储put的数据，不存储自动添加的数据
	width, height int                                // 图表的高度和宽度
	max, min      float64                            // y轴的最值
	lockPoints    sync.RWMutex                       // 保护dataSet的并发读写
	color         []lipgloss.Color
	middleLine    bool // 是否添加中间线
}

// New 新建图表，color的长度如果为1，则图表的颜色为color[0]，如果color的长度为heigh，则图表的颜色随高度变化
func NewTimeChart(width, height int, vmin, vmax float64, color []lipgloss.Color) *MyTimeChart {
	result := MyTimeChart{}
	result.max = vmax
	result.min = vmin
	result.width = width
	result.height = height
	zoneManager := zone.New()
	result.zM = zoneManager
	result.lockPoints = sync.RWMutex{}
	result.lockPoints.Lock()
	result.points = linkedlist.New[tchart.TimePoint]()
	result.middleLine = false
	now := time.Now()
	// 用最小值填充空白点
	t := result.width*2 + 1
	tmpPoints := make([]tchart.TimePoint, 0, t)
	for i := range t {
		tmpPoints = append(tmpPoints, tchart.TimePoint{Time: now.Add(time.Duration(-i) * time.Second), Value: result.min})
	}
	result.lockPoints.Unlock()
	s := tchart.New(width, height+1,
		tchart.WithLineStyle(runes.ThinLineStyle),
		tchart.WithZoneManager(zoneManager),
		tchart.WithYRange(vmin, vmax),
		tchart.WithXYSteps(0, 0),
		tchart.WithTimeSeries(tmpPoints),
	)
	result.chart = &s
	if len(color) == 2 {
		result.color = GenGradientColor(color[0], color[1], height)
	} else {
		result.color = color
	}
	return &result
}

// SortPoints 对不是自动填充的点进行排序
func (m *MyTimeChart) SortPoints() {
	m.points.Sort(func(x, y tchart.TimePoint) int {
		if x.Time.After(y.Time) {
			return 1
		}
		if x.Time.Before(y.Time) {
			return -1
		}
		return 0
	})
}

// RemoveUselessPoint 删除无用的点(即超出x轴范围的点)
func (m *MyTimeChart) RemoveUselessPoint() {
	m.SortPoints()
	// th为时间阈值，在此之前的点需要删除
	th := time.Now().Add(time.Duration(m.width*-2) * time.Second)
	for {
		// 如果没有点，则直接退出
		t, b := m.points.Get(0)
		if !b {
			break
		}
		if t.Time.Before(th) {
			// 如果第一个点在阈值之前，就删除
			m.points.Remove(0)
		} else {
			// 否则就退出
			break
		}
	}
}

// PutPoint 添加若干数据点
func (m *MyTimeChart) PutPoint(points []tchart.TimePoint) {
	m.lockPoints.Lock()
	if len(points) > 0 {
		m.points.Add(points...)
	}
	m.RemoveUselessPoint()
	threshold := time.Now().Add(time.Second * time.Duration(-2*m.width))
	points = m.points.Values()
	tmpPoint := make([]tchart.TimePoint, 0, m.width*2)
	// 判断是否需要补充空点
	if !points[0].Time.Before(threshold) {
		t := points[0].Time.Add(-time.Second)
		for {
			if !t.Before(threshold) {
				tmpPoint = append(tmpPoint, tchart.TimePoint{Time: t, Value: m.min})
				t = t.Add(-time.Second)
			} else {
				break
			}
		}
	}

	tmpPoint = append(tmpPoint, points...)
	sort.Slice(tmpPoint, func(i, j int) bool {
		return tmpPoint[i].Time.Before(tmpPoint[j].Time)
	})
	s := tchart.New(m.width, m.height+1,
		tchart.WithLineStyle(runes.ThinLineStyle),
		tchart.WithZoneManager(m.zM),
		tchart.WithYRange(m.min, m.max),
		tchart.WithXYSteps(0, 0),
		tchart.WithTimeSeries(tmpPoint),
	)
	m.lockPoints.Unlock()
	// 插入数据
	m.chart = nil
	m.chart = &s
	m.chart.DrawXYAxisAndLabel()
	m.chart.DrawBrailleAll()
}

// ResetPutPoint 清除原有的数据，然后插入数据
func (m *MyTimeChart) ResetPutPoint(points []tchart.TimePoint) {
	// 清除原有的点
	m.lockPoints.Lock()
	m.points.Clear()
	if len(points) > 0 {
		m.points.Add(points...)
	}
	m.RemoveUselessPoint()
	threshold := time.Now().Add(time.Second * time.Duration(-2*m.width))
	points = m.points.Values()
	tmpPoint := make([]tchart.TimePoint, 0, m.width*2)

	// 判断是否需要补充空点
	if !points[0].Time.Before(threshold) {
		t := points[0].Time.Add(-time.Second)
		for {
			if !t.Before(threshold) {
				tmpPoint = append(tmpPoint, tchart.TimePoint{Time: t, Value: m.min})
				t = t.Add(-time.Second)
			} else {
				break
			}
		}
	}
	tmpPoint = append(tmpPoint, points...)
	sort.Slice(tmpPoint, func(i, j int) bool {
		return tmpPoint[i].Time.Before(tmpPoint[j].Time)
	})
	s := tchart.New(m.width, m.height+1,
		tchart.WithLineStyle(runes.ThinLineStyle),
		tchart.WithZoneManager(m.zM),
		tchart.WithYRange(m.min, m.max),
		tchart.WithXYSteps(0, 0),
		tchart.WithTimeSeries(tmpPoint),
	)
	m.lockPoints.Unlock()
	// 插入数据
	m.chart = nil
	m.chart = &s
	m.chart.DrawXYAxisAndLabel()
	m.chart.DrawBrailleAll()
}

func (m *MyTimeChart) Init() tea.Cmd {
	m.chart.DrawXYAxisAndLabel()
	m.chart.DrawBrailleAll()
	return nil
}

func (m *MyTimeChart) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := inputMsg.(type) {
	case MyTimeChartMsg:
		if msg.Reset {
			m.ResetPutPoint(msg.Points)
		} else {
			m.PutPoint(msg.Points)
		}
		return m, nil
	}
	return m, nil
}

func (m *MyTimeChart) genLines() []string {
	str := m.zM.Scan(m.chart.View())
	sb := strings.Builder{}
	sb.WriteString(str[m.width+1:])
	str = sb.String()
	lines := strings.Split(strings.Trim(str, "\n"), "\n")
	runes := make([][]rune, len(lines))
	for k, v := range lines {
		runes[k] = []rune(v)
	}
	width := lipgloss.Width(str)
	height := lipgloss.Height(str)
	for col := range width {
		firstLine := -1
		var charStat utils.CharType = utils.CharEmpty
		for line := range height {
			c := runes[line][col]
			charType := utils.GetCharType(c)
			if charType != utils.CharEmpty && firstLine == -1 {
				firstLine = line
			}
			if firstLine == -1 {
				continue
			}
			if firstLine == line {
				// 遇到了一列的第一个非空字符
				if t, have := utils.MM[c]; have {
					runes[line][col] = t
				}
				charStat = utils.CharTypeOr(charStat, utils.GetCharType(runes[line][col]))
			} else {
				// 第一个非空字符下面的字符
				switch charType {
				case utils.CharEmpty:
					switch charStat {
					case utils.CharLeft:
						runes[line][col] = utils.LeftFullMW
					case utils.CharRight:
						runes[line][col] = utils.RightFullMW
					case utils.CharFull:
						runes[line][col] = utils.FullMW
					}
					if t, have := utils.MM[runes[line][col]]; have {
						runes[line][col] = t
					}
				case utils.CharLeft:
					switch utils.CharTypeOr(charStat, utils.CharLeft) {
					case utils.CharLeft:
						runes[line][col] = utils.LeftFullMW
					case utils.CharRight:
						runes[line][col] = utils.RightFullMW
					case utils.CharFull:
						runes[line][col] = utils.FullMW
					}
					charStat = utils.CharTypeOr(charStat, utils.CharLeft)
					if t, have := utils.MM[runes[line][col]]; have {
						runes[line][col] = t
					}
				case utils.CharRight:
					switch utils.CharTypeOr(charStat, utils.CharRight) {
					case utils.CharLeft:
						runes[line][col] = utils.LeftFullMW
					case utils.CharRight:
						runes[line][col] = utils.RightFullMW
					case utils.CharFull:
						runes[line][col] = utils.FullMW
					}
					charStat = utils.CharTypeOr(charStat, utils.CharRight)
					if t, have := utils.MM[runes[line][col]]; have {
						runes[line][col] = t
					}
				case utils.CharFull:
					charStat = utils.CharFull
					if t, have := utils.MM[runes[line][col]]; have {
						runes[line][col] = t
					}
				}

			}
		}
	}
	resultLines := make([]string, height)
	for k, v := range runes {
		resultLines[k] = string(v)
	}
	return resultLines
}

func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string {
	resultLines := m.genLines()
	height := len(resultLines)
	style := lipgloss.NewStyle()
	if len(color) == height {
		for k, v := range resultLines {
			resultLines[k] = style.Foreground(color[k]).Render(v)
		}
	}
	return strings.Join(resultLines, "\n")
}

func (m *MyTimeChart) View() string {
	resultLines := m.genLines()
	height := len(resultLines)
	switch len(m.color) {
	case 1:
		style := lipgloss.NewStyle().Foreground(m.color[0])
		return style.Render(strings.Join(resultLines, "\n"))
	case height:
		style := lipgloss.NewStyle()
		for k, v := range resultLines {
			resultLines[k] = style.Foreground(m.color[k]).Render(v)
		}
		return strings.Join(resultLines, "\n")
	default:
		return strings.Join(resultLines, "\n")
	}
}

// ViewWithMiddleLine
func (m *MyTimeChart) ViewWithMiddleLine() string {
	resultLines := m.genLines()
	height := len(resultLines)
	targetLine := 0
	isMiddle := false
	if height%2 == 0 {
		targetLine = height/2 - 1
	} else {
		targetLine = height / 2
		isMiddle = true
	}
	switch len(m.color) {
	case 1:
		style := lipgloss.NewStyle().Foreground(m.color[0])
		for k, v := range resultLines {
			if k == targetLine {
				if isMiddle {
					resultLines[k] = style.Strikethrough(true).Render(v)
					style.Strikethrough(false)
				} else {
					resultLines[k] = style.Underline(true).Render(v)
					style.Underline(false)
				}
			} else {
				resultLines[k] = style.Render(v)
			}
		}
		return strings.Join(resultLines, "\n")
	case height:
		style := lipgloss.NewStyle()
		for k, v := range resultLines {
			if k == targetLine {
				if isMiddle {
					resultLines[k] = style.Strikethrough(true).Foreground(m.color[k]).Render(v)
					style.Strikethrough(false)
				} else {
					resultLines[k] = style.Underline(true).Foreground(m.color[k]).Render(v)
					style.Underline(false)
				}
			} else {
				resultLines[k] = style.Foreground(m.color[k]).Render(v)
			}
		}
		return strings.Join(resultLines, "\n")
	default:
		style := lipgloss.NewStyle()
		for k, v := range resultLines {
			if k == targetLine {
				if isMiddle {
					resultLines[k] = style.Strikethrough(true).Render(v)
					style.Strikethrough(false)
				} else {
					resultLines[k] = style.Underline(true).Render(v)
					style.Underline(false)
				}
			} else {
				resultLines[k] = style.Render(v)
			}
		}
		return strings.Join(resultLines, "\n")
	}
}
