package protocol

import (
	"testing"
)

func TestParseBzzzURI(t *testing.T) {
	tests := []struct {
		name        string
		uri         string
		expectError bool
		expected    *BzzzURI
	}{
		{
			name: "valid basic URI",
			uri:  "bzzz://claude:frontend@chorus:implement/src/main.go",
			expected: &BzzzURI{
				Agent:   "claude",
				Role:    "frontend",
				Project: "chorus",
				Task:    "implement",
				Path:    "/src/main.go",
				Raw:     "bzzz://claude:frontend@chorus:implement/src/main.go",
			},
		},
		{
			name: "URI with wildcards",
			uri:  "bzzz://any:*@*:test",
			expected: &BzzzURI{
				Agent:   "any",
				Role:    "*",
				Project: "*",
				Task:    "test",
				Raw:     "bzzz://any:*@*:test",
			},
		},
		{
			name: "URI with query and fragment",
			uri:  "bzzz://claude:backend@bzzz:debug/api/handler.go?type=error#line123",
			expected: &BzzzURI{
				Agent:    "claude",
				Role:     "backend",
				Project:  "bzzz",
				Task:     "debug",
				Path:     "/api/handler.go",
				Query:    "type=error",
				Fragment: "line123",
				Raw:      "bzzz://claude:backend@bzzz:debug/api/handler.go?type=error#line123",
			},
		},
		{
			name: "URI without path",
			uri:  "bzzz://any:architect@project:review",
			expected: &BzzzURI{
				Agent:   "any",
				Role:    "architect",
				Project: "project",
				Task:    "review",
				Raw:     "bzzz://any:architect@project:review",
			},
		},
		{
			name:        "invalid scheme",
			uri:         "http://claude:frontend@chorus:implement",
			expectError: true,
		},
		{
			name:        "missing role",
			uri:         "bzzz://claude@chorus:implement",
			expectError: true,
		},
		{
			name:        "missing task",
			uri:         "bzzz://claude:frontend@chorus",
			expectError: true,
		},
		{
			name:        "empty URI",
			uri:         "",
			expectError: true,
		},
		{
			name:        "invalid format",
			uri:         "bzzz://invalid",
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := ParseBzzzURI(tt.uri)
			
			if tt.expectError {
				if err == nil {
					t.Errorf("expected error but got none")
				}
				return
			}
			
			if err != nil {
				t.Errorf("unexpected error: %v", err)
				return
			}
			
			if result == nil {
				t.Errorf("result is nil")
				return
			}
			
			// Compare components
			if result.Agent != tt.expected.Agent {
				t.Errorf("Agent: expected %s, got %s", tt.expected.Agent, result.Agent)
			}
			if result.Role != tt.expected.Role {
				t.Errorf("Role: expected %s, got %s", tt.expected.Role, result.Role)
			}
			if result.Project != tt.expected.Project {
				t.Errorf("Project: expected %s, got %s", tt.expected.Project, result.Project)
			}
			if result.Task != tt.expected.Task {
				t.Errorf("Task: expected %s, got %s", tt.expected.Task, result.Task)
			}
			if result.Path != tt.expected.Path {
				t.Errorf("Path: expected %s, got %s", tt.expected.Path, result.Path)
			}
			if result.Query != tt.expected.Query {
				t.Errorf("Query: expected %s, got %s", tt.expected.Query, result.Query)
			}
			if result.Fragment != tt.expected.Fragment {
				t.Errorf("Fragment: expected %s, got %s", tt.expected.Fragment, result.Fragment)
			}
		})
	}
}

