package util_test

import (
	"strings"
	"testing"
	"unicode/utf8"

	"galaxy/util"

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

func TestValidateString(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected string
		ok       bool
	}{
		// Basic cases
		{
			name:     "Valid string with Latin characters and digits",
			input:    "Hello_World-123",
			expected: "Hello_World-123",
			ok:       true,
		},
		{
			name:     "Valid string with Cyrillic characters",
			input:    "Привет_мир-42",
			expected: "Привет_мир-42",
			ok:       true,
		},
		{
			name:     "Valid Greek alphabet string",
			input:    "Αλφα_Βητα-2024",
			expected: "Αλφα_Βητα-2024",
			ok:       true,
		},
		{
			name:     "Valid Arabic alphabet string",
			input:    "مرحبا_العالم-7",
			expected: "مرحبا_العالم-7",
			ok:       true,
		},
		{
			name:     "Valid Japanese Katakana string",
			input:    "テスト_ケース-1",
			expected: "テスト_ケース-1",
			ok:       true,
		},
		{
			name:     "Valid Chinese characters",
			input:    "你好_世界-123", // "Hello World" in Chinese
			expected: "你好_世界-123",
			ok:       true,
		},
		{
			name:     "Valid Hindi characters",
			input:    "नमस्ते_दुनिया-456", // "Hello World" in Hindi
			expected: "नमस्ते_दुनिया-456",
			ok:       true,
		},
		{
			name:     "Valid Thai characters",
			input:    "สวัสดี_โลก-789", // "Hello World" in Thai
			expected: "สวัสดี_โลก-789",
			ok:       true,
		},
		{
			name:     "Valid Korean characters",
			input:    "안녕하세요_세계-101", // "Hello World" in Korean
			expected: "안녕하세요_세계-101",
			ok:       true,
		},
		{
			name:     "Valid Hebrew characters",
			input:    "שלום_עולם-202", // "Hello World" in Hebrew
			expected: "שלום_עולם-202",
			ok:       true,
		},
		// Special characters test cases
		{
			name:     "Valid special character @",
			input:    "Test@Name",
			expected: "Test@Name",
			ok:       true,
		},
		{
			name:     "Valid special character ^",
			input:    "Test^Name",
			expected: "Test^Name",
			ok:       true,
		},
		{
			name:     "Valid special character ~",
			input:    "Test~Name",
			expected: "Test~Name",
			ok:       true,
		},
		// Edge cases
		{
			name:     "Spaces are trimmed from both ends",
			input:    "   Test123_Name   ",
			expected: "Test123_Name",
			ok:       true,
		},
		{
			name:     "Spaces in the middle are not allowed",
			input:    "Test 123",
			expected: "",
			ok:       false,
		},
		{
			name:     "Tab character in the middle is not allowed",
			input:    "Test\tName",
			expected: "",
			ok:       false,
		},
		{
			name:     "Newline character is not allowed",
			input:    "Test\nName",
			expected: "",
			ok:       false,
		},
		{
			name:     "Dash at the beginning after TrimSpace is not allowed",
			input:    "  -Test123",
			expected: "",
			ok:       false,
		},
		{
			name:     "Dash at the end after TrimSpace is not allowed",
			input:    "Test123-   ",
			expected: "",
			ok:       false,
		},
		{
			name:     "Emoji is not allowed",
			input:    "Test🙂Name",
			expected: "",
			ok:       false,
		},
		{
			name:     "String containing only spaces",
			input:    "     ",
			expected: "",
			ok:       false,
		},
		{
			name:     "Empty string",
			input:    "",
			expected: "",
			ok:       false,
		},
		{
			name:     "Too long string",
			input:    "ValidatedStringHasTooManyCharacters",
			expected: "",
			ok:       false,
		},
		{
			name:     "Valid consecutive special chars",
			input:    "Valid_(special)_Chars",
			expected: "Valid_(special)_Chars",
			ok:       true,
		},
		{
			name:     "Too many consecutive special chars",
			input:    "Too_Many_(special[_]Chars",
			expected: "",
			ok:       false,
		},
		{
			name:     "Special char at the beginning",
			input:    "$pecialString",
			expected: "",
			ok:       false,
		},
		{
			name:     "Special char at the end",
			input:    "SpecialString_",
			expected: "",
			ok:       false,
		},
		{
			name:     "All valid special chars",
			input:    "A@#b$%c^*d-_e=+f~(g)[h]{i}j",
			expected: "A@#b$%c^*d-_e=+f~(g)[h]{i}j",
			ok:       true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, ok := util.ValidateTypeName(tt.input)
			assert.Equal(t, tt.ok, ok)
			assert.Equal(t, tt.expected, result)
		})
	}
}

