package managed_resources //nolint:staticcheck

import (
	"bytes"
	"testing"
	"text/template"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestRendererFuncMap_Interface(t *testing.T) {
	// GIVEN
	expectedFuncs := []string{"lower", "substr", "replace", "trimSuffix", "trimPrefix", "slugify"}

	for _, funcName := range expectedFuncs {
		// THEN
		assert.Contains(t, rendererFuncMap, funcName)
	}
}

func TestRendererFunc_substr(t *testing.T) {
	// GIVEN
	tests := []struct {
		name     string
		start    int
		end      int
		input    string
		expected string
	}{
		{"normal case", 1, 4, "hello", "ell"},
		{"negative start", -1, 3, "hello", "hel"},
		{"negative end", 1, -1, "hello", "ello"},
		{"end out of bounds", 1, 10, "hello", "ello"},
		{"start equals end", 2, 2, "hello", ""},
		{"empty string", 0, 1, "", ""},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// WHEN
			actual := substr(tt.start, tt.end, tt.input)

			// THEN
			assert.Equal(t, tt.expected, actual)
		})
	}
}

func TestRendererFunc_replace(t *testing.T) {
	// GIVEN
	tests := []struct {
		name     string
		old      string
		new      string
		input    string
		expected string
	}{
		{"simple replace", "o", "0", "hello", "hell0"},
		{"multiple occurrences", "l", "x", "hello", "hexxo"},
		{"no match", "z", "x", "hello", "hello"},
		{"empty does insert", "", "x", "hello", "xhxexlxlxox"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// WHEN
			actual := replace(tt.old, tt.new, tt.input)

			// THEN
			assert.Equal(t, tt.expected, actual)
		})
	}
}

func TestRendererFunc_trimSuffix(t *testing.T) {
	// GIVEN
	tests := []struct {
		name     string
		suffix   string
		input    string
		expected string
	}{
		{"trim existing suffix", ".txt", "file.txt", "file"},
		{"no matching suffix", ".doc", "file.txt", "file.txt"},
		{"empty suffix", "", "hello", "hello"},
		{"suffix longer than string", "verylongstring", "hi", "hi"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// WHEN
			actual := trimSuffix(tt.suffix, tt.input)

			// THEN
			assert.Equal(t, tt.expected, actual)
		})
	}
}

func TestRendererFunc_trimPrefix(t *testing.T) {
	// GIVEN
	tests := []struct {
		name     string
		prefix   string
		input    string
		expected string
	}{
		{"trim existing prefix", "pre_", "pre_fix", "fix"},
		{"no matching prefix", "post_", "pre_fix", "pre_fix"},
		{"empty prefix", "", "hello", "hello"},
		{"prefix longer than string", "verylongstring", "hi", "hi"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// WHEN
			actual := trimPrefix(tt.prefix, tt.input)

			// THEN
			assert.Equal(t, tt.expected, actual)
		})
	}
}

func TestRendererFunc_slugify(t *testing.T) {
	// GIVEN
	tests := []struct {
		name     string
		input    string
		expected string
	}{
		// Basic cases
		{"simple lowercase", "hello", "hello"},
		{"uppercase to lowercase", "HELLO", "hello"},
		{"mixed case", "HelloWorld", "helloworld"},

		// Special characters
		{"spaces to hyphens", "hello world", "hello-world"},
		{"underscores to hyphens", "hello_world", "hello-world"},
		{"dots to hyphens", "hello.world", "hello-world"},
		{"mixed special chars", "hello@world#test!", "hello-world-test"},

		// Multiple consecutive special chars
		{"multiple spaces", "hello   world", "hello---world"},
		{"mixed consecutive chars", "hello___...world", "hello------world"},

		// Leading/trailing special chars
		{"leading hyphen", "-hello", "hello"},
		{"trailing hyphen", "hello-", "hello"},
		{"leading and trailing", "-hello-world-", "hello-world"},
		{"only special chars", "!@#$%", ""},

		// Edge cases
		{"empty string", "", ""},
		{"only hyphens", "---", ""},
		{"single char", "a", "a"},
		{"single number", "1", "1"},

		// Long strings (RFC1123 63 char limit)
		{"exactly 63 chars", "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0", "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0"},
		{"over 63 chars", "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01234", "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0"},
		{"long with trailing hyphen", "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz-extra", "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz"},

		// Unicode handling
		{"unicode chars", "héllo-wörld", "h-llo-w-rld"},
		{"emoji", "hello😀world", "hello-world"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// WHEN
			actual, err := slugify(tt.input)
			require.NoError(t, err)

			// THEN
			assert.Equal(t, tt.expected, actual)
		})
	}
}

func TestRendererFunc_slugify_withCutOff(t *testing.T) {
	// GIVEN
	tests := []struct {
		name      string
		input     string
		cutOffLen int
		expected  string
	}{
		// Basic cases
		{"no cut-off", "hello", 0, "hello"},
		{"cut off", "hello", 2, "he"},
		{"cut off larger than input", "hello", 10, "hello"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// WHEN
			actual, err := slugify(tt.cutOffLen, tt.input)
			require.NoError(t, err)

			// THEN
			assert.Equal(t, tt.expected, actual)
		})
	}
}

func TestRendererFunc_slugify_Template(t *testing.T) {
	// GIVEN
	funcMap := template.FuncMap{
		"slugify": slugify,
	}
	tmpl := template.Must(template.New("test").Funcs(funcMap).Parse(`
Pipe usage:
{{ . | slugify }}
{{ . | slugify 9 }}
{{ . | slugify 0 }}

Direct call:
{{ slugify 9 . }}
{{ slugify . }}
`))

	// WHEN
	buf := &bytes.Buffer{}
	err := tmpl.Execute(buf, "This is a VERY long project name with Special-Characters_and.dots that should be slugified properly!")
	require.NoError(t, err)

	// THEN
	assert.Equal(t, `
Pipe usage:
this-is-a-very-long-project-name-with-special-characters-and-do
this-is-a
this-is-a-very-long-project-name-with-special-characters-and-dots-that-should-be-slugified-properly

Direct call:
this-is-a
this-is-a-very-long-project-name-with-special-characters-and-do
`, buf.String())
}
