package special_views

// |@@| C

import (
	"context"
	"gardening/src/lib/config"
	"gardening/src/lib/dev_tool"
	erro "gardening/src/lib/error"
	"gardening/src/lib/kit"
	"gardening/src/lib/logger"
	"gardening/src/srv/urlers"
	"os"
	"runtime"
	"strconv"
	"strings"

	"gitlab.com/ccyrillee/kitcla/goc"
	u "gitlab.com/ccyrillee/kitcla/goc_utils"
)

type ErrorView struct {
	AppUrler *urlers.AppUrler
	Kit      *kit.Kit
	Logger   *logger.Logger
	Config   *config.Config
	DevTool  *dev_tool.DevTool
}

type Frame struct {
	File string
	Line int
	Code string
}

type StackOption struct {
	label  string
	frames []*Frame
}

type ErrorViewMod struct {
	Ctx  context.Context
	Err1 error
	Err2 *erro.Error
	// Debug information
	RequestMethod   string
	RequestURL      string
	UserAgent       string
	RequestID       string
	ErrorTime       string
	RequestDuration string
	Headers         map[string]string
	QueryParams     map[string]string
	FormData        map[string]string
	// Route and Mae service information
	RoutePath    string
	RoutePattern string
	HandlerName  string
	MaeName      string
	RouteParams  map[string]string
	// Routing information from stack trace
	RouteMatched      string
	RouteMatchedPath  string
	MaeMatched        string
	MaeMatchedPath    string
	MiddlewareMatched []string
	MiddlewarePaths   []string
}

func (this *ErrorView) H(mod *ErrorViewMod) goc.HTML {
	return u.Ccs(
		"div",
		"bg-neutral-800 text-neutral-200 min-h-screen",
		this.banner(mod),
		this.main(mod),
	)
}

func (this *ErrorView) logs(mod *ErrorViewMod) goc.HTML {
	record, has := this.Logger.FindCurrentProfile(mod.Ctx)
	if !has {
		return u.Dcv("text-neutral-400 text-center py-8", "No logs available")
	}

	return this.logsTable(record.Logs)
}

func (this *ErrorView) record(mod *ErrorViewMod) goc.HTML {
	record, has := this.DevTool.FindCurrentRecord(mod.Ctx)
	if !has {
		return u.Dcv("text-neutral-400 text-center py-8", "No record available")
	}

	return u.Dcs("space-y-6",
		this.recordInfo(record),
	)
}

func (this *ErrorView) recordInfo(record *dev_tool.Record) goc.HTML {
	return u.Dcs("bg-neutral-800 border border-neutral-600 rounded-lg p-6",
		u.Dcv("text-lg font-semibold text-white mb-4", "Record Information"),
		u.Ccs("table", "w-full border-collapse",
			u.Ccs("tbody", "",
				u.Ccs("tr", "border-b border-neutral-700",
					u.Ccs("td", "w-64 px-3 py-3", u.Dcv("text-base font-medium text-neutral-400 bg-neutral-700 border border-neutral-600 rounded px-2 py-1", "UID")),
					u.Ccv("td", "px-3 py-3 text-base text-neutral-100 font-mono", record.Uid),
				),
				u.Ccs("tr", "",
					u.Ccs("td", "w-64 px-3 py-3", u.Dcv("text-base font-medium text-neutral-400 bg-neutral-700 border border-neutral-600 rounded px-2 py-1", "Timestamp")),
					u.Ccv("td", "px-3 py-3 text-base text-neutral-100 font-mono", record.Timestamp.Format("2006-01-02 15:04:05.000")),
				),
			),
		),
	)
}

func (this *ErrorView) routeInfo(routeInfo *dev_tool.RouteInfo) goc.HTML {
	if routeInfo == nil {
		return u.Dcv("text-neutral-400 text-center py-4", "No route information")
	}

	var rows []goc.HTML
	var rowData []struct{ key, value string }

	if routeInfo.Route != "" {
		rowData = append(rowData, struct{ key, value string }{"Route", routeInfo.Route})
	}
	if routeInfo.RouteUrl != "" {
		rowData = append(rowData, struct{ key, value string }{"URL", routeInfo.RouteUrl})
	}
	if routeInfo.RouteMethod != "" {
		rowData = append(rowData, struct{ key, value string }{"Method", routeInfo.RouteMethod})
	}
	if routeInfo.Mae != "" {
		rowData = append(rowData, struct{ key, value string }{"Mae", routeInfo.Mae})
	}
	if routeInfo.Duration != "" {
		rowData = append(rowData, struct{ key, value string }{"Duration", routeInfo.Duration})
	}

	for i, data := range rowData {
		borderClass := "border-b border-neutral-700"
		if i == len(rowData)-1 {
			borderClass = ""
		}
		rows = append(rows, u.Ccs("tr", borderClass,
			u.Ccs("td", "w-32 px-3 py-3", u.Dcv("text-base font-medium text-neutral-400 bg-neutral-700 border border-neutral-600 rounded px-3 py-2", data.key)),
			u.Ccv("td", "px-3 py-3 text-base text-neutral-100 font-mono", data.value),
		))
	}

	if len(rows) == 0 {
		return u.Dcv("text-neutral-400 text-center py-4", "No route information")
	}

	return u.Dcs("bg-neutral-800 border border-neutral-600 rounded-lg p-6",
		u.Dcv("text-lg font-semibold text-white mb-4", "Route Information"),
		u.Ccs("table", "w-full border-collapse",
			u.Ccs("tbody", "", rows...),
		),
	)
}

