// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package components

import (
	"fmt"
	"slices"

	"github.com/rivo/tview"

	"github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata"
	"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
)

// Diagnostics represents the diagnostics widget.
type Diagnostics struct {
	tview.Grid

	hline *HorizontalLine
	info  *tview.TextView

	selectedNode    string
	perNodeWarnings map[string][]*runtime.Diagnostic
}

// NewDiagnostics initializes Diagnostics.
func NewDiagnostics() *Diagnostics {
	widget := &Diagnostics{
		Grid:            *tview.NewGrid(),
		info:            tview.NewTextView(),
		hline:           NewHorizontalLine("Diagnostics"),
		perNodeWarnings: make(map[string][]*runtime.Diagnostic),
	}

	widget.info.
		SetDynamicColors(true).
		SetBorderPadding(0, 0, 1, 1)

	widget.SetRows(1, 0).SetColumns(0)

	widget.AddItem(widget.hline, 0, 0, 1, 1, 0, 0, false)
	widget.AddItem(widget.info, 1, 0, 1, 1, 0, 0, false)

	return widget
}

// GetCurrentHeight returns the height of the widget.
func (widget *Diagnostics) GetCurrentHeight() int {
	numWarnings := len(widget.perNodeWarnings[widget.selectedNode])
	if numWarnings == 0 {
		return 0
	}

	return 1 + numWarnings
}

// OnNodeSelect implements the NodeSelectListener interface.
func (widget *Diagnostics) OnNodeSelect(node string) {
	if node != widget.selectedNode {
		widget.selectedNode = node

		widget.redraw()
	}
}

// OnResourceDataChange implements the ResourceDataListener interface.
func (widget *Diagnostics) OnResourceDataChange(data resourcedata.Data) {
	r, ok := data.Resource.(*runtime.Diagnostic)
	if !ok {
		return
	}

	idx := slices.IndexFunc(widget.perNodeWarnings[data.Node], func(warning *runtime.Diagnostic) bool {
		return warning.Metadata().ID() == r.Metadata().ID()
	})

	if data.Deleted {
		if idx != -1 {
			widget.perNodeWarnings[data.Node] = slices.Delete(widget.perNodeWarnings[data.Node], idx, idx+1)
		}
	} else {
		if idx == -1 {
			widget.perNodeWarnings[data.Node] = append(widget.perNodeWarnings[data.Node], r)
		} else {
			widget.perNodeWarnings[data.Node][idx] = r
		}
	}

	if data.Node == widget.selectedNode {
		widget.redraw()
	}
}

// WriteLog writes the log line to the widget.
func (widget *Diagnostics) redraw() {
	widget.info.SetWrap(true)
	widget.info.Clear()

	for _, warning := range widget.perNodeWarnings[widget.selectedNode] {
		fmt.Fprintf(widget.info, "■ (%s) [red]%s[-]\n", //nolint:errcheck
			tview.Escape(warning.TypedSpec().DocumentationURL(warning.Metadata().ID())),
			tview.Escape(warning.TypedSpec().Message))
	}
}
