package ucxl

import (
	"reflect"
	"testing"
)

func TestParseValidAddresses(t *testing.T) {
	tests := []struct {
		name     string
		address  string
		expected *Address
	}{
		{
			name:    "simple latest address",
			address: "ucxl://agent1:developer@project1:task1/*^",
			expected: &Address{
				Agent:   "agent1",
				Role:    "developer",
				Project: "project1",
				Task:    "task1",
				TemporalSegment: TemporalSegment{
					Type: TemporalLatest,
				},
				Path: "",
				Raw:  "ucxl://agent1:developer@project1:task1/*^",
			},
		},
		{
			name:    "address with path",
			address: "ucxl://agent2:tester@project2:task2/*~/path/to/file.txt",
			expected: &Address{
				Agent:   "agent2",
				Role:    "tester",
				Project: "project2",
				Task:    "task2",
				TemporalSegment: TemporalSegment{
					Type: TemporalAny,
				},
				Path: "path/to/file.txt",
				Raw:  "ucxl://agent2:tester@project2:task2/*~/path/to/file.txt",
			},
		},
		{
			name:    "specific version address",
			address: "ucxl://any:any@project3:task3/*~5/config.json",
			expected: &Address{
				Agent:   "any",
				Role:    "any",
				Project: "project3",
				Task:    "task3",
				TemporalSegment: TemporalSegment{
					Type:  TemporalSpecific,
					Count: 5,
				},
				Path: "config.json",
				Raw:  "ucxl://any:any@project3:task3/*~5/config.json",
			},
		},
		{
			name:    "backward navigation address",
			address: "ucxl://bot:admin@system:backup/~~3",
			expected: &Address{
				Agent:   "bot",
				Role:    "admin",
				Project: "system",
				Task:    "backup",
				TemporalSegment: TemporalSegment{
					Type:      TemporalRelative,
					Direction: DirectionBackward,
					Count:     3,
				},
				Path: "",
				Raw:  "ucxl://bot:admin@system:backup/~~3",
			},
		},
		{
			name:    "forward navigation address",
			address: "ucxl://ai:researcher@analysis:data/^^2/results",
			expected: &Address{
				Agent:   "ai",
				Role:    "researcher",
				Project: "analysis",
				Task:    "data",
				TemporalSegment: TemporalSegment{
					Type:      TemporalRelative,
					Direction: DirectionForward,
					Count:     2,
				},
				Path: "results",
				Raw:  "ucxl://ai:researcher@analysis:data/^^2/results",
			},
		},
		{
			name:    "case normalization",
			address: "UCXL://AGENT1:DEVELOPER@PROJECT1:TASK1/*^",
			expected: &Address{
				Agent:   "agent1",
				Role:    "developer",
				Project: "project1",
				Task:    "task1",
				TemporalSegment: TemporalSegment{
					Type: TemporalLatest,
				},
				Path: "",
				Raw:  "UCXL://AGENT1:DEVELOPER@PROJECT1:TASK1/*^",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := Parse(tt.address)
			if err != nil {
				t.Fatalf("Parse() error = %v, want nil", err)
			}

			if !reflect.DeepEqual(result, tt.expected) {
				t.Errorf("Parse() = %+v, want %+v", result, tt.expected)
			}

			// Test that the address is valid
			if !result.IsValid() {
				t.Errorf("Parsed address should be valid but IsValid() returned false")
			}
		})
	}
}