func (this *ErrorView) logsTable(logs []*logger.Log) goc.HTML {
	if len(logs) == 0 {
		return u.Dcv("text-neutral-400 text-center py-8", "No logs found")
	}

	headerCells := []goc.HTML{
		u.Ccv("th", "w-32 px-4 py-3 text-left text-base font-medium text-neutral-400 uppercase tracking-wider bg-neutral-700", "Level"),
		u.Ccv("th", "w-1/2 px-4 py-3 text-left text-base font-medium text-neutral-400 uppercase tracking-wider bg-neutral-700", "Message"),
		u.Ccv("th", "w-1/2 px-4 py-3 text-left text-base font-medium text-neutral-400 uppercase tracking-wider bg-neutral-700", "KVT"),
	}
	headerRow := u.Ccs("tr", "", headerCells...)

	var dataRows []goc.HTML
	for i, log := range logs {
		levelBadge := this.logLevelBadge(log.Level)

		kvtContent := this.logKvtDisplay(log.Kvts)

		dataCells := []goc.HTML{
			u.Ccs("td", "w-32 px-4 py-3 text-base", levelBadge),
			u.Ccv("td", "w-1/2 px-4 py-3 text-base text-neutral-100", log.Text),
			u.Ccs("td", "w-1/2 px-4 py-3 text-base", kvtContent),
		}

		borderClass := "border-b border-neutral-600"
		if i == len(logs)-1 {
			borderClass = ""
		}

		dataRows = append(dataRows, u.Ccs("tr", borderClass, dataCells...))
	}

	return u.Ccs("div", "border border-neutral-600 rounded-lg overflow-hidden",
		u.Ccs("table", "w-full border-collapse",
			u.Ccs("thead", "", headerRow),
			u.Ccs("tbody", "bg-neutral-800", dataRows...),
		),
	)
}

func (this *ErrorView) logLevelBadge(level string) goc.HTML {
	var badgeClass string
	switch strings.ToUpper(level) {
	case "ERROR":
		badgeClass = "px-2 py-1 rounded text-xs font-medium bg-red-900 text-red-200"
	case "WARN", "WARNING":
		badgeClass = "px-2 py-1 rounded text-xs font-medium bg-yellow-900 text-yellow-200"
	case "INFO":
		badgeClass = "px-2 py-1 rounded text-xs font-medium bg-blue-900 text-blue-200"
	case "DEBUG":
		badgeClass = "px-2 py-1 rounded text-xs font-medium bg-gray-900 text-gray-200"
	default:
		badgeClass = "px-2 py-1 rounded text-xs font-medium bg-neutral-900 text-neutral-200"
	}
	return u.Dcv(badgeClass, strings.ToUpper(level))
}

func (this *ErrorView) logKvtDisplay(kvts []logger.KVT) goc.HTML {
	if len(kvts) == 0 {
		return u.Dcv("text-neutral-500", "-")
	}

	var rows []goc.HTML
	for i, kvt := range kvts {
		rows = append(rows, this.logKvtRow(kvt, i, len(kvts)))
	}

	return u.Ccs("table", "w-full border-collapse",
		u.Ccs("tbody", "", rows...),
	)
}

func (this *ErrorView) logKvtRow(kvt logger.KVT, index int, total int) goc.HTML {
	borderClass := "border-b border-neutral-700"
	if index == total-1 {
		borderClass = ""
	}

	var cells []goc.HTML
	cells = append(cells, u.Ccs("td", "w-32 px-2 py-2",
		u.Dcv("text-base font-medium text-neutral-400 bg-neutral-700 border border-neutral-600 rounded px-2 py-1", kvt.Key)))

	if len(kvt.Value) <= 100 {
		var valueContent []goc.HTML
		valueContent = append(valueContent, u.Dcv("text-xs font-mono text-neutral-200", kvt.Value))
		if len(kvt.Tags) > 0 {
			valueContent = append(valueContent, u.Dcv("text-xs text-neutral-500 font-mono mt-1", "["+strings.Join(kvt.Tags, "|")+"]"))
		}
		cells = append(cells, u.Ccs("td", "px-2 py-2", valueContent...))
	} else {
		cells = append(cells, this.logKvtValueCell(kvt))
	}

	return u.Ccs("tr", borderClass, cells...)
}

