package interactive

import (
	"strings"
	"testing"
)

// TestIsFirstWord tests the isFirstWord helper function
func TestIsFirstWord(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected bool
	}{
		{
			name:     "single word",
			input:    "select",
			expected: true,
		},
		{
			name:     "two words",
			input:    "select *",
			expected: false,
		},
		{
			name:     "empty string",
			input:    "",
			expected: true,
		},
		{
			name:     "word with trailing space",
			input:    "select ",
			expected: false,
		},
		{
			name:     "multiple spaces",
			input:    "select  from",
			expected: false,
		},
		{
			name:     "unicode characters",
			input:    "選択",
			expected: true,
		},
		{
			name:     "emoji",
			input:    "🔥",
			expected: true,
		},
		{
			name:     "emoji with space",
			input:    "🔥 test",
			expected: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := isFirstWord(tt.input)
			if result != tt.expected {
				t.Errorf("isFirstWord(%q) = %v, want %v", tt.input, result, tt.expected)
			}
		})
	}
}

// TestLastWord tests the lastWord helper function
// Bug: #4787 - lastWord() panics on single word or empty string
func TestLastWord(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "two words",
			input:    "select *",
			expected: " *",
		},
		{
			name:     "multiple words",
			input:    "select * from users",
			expected: " users",
		},
		{
			name:     "trailing space",
			input:    "select * from ",
			expected: " ",
		},
		{
			name:     "unicode",
			input:    "select 你好",
			expected: " 你好",
		},
		{
			name:     "emoji",
			input:    "select 🔥",
			expected: " 🔥",
		},
		{
			name:     "single_word", // #4787
			input:    "select",
			expected: "select",
		},
		{
			name:     "empty_string", // #4787
			input:    "",
			expected: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			defer func() {
				if r := recover(); r != nil {
					t.Errorf("lastWord(%q) panicked: %v", tt.input, r)
				}
			}()

			result := lastWord(tt.input)
			if result != tt.expected {
				t.Errorf("lastWord(%q) = %q, want %q", tt.input, result, tt.expected)
			}
		})
	}
}

// TestLastIndexByteNot tests the lastIndexByteNot helper function
func TestLastIndexByteNot(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		char     byte
		expected int
	}{
		{
			name:     "no matching char",
			input:    "hello",
			char:     ' ',
			expected: 4,
		},
		{
			name:     "trailing spaces",
			input:    "hello   ",
			char:     ' ',
			expected: 4,
		},
		{
			name:     "all spaces",
			input:    "     ",
			char:     ' ',
			expected: -1,
		},
		{
			name:     "empty string",
			input:    "",
			char:     ' ',
			expected: -1,
		},
		{
			name:     "single char not matching",
			input:    "a",
			char:     ' ',
			expected: 0,
		},
		{
			name:     "single char matching",
			input:    " ",
			char:     ' ',
			expected: -1,
		},
		{
			name:     "mixed spaces",
			input:    "hello world  ",
			char:     ' ',
			expected: 10,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := lastIndexByteNot(tt.input, tt.char)
			if result != tt.expected {
				t.Errorf("lastIndexByteNot(%q, %q) = %d, want %d", tt.input, tt.char, result, tt.expected)
			}
		})
	}
}

// TestGetPreviousWord tests the getPreviousWord helper function
func TestGetPreviousWord(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "simple case",
			input:    "select * from ",
			expected: "from",
		},
		{
			name:     "single word with trailing space",
			input:    "select ",
			expected: "select",
		},
		{
			name:     "single word",
			input:    "select",
			expected: "",
		},
		{
			name:     "multiple spaces",
			input:    "select  *  from  ",
			expected: "from",
		},
		{
			name:     "empty string",
			input:    "",
			expected: "",
		},
		{
			name:     "only spaces",
			input:    "   ",
			expected: "",
		},
		{
			name:     "unicode characters",
			input:    "select 你好 世界 ",
			expected: "世界",
		},
		{
			name:     "emoji",
			input:    "select 🔥 💥 ",
			expected: "💥",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := getPreviousWord(tt.input)
			if result != tt.expected {
				t.Errorf("getPreviousWord(%q) = %q, want %q", tt.input, result, tt.expected)
			}
		})
	}
}

