package app

import (
	"strings"
	"sync"

	"github.com/charmbracelet/glamour"
)

// defaultMarkdownWidth is the wrap width used when the caller doesn't yet know
// the terminal width (e.g. a zero/negative value). 80 columns is a safe terminal
// default that never blows up glamour's word-wrap.
const defaultMarkdownWidth = 80

// markdownRenderers memoizes one glamour renderer per wrap width. A glamour
// renderer bakes its word-wrap width in at construction, so we key the cache by
// width and build lazily. Constructing a renderer is comparatively expensive
// (it compiles a style + goldmark parser), hence the cache; the TUI renders the
// same handful of widths over its lifetime.
var (
	markdownMu        sync.Mutex
	markdownRenderers = map[int]*glamour.TermRenderer{}
)

// rendererForWidth returns a cached dark-styled glamour renderer wrapped to w,
// or nil if glamour could not build one (caller falls back to plain text).
func rendererForWidth(w int) *glamour.TermRenderer {
	markdownMu.Lock()
	defer markdownMu.Unlock()
	if r, ok := markdownRenderers[w]; ok {
		return r
	}
	// Pin the "dark" standard style so output is deterministic and independent
	// of the ambient terminal's background detection (important for tests and
	// for headless/over-the-wire sessions).
	r, err := glamour.NewTermRenderer(
		glamour.WithStandardStyle("dark"),
		glamour.WithWordWrap(w),
	)
	if err != nil {
		// Cache the failure as nil so we don't retry-and-fail every frame.
		markdownRenderers[w] = nil
		return nil
	}
	markdownRenderers[w] = r
	return r
}

// renderMarkdown renders content as terminal markdown (bold, italics, lists,
// headings, code blocks/inline code) word-wrapped to width, styled for a dark
// terminal. It is robust by construction: on any glamour error — or if the
// renderer can't be built — it returns the original plain content rather than
// crashing the TUI. Trailing whitespace glamour appends is trimmed so it doesn't
// disrupt the surrounding layout.
func renderMarkdown(content string, width int) string {
	if width < 1 {
		width = defaultMarkdownWidth
	}
	r := rendererForWidth(width)
	if r == nil {
		return content
	}
	out, err := r.Render(content)
	if err != nil {
		return content
	}
	// glamour pads block output with leading/trailing blank lines; strip them so
	// the rendered turn slots into the feed without extra gaps.
	out = strings.Trim(out, "\n")
	if strings.TrimSpace(out) == "" {
		// Nothing survived rendering (e.g. content was only whitespace) — prefer
		// the original so callers never lose the underlying text.
		return content
	}
	return out
}