func (this *ErrorView) logKvtValueCell(kvt logger.KVT) goc.HTML {
	truncated := kvt.Value[:100]
	var content []goc.HTML
	content = append(content, u.Cas("div",
		goc.Attr{
			"class":      "cursor-pointer",
			"x-on:click": "expanded = !expanded",
		},
		u.Cas("div",
			goc.Attr{"x-show": "!expanded"},
			u.Dcs("flex items-center",
				u.Dcv("text-xs font-mono text-neutral-200", truncated),
				u.Dcv("bg-orange-900 text-orange-200 px-1 ml-1 rounded text-xs", "..."),
			),
		),
		u.Cas("div",
			goc.Attr{"x-show": "expanded"},
			u.Dcv("whitespace-pre-wrap text-xs font-mono text-neutral-200", kvt.Value),
		),
	))

	if len(kvt.Tags) > 0 {
		content = append(content, u.Dcv("text-xs text-neutral-500 font-mono mt-1", "["+strings.Join(kvt.Tags, "|")+"]"))
	}

	return u.Cas("td",
		goc.Attr{
			"class":  "px-2 py-2",
			"x-data": "{expanded: false}",
		},
		content...,
	)
}

func (this *ErrorView) kvtTags(tags []string) goc.HTML {
	if len(tags) == 0 {
		return goc.HTML{}
	}
	return u.Dcv("text-xs text-neutral-500 font-mono ml-2", "["+strings.Join(tags, "|")+"]")
}

func (this *ErrorView) banner(mod *ErrorViewMod) goc.HTML {
	return u.Dcs("bg-red-900", this.bannerContent(mod))
}

func (this *ErrorView) bannerContent(mod *ErrorViewMod) goc.HTML {
	return u.Dcs("mx-auto container py-4",
		this.bannerError(mod),
	)
}

func (this *ErrorView) bannerError(mod *ErrorViewMod) goc.HTML {
	if mod.Err2 == nil {
		return u.Dcv("text-2xl font-bold text-white", mod.Err1.Error())
	}

	var content []goc.HTML
	content = append(content, u.Dcv("text-2xl font-bold text-white mb-3", mod.Err2.Text))

	prevTexts := this.collectPrevErrorTexts(mod.Err2.Prev)
	if len(prevTexts) > 0 {
		var prevItems []goc.HTML
		for _, text := range prevTexts {
			prevItems = append(prevItems, u.Dcs("flex items-center space-x-2",
				u.Dcv("text-red-300 text-sm", "↳"),
				u.Dcv("text-lg text-red-100", text),
			))
		}
		content = append(content, u.Dcs("mt-4 space-y-2", prevItems...))
	}

	return u.Dcs("", content...)
}

func (this *ErrorView) collectPrevErrorTexts(err error) []string {
	var texts []string

	for err != nil {
		if prevErr, ok := err.(*erro.Error); ok {
			texts = append(texts, prevErr.Text)
			err = prevErr.Prev
		} else {
			texts = append(texts, err.Error())
			err = nil
		}
	}

	return texts
}

func (this *ErrorView) toggleButton() goc.HTML {
	return u.Cas("button",
		goc.Attr{
			"class":      "px-3 py-2 text-xs font-medium text-neutral-300 bg-neutral-700 hover:bg-neutral-600 border border-neutral-600 hover:border-neutral-500 rounded transition-colors",
			"x-on:click": "open = !open",
		},
		u.Cas("span",
			goc.Attr{"x-show": "!open"},
			u.Dcv("", "Show Code"),
		),
		u.Cas("span",
			goc.Attr{"x-show": "open"},
			u.Dcv("", "Hide Code"),
		),
	)
}

func (this *ErrorView) errorCard(mod *ErrorViewMod) goc.HTML {
	var content []goc.HTML

	if mod.Err2 == nil {
		content = append(content, u.Dcv("text-lg text-neutral-100", mod.Err1.Error()))
	} else {
		content = append(content, this.displayErrorChain(mod.Err2))
	}
	return u.Dcs("", content...)
}

func (this *ErrorView) debugRow(key string, value string) goc.HTML {
	return u.Ccs("tr", "border-b border-neutral-700 last:border-b-0",
		u.Ccs("td", "w-64 px-3 py-2", u.Dcv("text-base font-medium text-neutral-400 bg-neutral-700 border border-neutral-600 rounded px-2 py-1", key)),
		u.Ccv("td", "px-3 py-2 text-base text-neutral-100 font-mono", value),
	)
}