// TestGetTable tests the getTable helper function
func TestGetTable(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "simple select",
			input:    "select * from users",
			expected: "users",
		},
		{
			name:     "qualified table",
			input:    "select * from public.users",
			expected: "public.users",
		},
		{
			name:     "no from clause",
			input:    "select 1",
			expected: "",
		},
		{
			name:     "from at end",
			input:    "select * from",
			expected: "",
		},
		{
			name:     "from with trailing text",
			input:    "select * from users where",
			expected: "users",
		},
		{
			name:     "double spaces",
			input:    "select  *  from  users",
			expected: "users",
		},
		{
			name:     "empty string",
			input:    "",
			expected: "",
		},
		{
			name:     "case sensitive - lowercase from",
			input:    "SELECT * from users",
			expected: "users",
		},
		{
			name:     "uppercase FROM",
			input:    "SELECT * FROM users",
			expected: "",
		},
		{
			name:     "unicode table name",
			input:    "select * from 用户表",
			expected: "用户表",
		},
		{
			name:     "emoji in table name",
			input:    "select * from users🔥",
			expected: "users🔥",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := getTable(tt.input)
			if result != tt.expected {
				t.Errorf("getTable(%q) = %q, want %q", tt.input, result, tt.expected)
			}
		})
	}
}

// TestIsEditingTable tests the isEditingTable helper function
func TestIsEditingTable(t *testing.T) {
	tests := []struct {
		name     string
		text     string
		prevWord string
		expected bool
	}{
		{
			name:     "from keyword with trailing space",
			text:     "from ",
			prevWord: "from",
			expected: true,
		},
		{
			name:     "from keyword without trailing space",
			text:     "from",
			prevWord: "from",
			expected: false,
		},
		{
			name:     "not from keyword",
			text:     "select ",
			prevWord: "select",
			expected: false,
		},
		{
			name:     "empty string",
			text:     "",
			prevWord: "",
			expected: false,
		},
		{
			name:     "FROM uppercase",
			text:     "FROM ",
			prevWord: "FROM",
			expected: false,
		},
		{
			name:     "whitespace",
			text:     " from ",
			prevWord: " from ",
			expected: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := isEditingTable(tt.text, tt.prevWord)
			if result != tt.expected {
				t.Errorf("isEditingTable(%q, %q) = %v, want %v", tt.text, tt.prevWord, result, tt.expected)
			}
		})
	}
}

// TestGetQueryInfo tests the getQueryInfo function (passing cases only)
func TestGetQueryInfo(t *testing.T) {
	tests := []struct {
		name            string
		input           string
		expectedTable   string
		expectedEditing bool
	}{
		{
			name:            "editing table after from",
			input:           "select * from ",
			expectedTable:   "",
			expectedEditing: true,
		},
		{
			name:            "table specified",
			input:           "select * from users ",
			expectedTable:   "users",
			expectedEditing: false,
		},
		{
			name:            "not at from clause",
			input:           "select * ",
			expectedTable:   "",
			expectedEditing: false,
		},
		{
			name:            "empty query",
			input:           "",
			expectedTable:   "",
			expectedEditing: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := getQueryInfo(tt.input)
			if result.Table != tt.expectedTable {
				t.Errorf("getQueryInfo(%q).Table = %q, want %q", tt.input, result.Table, tt.expectedTable)
			}
			if result.EditingTable != tt.expectedEditing {
				t.Errorf("getQueryInfo(%q).EditingTable = %v, want %v", tt.input, result.EditingTable, tt.expectedEditing)
			}
		})
	}
}