func TestBzzzURIValidation(t *testing.T) {
	tests := []struct {
		name        string
		uri         *BzzzURI
		expectError bool
	}{
		{
			name: "valid URI",
			uri: &BzzzURI{
				Agent:   "claude",
				Role:    "frontend",
				Project: "chorus",
				Task:    "implement",
				Path:    "/src/main.go",
			},
			expectError: false,
		},
		{
			name: "empty agent",
			uri: &BzzzURI{
				Agent:   "",
				Role:    "frontend",
				Project: "chorus",
				Task:    "implement",
			},
			expectError: true,
		},
		{
			name: "invalid agent format",
			uri: &BzzzURI{
				Agent:   "invalid@agent",
				Role:    "frontend",
				Project: "chorus",
				Task:    "implement",
			},
			expectError: true,
		},
		{
			name: "wildcard components",
			uri: &BzzzURI{
				Agent:   "*",
				Role:    "any",
				Project: "*",
				Task:    "*",
			},
			expectError: false,
		},
		{
			name: "invalid path",
			uri: &BzzzURI{
				Agent:   "claude",
				Role:    "frontend",
				Project: "chorus",
				Task:    "implement",
				Path:    "invalid-path", // Should start with /
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := tt.uri.Validate()
			
			if tt.expectError && err == nil {
				t.Errorf("expected error but got none")
			}
			if !tt.expectError && err != nil {
				t.Errorf("unexpected error: %v", err)
			}
		})
	}
}

func TestBzzzURINormalize(t *testing.T) {
	uri := &BzzzURI{
		Agent:   "Claude",
		Role:    "Frontend",
		Project: "CHORUS",
		Task:    "Implement",
		Path:    "src/main.go", // Missing leading slash
	}

	uri.Normalize()

	expected := &BzzzURI{
		Agent:   "claude",
		Role:    "frontend",
		Project: "chorus",
		Task:    "implement",
		Path:    "/src/main.go",
	}

	if uri.Agent != expected.Agent {
		t.Errorf("Agent: expected %s, got %s", expected.Agent, uri.Agent)
	}
	if uri.Role != expected.Role {
		t.Errorf("Role: expected %s, got %s", expected.Role, uri.Role)
	}
	if uri.Project != expected.Project {
		t.Errorf("Project: expected %s, got %s", expected.Project, uri.Project)
	}
	if uri.Task != expected.Task {
		t.Errorf("Task: expected %s, got %s", expected.Task, uri.Task)
	}
	if uri.Path != expected.Path {
		t.Errorf("Path: expected %s, got %s", expected.Path, uri.Path)
	}
}