func (this *ErrorView) displayErrorChain(err *erro.Error) goc.HTML {
	var allErrors []*erro.Error
	current := err
	for current != nil {
		allErrors = append(allErrors, current)
		if current.Prev != nil {
			if prevError, ok := current.Prev.(*erro.Error); ok {
				current = prevError
			} else {
				break
			}
		} else {
			break
		}
	}

	var sections []goc.HTML
	for i := len(allErrors) - 1; i >= 0; i-- {
		currentErr := allErrors[i]
		isDeepest := i == len(allErrors)-1
		isTop := i == 0

		sectionContent := this.errorSection(currentErr, isDeepest, isTop)
		sections = append(sections, sectionContent)
	}

	return u.Dcs("flex flex-col space-y-6", sections...)
}

func (this *ErrorView) errorSection(err *erro.Error, isDeepest bool, isTop bool) goc.HTML {
	textClass := "text-lg text-neutral-100"
	badgeClass := "inline-block px-3 py-1 text-xs font-medium rounded-full mb-3 bg-purple-900 text-purple-200"

	header := u.Dcv(textClass, err.Text)

	if isDeepest {
		header = u.Dcs("flex flex-row justify-between items-center mb-3",
			u.Dcv(textClass, err.Text),
			u.Dcv(badgeClass, "deepest"),
		)
	}
	if isTop {
		header = u.Dcs("flex flex-row justify-between items-center mb-3",
			u.Dcv(textClass, err.Text),
			u.Dcv(badgeClass, "top"),
		)
	}

	frame := &Frame{
		File: err.Frame.File,
		Line: err.Frame.Line,
	}
	frame.Code = this.extractCode(frame)

	return u.Dcs("flex flex-col", header, this.frame(frame, 0), this.kvtTable(err.Kvts))
}

func (this *ErrorView) handlePreviousError(prevErr error) goc.HTML {
	if customErr, ok := prevErr.(*erro.Error); ok {
		return this.displayErrorChain(customErr)
	}

	return u.Dcs("mb-6 pb-6 border-t border-neutral-600 pt-6",
		u.Dcv("inline-block px-3 py-1 text-xs font-medium rounded-full mb-3 bg-gray-900 text-gray-200", "BASE ERROR"),
		u.Dcv("text-base text-neutral-100", prevErr.Error()),
	)
}

func (this *ErrorView) kvtTable(kvts []erro.KVT) goc.HTML {
	var rows []goc.HTML

	for i, kvt := range kvts {
		var cells []goc.HTML
		cells = append(cells, u.Ccs("td", "w-64 px-2 py-3", u.Dcv("text-sm font-medium text-neutral-300 bg-neutral-700 border border-neutral-600 rounded px-3 py-2", kvt.Key)))

		var valueContent []goc.HTML
		valueContent = append(valueContent, u.Dcv("text-neutral-100 px-3 py-2 rounded border border-neutral-600 font-mono", kvt.Value))
		if len(kvt.Tags) > 0 {
			valueContent = append(valueContent, u.Dcv("text-xs text-neutral-400 mt-2 font-mono", "["+strings.Join(kvt.Tags, "|")+"]"))
		}

		borderClass := "border-b border-neutral-600"
		if i == len(kvts)-1 {
			borderClass = ""
		}

		cells = append(cells, u.Ccs("td", "px-3 py-3 text-sm", valueContent...))

		rows = append(rows, u.Ccs("tr", borderClass, cells...))
	}

	return u.Ccs("table", "w-full mt-4 border-collapse table-fixed rounded-lg overflow-hidden",
		u.Ccs("tbody", "", rows...),
	)
}

func (this *ErrorView) main(mod *ErrorViewMod) goc.HTML {
	return u.Ccs(
		"main",
		"mx-auto container",
		this.content(mod),
	)
}

func (this *ErrorView) content(mod *ErrorViewMod) goc.HTML {
	return this.tabSystem(mod)
}

func (this *ErrorView) tabSystem(mod *ErrorViewMod) goc.HTML {
	return u.Cas("div",
		goc.Attr{
			"class":  "mt-8",
			"x-data": "{activeTab: 'error'}",
		},
		this.tabNavigation(),
		this.tabContent(mod),
	)
}

func (this *ErrorView) tabNavigation() goc.HTML {
	return u.Dcs("flex border-b border-neutral-600 mb-6",
		this.tab("error", "Error"),
		this.tab("stack", "Stack Trace"),
		this.tab("logs", "Logs"),
		this.tab("record", "Record"),
		this.tab("http", "HTTP"),
		this.tab("routing", "Routing"),
	)
}

