package preproc

import (
	"testing"
)

func TestDefineList_AddAndDefined(t *testing.T) {
	dl := NewDefineList()

	if dl.Defined("FOO") {
		t.Error("FOO should not be defined initially")
	}

	dl.Add("FOO", "bar")
	if !dl.Defined("FOO") {
		t.Error("FOO should be defined after Add")
	}
}

func TestDefineList_Delete(t *testing.T) {
	dl := NewDefineList()
	dl.Add("FOO", "bar")

	if !dl.Delete("FOO") {
		t.Error("Delete should return true for existing key")
	}

	if dl.Defined("FOO") {
		t.Error("FOO should not be defined after Delete")
	}

	if dl.Delete("NONEXISTENT") {
		t.Error("Delete should return false for non-existent key")
	}
}

func TestDefineList_ReplaceDefines_Simple(t *testing.T) {
	dl := NewDefineList()
	dl.Add("FOO", "replacement")

	tests := []struct {
		input    string
		expected string
	}{
		{"FOO", "replacement"},
		{"before FOO after", "before replacement after"},
		{"FOOFOO", "replacementreplacement"},
		{"no match here", "no match here"},
		{"", ""},
	}

	for _, tt := range tests {
		got := dl.ReplaceDefines(tt.input)
		if got != tt.expected {
			t.Errorf("ReplaceDefines(%q) = %q, want %q", tt.input, got, tt.expected)
		}
	}
}

func TestDefineList_ReplaceDefines_LongestPrefix(t *testing.T) {
	dl := NewDefineList()
	dl.Add("FOO", "short")
	dl.Add("FOOBAR", "long")

	tests := []struct {
		input    string
		expected string
	}{
		{"FOO", "short"},
		{"FOOBAR", "long"},       // longest match wins
		{"FOOBARBAZ", "longBAZ"}, // longest match, rest unchanged
		{"FOO BAR", "short BAR"}, // space breaks match
	}

	for _, tt := range tests {
		got := dl.ReplaceDefines(tt.input)
		if got != tt.expected {
			t.Errorf("ReplaceDefines(%q) = %q, want %q", tt.input, got, tt.expected)
		}
	}
}

func TestDefineList_ReplaceDefines_NoRecursion(t *testing.T) {
	dl := NewDefineList()
	dl.Add("A", "B")
	dl.Add("B", "C")

	// Should not recursively expand B->C
	if got := dl.ReplaceDefines("A"); got != "B" {
		t.Errorf("ReplaceDefines should not recurse: got %q, want %q", got, "B")
	}
}

func TestDefineList_ReplaceDefines_CaseSensitive(t *testing.T) {
	dl := NewDefineList()
	dl.Add("FOO", "bar")

	if got := dl.ReplaceDefines("foo"); got != "foo" {
		t.Errorf("Should be case-sensitive: got %q, want %q", got, "foo")
	}
}

func TestDefineList_ReplaceDefines_UTF8(t *testing.T) {
	dl := NewDefineList()
	dl.Add("🚀", "rocket")
	dl.Add("αβ", "alpha-beta")

	tests := []struct {
		input    string
		expected string
	}{
		{"🚀", "rocket"},
		{"test🚀test", "testrockettest"},
		{"αβγ", "alpha-betaγ"},
		{"日本語FOO", "日本語FOO"}, // no match
	}

	for _, tt := range tests {
		got := dl.ReplaceDefines(tt.input)
		if got != tt.expected {
			t.Errorf("ReplaceDefines(%q) = %q, want %q", tt.input, got, tt.expected)
		}
	}
}

func TestDefineList_ReplaceDefines_MultipleReplacements(t *testing.T) {
	dl := NewDefineList()
	dl.Add("X", "1")
	dl.Add("Y", "2")
	dl.Add("Z", "3")

	got := dl.ReplaceDefines("X Y Z X")
	want := "1 2 3 1"
	if got != want {
		t.Errorf("ReplaceDefines = %q, want %q", got, want)
	}
}

func TestDefineList_ReplaceDefines_OverlappingPrefixes(t *testing.T) {
	dl := NewDefineList()
	dl.Add("A", "x")
	dl.Add("AB", "y")
	dl.Add("ABC", "z")

	tests := []struct {
		input    string
		expected string
	}{
		{"A", "x"},
		{"AB", "y"},
		{"ABC", "z"},
		{"ABCD", "zD"},
		{"AAA", "xxx"},        // three separate A matches
		{"ABABC", "yz"},       // AB, then ABC
		{"A-AB-ABC", "x-y-z"}, // separated by dashes
	}

	for _, tt := range tests {
		got := dl.ReplaceDefines(tt.input)
		if got != tt.expected {
			t.Errorf("ReplaceDefines(%q) = %q, want %q", tt.input, got, tt.expected)
		}
	}
}

func TestDefineList_ReplaceDefines_EmptyValue(t *testing.T) {
	dl := NewDefineList()
	dl.Add("REMOVE", "")

	got := dl.ReplaceDefines("before REMOVE after")
	want := "before  after"
	if got != want {
		t.Errorf("ReplaceDefines = %q, want %q", got, want)
	}
}

func TestDefineList_ReplaceDefines_UpdateValue(t *testing.T) {
	dl := NewDefineList()
	dl.Add("FOO", "first")
	dl.Add("FOO", "second") // update

	got := dl.ReplaceDefines("FOO")
	if got != "second" {
		t.Errorf("After update: got %q, want %q", got, "second")
	}
}
