package app

import (
	"strings"
	"testing"
)

// stripANSI removes CSI escape sequences so we can assert on the visible text
// glamour produced without depending on exact styling codes.
func stripANSI(s string) string {
	var b strings.Builder
	for i := 0; i < len(s); {
		if s[i] == 0x1b && i+1 < len(s) && s[i+1] == '[' {
			j := i + 2
			for j < len(s) && (s[j] < 0x40 || s[j] > 0x7e) {
				j++
			}
			if j < len(s) {
				j++ // consume the final byte
			}
			i = j
			continue
		}
		b.WriteByte(s[i])
		i++
	}
	return b.String()
}

// Rich markdown should be transformed (output differs from raw input) while the
// literal words survive the round-trip.
func TestRenderMarkdownTransformsAndPreservesText(t *testing.T) {
	in := "# Heading\n\nSome **bold** and *italic* text.\n\n- first item\n- second item\n\n`inline code` and:\n\n```go\nfmt.Println(\"hi\")\n```\n"
	out := renderMarkdown(in, 80)

	if out == in {
		t.Fatalf("markdown input should be rendered, got identical output")
	}

	plain := stripANSI(out)
	for _, want := range []string{"Heading", "bold", "italic", "first item", "second item", "inline code", "fmt.Println"} {
		if !strings.Contains(plain, want) {
			t.Errorf("rendered output missing %q\n--- visible ---\n%s", want, plain)
		}
	}
}

// A plain, short string must round-trip to contain its text and not panic,
// regardless of how much (or how little) styling glamour adds.
func TestRenderMarkdownPlainStringRoundTrips(t *testing.T) {
	in := "just a short plain sentence"
	out := renderMarkdown(in, 80)
	if !strings.Contains(stripANSI(out), in) {
		t.Fatalf("plain string did not survive rendering: %q", stripANSI(out))
	}
}

// Pathological / empty inputs must never panic and must return something
// containing the input text (trivially true for empty input).
func TestRenderMarkdownPathologicalDoesNotPanic(t *testing.T) {
	cases := []string{
		"",
		"   ",
		"\n\n\n",
		"```unterminated code fence\nstill going",
		strings.Repeat("a", 10_000),
		"[](()]][[**__",
	}
	for _, in := range cases {
		func() {
			defer func() {
				if r := recover(); r != nil {
					t.Fatalf("renderMarkdown panicked on %q: %v", in, r)
				}
			}()
			out := renderMarkdown(in, 40)
			// Empty/whitespace-only inputs fall back to the original content.
			if strings.TrimSpace(in) == "" {
				if out != in {
					t.Errorf("whitespace input %q should fall back to itself, got %q", in, out)
				}
			}
		}()
	}
}

// A non-positive width must not crash and should fall back to the default
// wrap width rather than feeding glamour an invalid value.
func TestRenderMarkdownZeroWidthFallsBack(t *testing.T) {
	out := renderMarkdown("**hello** world", 0)
	if !strings.Contains(stripANSI(out), "hello") {
		t.Fatalf("zero-width render dropped text: %q", stripANSI(out))
	}
}