func (this *ErrorView) tab(tabId string, label string) goc.HTML {
	return u.Cas("button",
		goc.Attr{
			"class":        "px-6 py-3 text-sm font-medium transition-colors",
			"x-bind:class": "activeTab === '" + tabId + "' ? 'border-b-2 border-red-500 text-white' : 'text-neutral-400 hover:text-neutral-200'",
			"x-on:click":   "activeTab = '" + tabId + "'",
		},
		u.Dcv("", label),
	)
}

func (this *ErrorView) tabContent(mod *ErrorViewMod) goc.HTML {
	return u.Ds(
		this.tabPanel("error", this.errorCard(mod)),
		this.tabPanel("stack", this.stack(mod)),
		this.tabPanel("logs", this.logs(mod)),
		this.tabPanel("record", this.record(mod)),
		this.tabPanel("http", this.httpInfo(mod)),
		this.tabPanel("routing", this.routingInfo(mod)),
	)
}

func (this *ErrorView) tabPanel(tabId string, content goc.HTML) goc.HTML {
	return u.Cas("div",
		goc.Attr{
			"x-show": "activeTab === '" + tabId + "'",
		},
		content,
	)
}

func (this *ErrorView) getCurrentStack() []*Frame {
	var stack []*Frame
	// Skip the expected number of frames
	for i := 8; ; i++ {
		_, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		frame := &Frame{
			File: file,
			Line: line,
		}
		frame.Code = this.extractCode(frame)
		stack = append(stack, frame)
	}
	return stack
}

func (this *ErrorView) frame(frame *Frame, i int) goc.HTML {
	codeOpen := "false"
	if i < 5 && this.showFrame(frame) {
		codeOpen = "true"
	}

	return u.Cas("div",
		goc.Attr{
			"class":  "bg-neutral-800 border border-neutral-600 rounded-lg p-6 shadow-sm",
			"x-data": "{open:" + codeOpen + "}",
		},
		this.frameTitle(frame, i),
		this.frameCode(frame),
	)
}

func (this *ErrorView) frameMeta(frame *Frame, index int) goc.HTML {
	return u.Dcs("flex items-center justify-between text-sm text-neutral-400 mb-4",
		u.Dcs("flex items-center space-x-3",
			u.Dcv("bg-neutral-700 px-2 py-1 rounded text-xs font-mono", "#"+strconv.Itoa(index)),
			this.frameLocation(frame),
		),
		u.Dcv("text-xs", "Line "+strconv.Itoa(frame.Line)),
	)
}

func (this *ErrorView) frameLocation(frame *Frame) goc.HTML {
	fileName := strings.TrimPrefix(frame.File, this.Config.GetRootDir()+"/")
	return u.Dcv("font-mono text-neutral-300", fileName)
}

func (this *ErrorView) codeLine(line string) goc.HTML {
	css := "text-sm font-mono leading-relaxed"
	if strings.HasPrefix(line, HIDDEN_CHARACTER) {
		css += " bg-yellow-900 bg-opacity-50 -mx-2 px-2 py-1 rounded"
	} else {
		css += " text-neutral-300"
	}

	return u.Ccv("pre", css, line)
}

func (this *ErrorView) frameCode(frame *Frame) goc.HTML {
	var subs []goc.HTML

	for _, line := range strings.Split(frame.Code, "\n") {
		subs = append(subs, this.codeLine(line))
	}

	return u.Cas("div",
		goc.Attr{
			"class":  "bg-neutral-900 border border-neutral-700 rounded-lg mt-4 overflow-hidden",
			"x-show": "open",
		},
		u.Dcs("p-4 space-y-1", subs...),
	)
}

func (this *ErrorView) stack(mod *ErrorViewMod) goc.HTML {
	stackOptions := this.getStackOptions(mod)
	if len(stackOptions) == 0 {
		return u.Dcv("text-neutral-400 text-center py-8", "No stack trace available")
	}

	defaultStack := this.getDefaultStack(stackOptions)

	var stackSelectors []goc.HTML
	for i, option := range stackOptions {
		isDefault := option.label == defaultStack
		stackSelectors = append(stackSelectors, this.stackSelector(option, i, isDefault))
	}

	var stackDisplays []goc.HTML
	for i, option := range stackOptions {
		isDefault := option.label == defaultStack
		stackDisplays = append(stackDisplays, this.stackDisplay(option, i, isDefault))
	}

	return u.Cas("div",
		goc.Attr{"x-data": "{ selectedStack: '" + defaultStack + "' }"},
		u.Dcs("space-y-4",
			u.Dcs("flex flex-wrap gap-2", stackSelectors...),
			u.Dcs("", stackDisplays...),
		),
	)
}

func (this *ErrorView) getStackOptions(mod *ErrorViewMod) []StackOption {
	var options []StackOption

	if mod.Err2 != nil && len(mod.Err2.Stack) > 0 {
		options = append(options, StackOption{
			label:  "error",
			frames: this.convertErrorStackToFrames(mod.Err2.Stack),
		})
	}

	options = append(options, StackOption{
		label:  "recover",
		frames: this.getCurrentStack(),
	})

	return options
}