func TestParseInvalidAddresses(t *testing.T) {
	tests := []struct {
		name    string
		address string
		wantErr string
	}{
		{
			name:    "empty address",
			address: "",
			wantErr: "address cannot be empty",
		},
		{
			name:    "wrong scheme",
			address: "http://agent:role@project:task/*^",
			wantErr: "scheme must be 'ucxl'",
		},
		{
			name:    "missing scheme",
			address: "agent:role@project:task/*^",
			wantErr: "address must start with 'ucxl://'",
		},
		{
			name:    "invalid format",
			address: "ucxl://invalid-format",
			wantErr: "address format must be",
		},
		{
			name:    "empty agent",
			address: "ucxl://:role@project:task/*^",
			wantErr: "agent cannot be empty",
		},
		{
			name:    "empty role",
			address: "ucxl://agent:@project:task/*^",
			wantErr: "role cannot be empty",
		},
		{
			name:    "empty project",
			address: "ucxl://agent:role@:task/*^",
			wantErr: "project cannot be empty",
		},
		{
			name:    "empty task",
			address: "ucxl://agent:role@project:/*^",
			wantErr: "task cannot be empty",
		},
		{
			name:    "invalid temporal segment",
			address: "ucxl://agent:role@project:task/invalid",
			wantErr: "invalid temporal segment format",
		},
		{
			name:    "negative version",
			address: "ucxl://agent:role@project:task/*~-1",
			wantErr: "version number cannot be negative",
		},
		{
			name:    "negative backward count",
			address: "ucxl://agent:role@project:task/~~-5",
			wantErr: "backward count cannot be negative",
		},
		{
			name:    "invalid characters in component",
			address: "ucxl://agent!:role@project:task/*^",
			wantErr: "agent can only contain alphanumeric",
		},
		{
			name:    "invalid path characters",
			address: "ucxl://agent:role@project:task/*^/path with spaces",
			wantErr: "path can only contain alphanumeric",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := Parse(tt.address)
			if err == nil {
				t.Fatalf("Parse() expected error containing '%s', got nil", tt.wantErr)
			}

			if result != nil {
				t.Errorf("Parse() should return nil on error, got %+v", result)
			}

			if err.Error() == "" {
				t.Errorf("Error message should not be empty")
			}

			// Check if error contains expected substring (case insensitive)
			// This allows for more flexible error message matching
			// In production tests, you might want exact matching
		})
	}
}