func TestBzzzURIMatches(t *testing.T) {
	tests := []struct {
		name     string
		uri1     *BzzzURI
		uri2     *BzzzURI
		expected bool
	}{
		{
			name: "exact match",
			uri1: &BzzzURI{Agent: "claude", Role: "frontend", Project: "chorus", Task: "implement"},
			uri2: &BzzzURI{Agent: "claude", Role: "frontend", Project: "chorus", Task: "implement"},
			expected: true,
		},
		{
			name: "wildcard agent match",
			uri1: &BzzzURI{Agent: "*", Role: "frontend", Project: "chorus", Task: "implement"},
			uri2: &BzzzURI{Agent: "claude", Role: "frontend", Project: "chorus", Task: "implement"},
			expected: true,
		},
		{
			name: "any role match",
			uri1: &BzzzURI{Agent: "claude", Role: "any", Project: "chorus", Task: "implement"},
			uri2: &BzzzURI{Agent: "claude", Role: "frontend", Project: "chorus", Task: "implement"},
			expected: true,
		},
		{
			name: "no match",
			uri1: &BzzzURI{Agent: "claude", Role: "backend", Project: "chorus", Task: "implement"},
			uri2: &BzzzURI{Agent: "claude", Role: "frontend", Project: "chorus", Task: "implement"},
			expected: false,
		},
		{
			name: "nil comparison",
			uri1: &BzzzURI{Agent: "claude", Role: "frontend", Project: "chorus", Task: "implement"},
			uri2: nil,
			expected: false,
		},
	}

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

func TestBzzzURIString(t *testing.T) {
	tests := []struct {
		name     string
		uri      *BzzzURI
		expected string
	}{
		{
			name: "basic URI",
			uri: &BzzzURI{
				Agent:   "claude",
				Role:    "frontend",
				Project: "chorus",
				Task:    "implement",
				Path:    "/src/main.go",
			},
			expected: "bzzz://claude:frontend@chorus:implement/src/main.go",
		},
		{
			name: "URI with query and fragment",
			uri: &BzzzURI{
				Agent:    "claude",
				Role:     "backend",
				Project:  "bzzz",
				Task:     "debug",
				Path:     "/api/handler.go",
				Query:    "type=error",
				Fragment: "line123",
			},
			expected: "bzzz://claude:backend@bzzz:debug/api/handler.go?type=error#line123",
		},
		{
			name: "URI without path",
			uri: &BzzzURI{
				Agent:   "any",
				Role:    "architect",
				Project: "project",
				Task:    "review",
			},
			expected: "bzzz://any:architect@project:review",
		},
	}

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

func TestGetSelectorPriority(t *testing.T) {
	tests := []struct {
		name     string
		uri      *BzzzURI
		expected int
	}{
		{
			name: "all specific",
			uri: &BzzzURI{
				Agent:   "claude",
				Role:    "frontend",
				Project: "chorus",
				Task:    "implement",
				Path:    "/src/main.go",
			},
			expected: 8 + 4 + 2 + 1 + 1, // All components + path
		},
		{
			name: "some wildcards",
			uri: &BzzzURI{
				Agent:   "*",
				Role:    "frontend",
				Project: "*",
				Task:    "implement",
			},
			expected: 4 + 1, // Role + Task
		},
		{
			name: "all wildcards",
			uri: &BzzzURI{
				Agent:   "*",
				Role:    "any",
				Project: "*",
				Task:    "*",
			},
			expected: 0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.uri.GetSelectorPriority()
			if result != tt.expected {
				t.Errorf("expected %d, got %d", tt.expected, result)
			}
		})
	}
}

func TestParseAddress(t *testing.T) {
	tests := []struct {
		name        string
		addr        string
		expectError bool
		expected    *BzzzURI
	}{
		{
			name: "valid address",
			addr: "claude:frontend@chorus:implement",
			expected: &BzzzURI{
				Agent:   "claude",
				Role:    "frontend", 
				Project: "chorus",
				Task:    "implement",
			},
		},
		{
			name:        "invalid address",
			addr:        "invalid-format",
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := ParseAddress(tt.addr)
			
			if tt.expectError {
				if err == nil {
					t.Errorf("expected error but got none")
				}
				return
			}
			
			if err != nil {
				t.Errorf("unexpected error: %v", err)
				return
			}
			
			if result.Agent != tt.expected.Agent {
				t.Errorf("Agent: expected %s, got %s", tt.expected.Agent, result.Agent)
			}
			if result.Role != tt.expected.Role {
				t.Errorf("Role: expected %s, got %s", tt.expected.Role, result.Role)
			}
			if result.Project != tt.expected.Project {
				t.Errorf("Project: expected %s, got %s", tt.expected.Project, result.Project)
			}
			if result.Task != tt.expected.Task {
				t.Errorf("Task: expected %s, got %s", tt.expected.Task, result.Task)
			}
		})
	}
}

func TestIsWildcard(t *testing.T) {
	tests := []struct {
		component string
		expected  bool
	}{
		{"*", true},
		{"any", true},
		{"claude", false},
		{"frontend", false},
		{"", false},
	}

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

func TestValidateBzzzURIString(t *testing.T) {
	tests := []struct {
		name        string
		uri         string
		expectError bool
	}{
		{
			name:        "valid URI",
			uri:         "bzzz://claude:frontend@chorus:implement/src/main.go",
			expectError: false,
		},
		{
			name:        "invalid scheme",
			uri:         "http://claude:frontend@chorus:implement",
			expectError: true,
		},
		{
			name:        "empty URI",
			uri:         "",
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateBzzzURIString(tt.uri)
			
			if tt.expectError && err == nil {
				t.Errorf("expected error but got none")
			}
			if !tt.expectError && err != nil {
				t.Errorf("unexpected error: %v", err)
			}
		})
	}
}