func (this *ErrorView) convertErrorStackToFrames(stack []*erro.StackFrame) []*Frame {
	var frames []*Frame
	for _, stackFrame := range stack {
		if stackFrame == nil {
			continue
		}
		frame := &Frame{
			File: stackFrame.File,
			Line: stackFrame.Line,
		}
		frame.Code = this.extractCode(frame)
		frames = append(frames, frame)
	}
	return frames
}

func (this *ErrorView) getDefaultStack(options []StackOption) string {
	if len(options) == 0 {
		return ""
	}

	for _, option := range options {
		if option.label == "error" {
			return "error"
		}
	}

	return options[0].label
}

func (this *ErrorView) stackSelector(option StackOption, index int, isDefault bool) goc.HTML {
	classes := "px-3 py-1 text-sm rounded border cursor-pointer transition-colors"
	if isDefault {
		classes += " bg-neutral-700 border-neutral-600 text-white"
	} else {
		classes += " bg-neutral-800 border-neutral-700 text-neutral-300 hover:bg-neutral-700"
	}

	return u.Cas("button",
		goc.Attr{
			"class":  classes,
			"@click": "selectedStack = '" + option.label + "'",
			":class": "selectedStack === '" + option.label + "' ? 'bg-neutral-700 border-neutral-600 text-white' : 'bg-neutral-800 border-neutral-700 text-neutral-300 hover:bg-neutral-700'",
		},
		u.Dcv("", option.label),
	)
}

func (this *ErrorView) stackDisplay(option StackOption, index int, isDefault bool) goc.HTML {
	var subs []goc.HTML
	i := 0
	for _, frame := range option.frames {
		if this.isSkippedFrame(frame) {
			continue
		}
		subs = append(subs, this.frame(frame, i))
		i++
	}

	if len(subs) == 0 {
		return u.Cas("div",
			goc.Attr{
				"x-show": "selectedStack === '" + option.label + "'",
			},
			u.Dcv("text-neutral-400 text-center py-8", "No frames available"),
		)
	}

	return u.Cas("div",
		goc.Attr{
			"x-show": "selectedStack === '" + option.label + "'",
		},
		u.Dcs("space-y-4", subs...),
	)
}

const HIDDEN_CHARACTER = " "

func (this *ErrorView) extractCode(frame *Frame) string {
	b, err := os.ReadFile(frame.File)
	if err != nil {
		return ""
	}
	s := ""
	lines := strings.Split(string(b), "\n")
	for i := 0; i < len(lines); i++ {
		if i < frame.Line-6 || i > frame.Line+5 {
			continue
		}
		prefix := " "
		if frame.Line == i+1 {
			prefix = HIDDEN_CHARACTER
		}
		prefix += strconv.Itoa(i+1) + ". "

		s += prefix + lines[i] + "\n"
	}
	return s
}

func (this *ErrorView) frameTitle(frame *Frame, index int) goc.HTML {
	return u.Dcs("flex justify-between items-center mb-3",
		u.Dcs("flex items-center space-x-3",
			u.Dcv("bg-neutral-700 px-2 py-1 rounded text-xs font-mono text-neutral-400", "#"+strconv.Itoa(index)),
			this.frameLocation(frame),
			u.Dcv("text-xs text-neutral-500", "Line "+strconv.Itoa(frame.Line)),
		),
		u.Dcs("flex items-center space-x-2",
			this.toggleButton(),
			this.openInIdeButton(frame.File+":"+strconv.Itoa(frame.Line)),
		),
	)
}

func (this *ErrorView) isSkippedFrame(frame *Frame) bool {
	return strings.Contains(frame.File, "/go/pkg/mod/golang.org/toolchain@") ||
		strings.Contains(frame.File, "/go/src/runtime/")
}

func (this *ErrorView) showFrame(frame *Frame) bool {
	if strings.HasSuffix(frame.File, "/src/lib/middlewares/recovery_middleware.go") {
		return false
	}
	if strings.HasSuffix(frame.File, "/vendor/github.com/gin-gonic/gin/recovery.go") && frame.Line == 98 {
		return false
	}
	if strings.HasSuffix(frame.File, "/src/lib/lib.go") && frame.Line == 9 {
		return false
	}
	return true
}