// Fuzz test for ValidateString function
func FuzzValidateString(f *testing.F) {
	// Adding a few basic strings to start the fuzz test
	f.Add("Hello_World-123")
	f.Add("Test@Name")
	f.Add("Привет_мир-42")
	f.Add("αβγ@~")
	f.Add("مرحبا_العالم-7")

	// Fuzz function
	f.Fuzz(func(t *testing.T, input string) {
		// Call the function and check if the result matches expectations
		result, ok := util.ValidateTypeName(input)

		// Check if the string is non-empty and valid UTF-8
		if len(input) > 0 {
			if !utf8.ValidString(input) {
				t.Errorf("Error: string is not a valid UTF-8 string: %s", input)
			}
		}

		// If the string is empty, ok should be false
		if len(result) == 0 {
			if ok {
				t.Errorf("Expected false for invalid string, but got true: %s", input)
			}
		} else {
			// If the result is not empty, ok should be true
			if !ok {
				t.Errorf("Expected true for valid string, but got false: %s", input)
			}
		}

		// Additional check: if input has spaces at the beginning or end, it should fail
		if input[0] == ' ' || input[len(input)-1] == ' ' {
			if ok {
				t.Errorf("Error: string contains spaces at the beginning or end: %s", input)
			}
		}
	})
}

func TestAppendRandomSuffixGenerator(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		suffix   string
		expected string
	}{
		{
			name:     "Regular String",
			input:    "Regular_String",
			suffix:   "1234",
			expected: "Regular_String1234",
		},
		{
			name:     "Zero Length String",
			input:    "",
			suffix:   "1234",
			expected: "1234",
		},
		{
			name:     "Edge Case String len=28",
			input:    "Edge_Case_String_ABCDEFGHIGK",
			suffix:   "1234",
			expected: "Edge_Case_String_ABCDEFGHI1234",
		},
		{
			name:     "Extra Long String len=31",
			input:    "Extra_Long_String_ABCDEFGHIGKLM",
			suffix:   "1234",
			expected: "Extra_Long_String_ABCDEFGH1234",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := util.AppendRandomSuffixGenerator(tt.input, func() string { return tt.suffix })
			assert.Equal(t, tt.expected, result)
		})
	}
}

func TestRandomSuffixGenerator(t *testing.T) {
	// The generator draws from a ~10 000 element space (Intn(9999)
	// formatted as four digits). Comparing each sample against the
	// previous one with NotEqual flaked ~1 % per 100-iteration run on
	// natural collisions. Count unique values instead — if the
	// generator ever gets stuck on a tiny range we still catch it,
	// without depending on the birthday paradox not firing today.
	const samples = 200
	seen := make(map[string]struct{}, samples)
	for range samples {
		s := util.RandomSuffixGenerator()
		assert.Len(t, s, 4)
		assert.True(t, strings.ContainsFunc(s, func(r rune) bool { return r >= '0' && r <= '9' }))
		seen[s] = struct{}{}
	}
	// In 200 draws from ~10 000 the expected number of unique values
	// is ~198; a stuck generator (single value) would land at 1, a
	// 256-value range at ~196. 150 is well above the floor either
	// way and well below the expected mean.
	assert.GreaterOrEqual(t, len(seen), 150,
		"RandomSuffixGenerator drew from too small a range over %d samples", samples)
}