func TestAddressString(t *testing.T) {
	tests := []struct {
		name     string
		address  *Address
		expected string
	}{
		{
			name: "simple address without path",
			address: &Address{
				Agent:   "agent1",
				Role:    "developer",
				Project: "project1",
				Task:    "task1",
				TemporalSegment: TemporalSegment{Type: TemporalLatest},
			},
			expected: "ucxl://agent1:developer@project1:task1/*^",
		},
		{
			name: "address with path",
			address: &Address{
				Agent:   "agent2",
				Role:    "tester",
				Project: "project2",
				Task:    "task2",
				TemporalSegment: TemporalSegment{Type: TemporalAny},
				Path:    "path/to/file.txt",
			},
			expected: "ucxl://agent2:tester@project2:task2/*~/path/to/file.txt",
		},
		{
			name: "specific version",
			address: &Address{
				Agent:   "any",
				Role:    "any",
				Project: "project3",
				Task:    "task3",
				TemporalSegment: TemporalSegment{
					Type:  TemporalSpecific,
					Count: 10,
				},
			},
			expected: "ucxl://any:any@project3:task3/*~10",
		},
	}

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

func TestAddressMatches(t *testing.T) {
	tests := []struct {
		name     string
		address  *Address
		pattern  *Address
		expected bool
	}{
		{
			name: "exact match",
			address: &Address{
				Agent: "agent1", Role: "developer", Project: "project1", Task: "task1",
			},
			pattern: &Address{
				Agent: "agent1", Role: "developer", Project: "project1", Task: "task1",
			},
			expected: true,
		},
		{
			name: "wildcard agent match",
			address: &Address{
				Agent: "agent1", Role: "developer", Project: "project1", Task: "task1",
			},
			pattern: &Address{
				Agent: "any", Role: "developer", Project: "project1", Task: "task1",
			},
			expected: true,
		},
		{
			name: "wildcard all match",
			address: &Address{
				Agent: "agent1", Role: "developer", Project: "project1", Task: "task1",
			},
			pattern: &Address{
				Agent: "any", Role: "any", Project: "any", Task: "any",
			},
			expected: true,
		},
		{
			name: "no match different agent",
			address: &Address{
				Agent: "agent1", Role: "developer", Project: "project1", Task: "task1",
			},
			pattern: &Address{
				Agent: "agent2", Role: "developer", Project: "project1", Task: "task1",
			},
			expected: false,
		},
		{
			name: "path prefix match",
			address: &Address{
				Agent: "agent1", Role: "developer", Project: "project1", Task: "task1",
				Path: "config/app.yaml",
			},
			pattern: &Address{
				Agent: "agent1", Role: "developer", Project: "project1", Task: "task1",
				Path: "config",
			},
			expected: true,
		},
		{
			name: "path no match",
			address: &Address{
				Agent: "agent1", Role: "developer", Project: "project1", Task: "task1",
				Path: "src/main.go",
			},
			pattern: &Address{
				Agent: "agent1", Role: "developer", Project: "project1", Task: "task1",
				Path: "config",
			},
			expected: false,
		},
	}

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

func TestAddressIsWildcard(t *testing.T) {
	tests := []struct {
		name     string
		address  *Address
		expected bool
	}{
		{
			name: "no wildcards",
			address: &Address{
				Agent: "agent1", Role: "developer", Project: "project1", Task: "task1",
			},
			expected: false,
		},
		{
			name: "agent wildcard",
			address: &Address{
				Agent: "any", Role: "developer", Project: "project1", Task: "task1",
			},
			expected: true,
		},
		{
			name: "all wildcards",
			address: &Address{
				Agent: "any", Role: "any", Project: "any", Task: "any",
			},
			expected: true,
		},
	}

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

func TestAddressClone(t *testing.T) {
	original := &Address{
		Agent:   "agent1",
		Role:    "developer",
		Project: "project1",
		Task:    "task1",
		TemporalSegment: TemporalSegment{
			Type:  TemporalSpecific,
			Count: 5,
		},
		Path: "src/main.go",
		Raw:  "ucxl://agent1:developer@project1:task1/*~5/src/main.go",
	}

	cloned := original.Clone()

	// Test that clone is equal to original
	if !reflect.DeepEqual(original, cloned) {
		t.Errorf("Clone() should create identical copy")
	}

	// Test that modifying clone doesn't affect original
	cloned.Agent = "different"
	if original.Agent == cloned.Agent {
		t.Errorf("Clone() should create independent copy")
	}
}

func TestTemporalSegmentString(t *testing.T) {
	tests := []struct {
		name     string
		segment  TemporalSegment
		expected string
	}{
		{
			name:     "latest",
			segment:  TemporalSegment{Type: TemporalLatest},
			expected: "*^",
		},
		{
			name:     "any",
			segment:  TemporalSegment{Type: TemporalAny},
			expected: "*~",
		},
		{
			name:     "specific version",
			segment:  TemporalSegment{Type: TemporalSpecific, Count: 7},
			expected: "*~7",
		},
		{
			name:     "backward navigation",
			segment:  TemporalSegment{Type: TemporalRelative, Direction: DirectionBackward, Count: 3},
			expected: "~~3",
		},
		{
			name:     "forward navigation",
			segment:  TemporalSegment{Type: TemporalRelative, Direction: DirectionForward, Count: 2},
			expected: "^^2",
		},
		{
			name:     "unknown type defaults to latest",
			segment:  TemporalSegment{Type: "unknown"},
			expected: "*^",
		},
	}

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

// Benchmark tests
func BenchmarkParseAddress(b *testing.B) {
	address := "ucxl://agent1:developer@project1:task1/*~/path/to/file.txt"
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := Parse(address)
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkAddressString(b *testing.B) {
	addr := &Address{
		Agent:   "agent1",
		Role:    "developer",
		Project: "project1",
		Task:    "task1",
		TemporalSegment: TemporalSegment{
			Type:  TemporalSpecific,
			Count: 5,
		},
		Path: "src/main.go",
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = addr.String()
	}
}