func (this *ErrorView) httpInfo(mod *ErrorViewMod) goc.HTML {
	var sections []goc.HTML

	if requestSection := this.httpRequestSection(mod); this.hasRequestInfo(mod) {
		sections = append(sections, requestSection)
	}
	if routeSection := this.httpRouteSection(mod); this.hasRouteInfo(mod) {
		sections = append(sections, routeSection)
	}
	if paramsSection := this.httpParamsSection(mod); len(mod.QueryParams) > 0 {
		sections = append(sections, paramsSection)
	}
	if formSection := this.httpFormDataSection(mod); len(mod.FormData) > 0 {
		sections = append(sections, formSection)
	}
	if headersSection := this.httpHeadersSection(mod); this.hasHeaderInfo(mod) {
		sections = append(sections, headersSection)
	}

	if len(sections) == 0 {
		return u.Dcv("text-neutral-400 text-center py-8", "No HTTP information available")
	}

	return u.Dcs("space-y-6", sections...)
}

func (this *ErrorView) hasRequestInfo(mod *ErrorViewMod) bool {
	return mod.RequestMethod != "" || mod.RequestURL != "" || mod.RequestID != "" ||
		mod.ErrorTime != "" || mod.RequestDuration != "" || mod.UserAgent != ""
}

func (this *ErrorView) hasRouteInfo(mod *ErrorViewMod) bool {
	return mod.RoutePath != "" || mod.HandlerName != "" || mod.MaeName != ""
}

func (this *ErrorView) hasHeaderInfo(mod *ErrorViewMod) bool {
	importantHeaders := []string{"Content-Type", "Accept", "Host", "Referer"}
	for _, headerName := range importantHeaders {
		if _, exists := mod.Headers[headerName]; exists {
			return true
		}
	}
	return false
}

func (this *ErrorView) httpRequestSection(mod *ErrorViewMod) goc.HTML {
	var rows []goc.HTML

	if mod.RequestMethod != "" {
		rows = append(rows, this.httpRow("Method", mod.RequestMethod))
	}
	if mod.RequestURL != "" {
		rows = append(rows, this.httpRow("URL", mod.RequestURL))
	}
	if mod.RequestID != "" {
		rows = append(rows, this.httpRow("Request ID", mod.RequestID))
	}
	if mod.ErrorTime != "" {
		rows = append(rows, this.httpRow("Error Time", mod.ErrorTime))
	}
	if mod.RequestDuration != "" {
		rows = append(rows, this.httpRow("Duration", mod.RequestDuration))
	}
	if mod.UserAgent != "" {
		rows = append(rows, this.httpRow("User Agent", mod.UserAgent))
	}

	if len(rows) == 0 {
		return goc.HTML{}
	}

	return u.Dcs("bg-neutral-800 border border-neutral-600 rounded-lg p-6",
		u.Dcv("text-lg font-semibold text-white mb-4", "Request Information"),
		u.Ccs("table", "w-full border-collapse",
			u.Ccs("tbody", "", rows...),
		),
	)
}

func (this *ErrorView) httpRouteSection(mod *ErrorViewMod) goc.HTML {
	var rows []goc.HTML

	if mod.RoutePath != "" {
		rows = append(rows, this.httpRow("Route Path", mod.RoutePath))
	}
	if mod.HandlerName != "" {
		rows = append(rows, this.httpRow("Handler", mod.HandlerName))
	}
	if mod.MaeName != "" {
		rows = append(rows, this.httpRow("Mae Service", mod.MaeName))
	}

	if len(rows) == 0 {
		return goc.HTML{}
	}

	return u.Dcs("bg-neutral-800 border border-neutral-600 rounded-lg p-6",
		u.Dcv("text-lg font-semibold text-white mb-4", "Route Information"),
		u.Ccs("table", "w-full border-collapse",
			u.Ccs("tbody", "", rows...),
		),
	)
}

func (this *ErrorView) httpParamsSection(mod *ErrorViewMod) goc.HTML {
	var rows []goc.HTML

	for key, value := range mod.RouteParams {
		rows = append(rows, this.httpRow(key, value))
	}

	for key, value := range mod.QueryParams {
		rows = append(rows, this.httpRow(key, value))
	}

	if len(rows) == 0 {
		return goc.HTML{}
	}

	return u.Dcs("bg-neutral-800 border border-neutral-600 rounded-lg p-6",
		u.Dcv("text-lg font-semibold text-white mb-4", "Query Parameters"),
		u.Ccs("table", "w-full border-collapse",
			u.Ccs("tbody", "", rows...),
		),
	)
}

func (this *ErrorView) httpFormDataSection(mod *ErrorViewMod) goc.HTML {
	var rows []goc.HTML

	for key, value := range mod.FormData {
		rows = append(rows, this.httpRow(key, value))
	}

	if len(rows) == 0 {
		return goc.HTML{}
	}

	return u.Dcs("bg-neutral-800 border border-neutral-600 rounded-lg p-6",
		u.Dcv("text-lg font-semibold text-white mb-4", "Form Data"),
		u.Ccs("table", "w-full border-collapse",
			u.Ccs("tbody", "", rows...),
		),
	)
}