// TestCleanBufferForWSL tests the WSL-specific buffer cleaning
func TestCleanBufferForWSL(t *testing.T) {
	tests := []struct {
		name           string
		input          string
		expectedOutput string
		expectedIgnore bool
	}{
		{
			name:           "normal text",
			input:          "hello",
			expectedOutput: "hello",
			expectedIgnore: false,
		},
		{
			name:           "empty string",
			input:          "",
			expectedOutput: "",
			expectedIgnore: false,
		},
		{
			name:           "escape sequence",
			input:          string([]byte{27, 65}), // ESC + 'A'
			expectedOutput: "",
			expectedIgnore: true,
		},
		{
			name:           "single escape",
			input:          string([]byte{27}),
			expectedOutput: string([]byte{27}),
			expectedIgnore: false,
		},
		{
			name:           "unicode",
			input:          "你好",
			expectedOutput: "你好",
			expectedIgnore: false,
		},
		{
			name:           "emoji",
			input:          "🔥",
			expectedOutput: "🔥",
			expectedIgnore: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			output, ignore := cleanBufferForWSL(tt.input)
			if output != tt.expectedOutput {
				t.Errorf("cleanBufferForWSL(%q) output = %q, want %q", tt.input, output, tt.expectedOutput)
			}
			if ignore != tt.expectedIgnore {
				t.Errorf("cleanBufferForWSL(%q) ignore = %v, want %v", tt.input, ignore, tt.expectedIgnore)
			}
		})
	}
}

// TestSanitiseTableName tests table name escaping (passing cases only)
func TestSanitiseTableName(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "simple lowercase table",
			input:    "users",
			expected: "users",
		},
		{
			name:     "uppercase table",
			input:    "Users",
			expected: `"Users"`,
		},
		{
			name:     "table with space",
			input:    "user data",
			expected: `"user data"`,
		},
		{
			name:     "table with hyphen",
			input:    "user-data",
			expected: `"user-data"`,
		},
		{
			name:     "qualified table",
			input:    "schema.table",
			expected: "schema.table",
		},
		{
			name:     "qualified with uppercase",
			input:    "Schema.Table",
			expected: `"Schema"."Table"`,
		},
		{
			name:     "qualified with spaces",
			input:    "my schema.my table",
			expected: `"my schema"."my table"`,
		},
		{
			name:     "empty string",
			input:    "",
			expected: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := sanitiseTableName(tt.input)
			if result != tt.expected {
				t.Errorf("sanitiseTableName(%q) = %q, want %q", tt.input, result, tt.expected)
			}
		})
	}
}

// TestHelperFunctionsWithExtremeInput tests helper functions with extreme inputs
func TestHelperFunctionsWithExtremeInput(t *testing.T) {
	t.Run("very long string", func(t *testing.T) {
		longString := strings.Repeat("a ", 10000)

		// Test that these don't panic or hang
		defer func() {
			if r := recover(); r != nil {
				t.Errorf("Function panicked on long string: %v", r)
			}
		}()

		_ = isFirstWord(longString)
		_ = getTable(longString)
		_ = getPreviousWord(longString)
		_ = getQueryInfo(longString)
	})

	t.Run("null bytes", func(t *testing.T) {
		nullByteString := "select\x00from\x00users"

		defer func() {
			if r := recover(); r != nil {
				t.Errorf("Function panicked on null bytes: %v", r)
			}
		}()

		_ = isFirstWord(nullByteString)
		_ = getTable(nullByteString)
		_ = getPreviousWord(nullByteString)
	})

	t.Run("control characters", func(t *testing.T) {
		controlString := "select\n\r\tfrom\n\rusers"

		defer func() {
			if r := recover(); r != nil {
				t.Errorf("Function panicked on control chars: %v", r)
			}
		}()

		_ = isFirstWord(controlString)
		_ = getTable(controlString)
		_ = getPreviousWord(controlString)
	})

	t.Run("SQL injection attempts", func(t *testing.T) {
		injectionStrings := []string{
			"'; DROP TABLE users; --",
			"1' OR '1'='1",
			"1; DELETE FROM connections; --",
			"select * from users where id = 1' union select * from passwords --",
		}

		for _, injection := range injectionStrings {
			defer func() {
				if r := recover(); r != nil {
					t.Errorf("Function panicked on injection string %q: %v", injection, r)
				}
			}()

			_ = isFirstWord(injection)
			_ = getTable(injection)
			_ = getPreviousWord(injection)
			_ = getQueryInfo(injection)
		}
	})
}