func (this *ErrorView) httpHeadersSection(mod *ErrorViewMod) goc.HTML {
	var rows []goc.HTML

	importantHeaders := []string{"Content-Type", "Accept", "Host", "Referer"}
	for _, headerName := range importantHeaders {
		if value, exists := mod.Headers[headerName]; exists {
			rows = append(rows, this.httpRow(headerName, value))
		}
	}

	if len(rows) == 0 {
		return goc.HTML{}
	}

	return u.Dcs("bg-neutral-800 border border-neutral-600 rounded-lg p-6",
		u.Dcv("text-lg font-semibold text-white mb-4", "Headers"),
		u.Ccs("table", "w-full border-collapse",
			u.Ccs("tbody", "", rows...),
		),
	)
}

func (this *ErrorView) httpRow(key, value string) goc.HTML {
	return u.Ccs("tr", "border-b border-neutral-700",
		u.Ccs("td", "w-64 px-3 py-3", u.Dcv("text-base font-medium text-neutral-400 bg-neutral-700 border border-neutral-600 rounded px-2 py-1", key)),
		u.Ccv("td", "px-3 py-3 text-base text-neutral-100 font-mono", value),
	)
}

func (this *ErrorView) routingInfo(mod *ErrorViewMod) goc.HTML {
	var routingRows []goc.HTML

	if mod.RouteMatched != "" {
		routingRows = append(routingRows, this.routingRow("Route", mod.RouteMatchedPath))
	}
	if mod.MaeMatched != "" {
		routingRows = append(routingRows, this.routingRow("Mae", mod.MaeMatchedPath))
	}

	if len(mod.MiddlewareMatched) > 0 {
		var middlewareItems []goc.HTML
		for i := range mod.MiddlewareMatched {
			path := ""
			if i < len(mod.MiddlewarePaths) {
				path = mod.MiddlewarePaths[i]
			}
			middlewareItems = append(middlewareItems, u.Dcs("flex justify-between items-center py-1",
				u.Dcv("font-mono text-neutral-100", this.trimPath(path)),
				this.openInIdeButton(path),
			))
		}

		routingRows = append(routingRows, u.Ccs("tr", "border-b border-neutral-700",
			u.Ccs("td", "w-64 px-3 py-2",
				u.Dcv("text-base font-medium text-neutral-400 bg-neutral-700 border border-neutral-600 rounded px-2 py-1", "Middlewares"),
			),
			u.Ccs("td", "px-3 py-2 text-base",
				u.Dcs("space-y-1", middlewareItems...),
			),
		))
	}

	if len(routingRows) == 0 {
		return u.Dcv("text-neutral-400 text-center py-8", "No routing information available")
	}

	return u.Dcs("bg-neutral-800 border border-neutral-600 rounded-lg p-6",
		u.Dcv("text-lg font-semibold text-white mb-4", "Routing Information"),
		u.Ccs("table", "w-full border-collapse",
			u.Ccs("tbody", "", routingRows...),
		),
	)
}

func (this *ErrorView) routingRow(key, filePath string) goc.HTML {
	return u.Ccs("tr", "border-b border-neutral-700",
		u.Ccs("td", "w-64 px-3 py-2",
			u.Dcv("text-base font-medium text-neutral-400 bg-neutral-700 border border-neutral-600 rounded px-2 py-1", key),
		),
		u.Ccs("td", "px-3 py-2 text-base",
			u.Dcs("flex justify-between items-center",
				u.Dcv("font-mono text-base text-neutral-100", this.trimPath(filePath)),
				this.openInIdeButton(filePath),
			),
		),
	)
}

func (this *ErrorView) trimPath(filePath string) string {
	if filePath == "" {
		return "N/A"
	}
	return strings.TrimPrefix(filePath, this.Config.GetRootDir()+"/")
}

func (this *ErrorView) openInIdeButton(filePath string) goc.HTML {
	if filePath == "" {
		return goc.HTML{}
	}

	link := "http://localhost:8077/app/perform-open-filepath"

	return u.Cas("form",
		goc.Attr{
			"method": "POST",
			"action": link,
			"class":  "inline",
			"target": "_blank",
		},
		u.Cas("input",
			goc.Attr{
				"type":  "hidden",
				"name":  "form[filepath]",
				"value": filePath,
			},
		),
		u.Cas("input",
			goc.Attr{
				"type":  "hidden",
				"name":  "form[via]",
				"value": "ide",
			},
		),
		u.Cas("button",
			goc.Attr{
				"type":  "submit",
				"class": "px-3 py-2 text-xs font-medium text-neutral-300 bg-neutral-700 hover:bg-neutral-600 border border-neutral-600 hover:border-neutral-500 rounded transition-colors",
				"title": "Open in IDE: " + filePath,
			},
			u.Dcv("", "Open in IDE"),
		),
	)
}
