package integration

import (
	"testing"

	"chorus.services/bzzz/pkg/ucxl"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestUCXLAddressParsing(t *testing.T) {
	testCases := []struct {
		name      string
		address   string
		expectErr bool
		expected  *ucxl.UCXLAddress
	}{
		{
			name:    "Basic address",
			address: "ucxl://agent-001:coordinator@bzzz:config/",
			expected: &ucxl.UCXLAddress{
				Raw:     "ucxl://agent-001:coordinator@bzzz:config/",
				Agent:   "agent-001",
				Role:    "coordinator",
				Project: "bzzz",
				Task:    "config",
				Path:    "",
				Temporal: "",
			},
		},
		{
			name:    "Address with path",
			address: "ucxl://agent-001:coordinator@bzzz:config/network/settings",
			expected: &ucxl.UCXLAddress{
				Raw:     "ucxl://agent-001:coordinator@bzzz:config/network/settings",
				Agent:   "agent-001",
				Role:    "coordinator",
				Project: "bzzz",
				Task:    "config",
				Path:    "network/settings",
				Temporal: "",
			},
		},
		{
			name:    "Address with temporal navigation",
			address: "ucxl://agent-001:coordinator@bzzz:config/*^/",
			expected: &ucxl.UCXLAddress{
				Raw:     "ucxl://agent-001:coordinator@bzzz:config/*^/",
				Agent:   "agent-001",
				Role:    "coordinator",
				Project: "bzzz",
				Task:    "config",
				Path:    "",
				Temporal: "^/",
			},
		},
		{
			name:    "Address with path and temporal",
			address: "ucxl://agent-001:coordinator@bzzz:config/settings*~/",
			expected: &ucxl.UCXLAddress{
				Raw:     "ucxl://agent-001:coordinator@bzzz:config/settings*~/",
				Agent:   "agent-001",
				Role:    "coordinator",
				Project: "bzzz",
				Task:    "config",
				Path:    "settings",
				Temporal: "~/",
			},
		},
		{
			name:    "Wildcard address",
			address: "ucxl://*:*@*:*/*^/",
			expected: &ucxl.UCXLAddress{
				Raw:     "ucxl://*:*@*:*/*^/",
				Agent:   "*",
				Role:    "*",
				Project: "*",
				Task:    "*",
				Path:    "",
				Temporal: "^/",
			},
		},
		{
			name:      "Invalid format - no scheme",
			address:   "agent-001:coordinator@bzzz:config/",
			expectErr: true,
		},
		{
			name:      "Invalid format - wrong scheme",
			address:   "http://agent-001:coordinator@bzzz:config/",
			expectErr: true,
		},
		{
			name:      "Invalid format - missing components",
			address:   "ucxl://agent-001@bzzz/",
			expectErr: true,
		},
		{
			name:      "Empty address",
			address:   "",
			expectErr: true,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			result, err := ucxl.ParseUCXLAddress(tc.address)

			if tc.expectErr {
				assert.Error(t, err)
				assert.Nil(t, result)
			} else {
				require.NoError(t, err)
				require.NotNil(t, result)
				assert.Equal(t, tc.expected.Agent, result.Agent)
				assert.Equal(t, tc.expected.Role, result.Role)
				assert.Equal(t, tc.expected.Project, result.Project)
				assert.Equal(t, tc.expected.Task, result.Task)
				assert.Equal(t, tc.expected.Path, result.Path)
				assert.Equal(t, tc.expected.Temporal, result.Temporal)
			}
		})
	}
}

func TestUCXLAddressGeneration(t *testing.T) {
	testCases := []struct {
		name      string
		agent     string
		role      string
		project   string
		task      string
		path      string
		temporal  string
		expected  string
		expectErr bool
	}{
		{
			name:     "Basic generation",
			agent:    "agent-001",
			role:     "coordinator",
			project:  "bzzz",
			task:     "config",
			expected: "ucxl://agent-001:coordinator@bzzz:config/",
		},
		{
			name:     "Generation with path",
			agent:    "agent-001",
			role:     "coordinator",
			project:  "bzzz",
			task:     "config",
			path:     "/network/settings",
			expected: "ucxl://agent-001:coordinator@bzzz:config/%2Fnetwork%2Fsettings/",
		},
		{
			name:     "Generation with temporal",
			agent:    "agent-001",
			role:     "coordinator",
			project:  "bzzz",
			task:     "config",
			temporal: "^/",
			expected: "ucxl://agent-001:coordinator@bzzz:config/*^/",
		},
		{
			name:     "Generation with both",
			agent:    "agent-001",
			role:     "coordinator",
			project:  "bzzz",
			task:     "config",
			path:     "settings",
			temporal: "~/",
			expected: "ucxl://agent-001:coordinator@bzzz:config/settings*~/",
		},
		{
			name:      "Missing required field",
			agent:     "",
			role:      "coordinator",
			project:   "bzzz",
			task:      "config",
			expectErr: true,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			result, err := ucxl.GenerateUCXLAddress(tc.agent, tc.role, tc.project, tc.task, tc.path, tc.temporal)

			if tc.expectErr {
				assert.Error(t, err)
				assert.Empty(t, result)
			} else {
				assert.NoError(t, err)
				assert.Equal(t, tc.expected, result)

				// Verify generated address can be parsed back
				parsed, err := ucxl.ParseUCXLAddress(result)
				require.NoError(t, err)
				assert.Equal(t, tc.agent, parsed.Agent)
				assert.Equal(t, tc.role, parsed.Role)
				assert.Equal(t, tc.project, parsed.Project)
				assert.Equal(t, tc.task, parsed.Task)
			}
		})
	}
}

func TestUCXLAddressValidation(t *testing.T) {
	validAddresses := []string{
		"ucxl://agent:role@project:task/",
		"ucxl://*:*@*:*/*^/",
		"ucxl://agent-001:coordinator@bzzz:config/settings*~/",
		"ucxl://test:test@test:test/path/to/resource",
	}

	invalidAddresses := []string{
		"",
		"http://invalid",
		"ucxl://incomplete",
		"not-a-url",
		"ucxl://:@:/",
	}

	for _, addr := range validAddresses {
		t.Run("Valid_"+addr, func(t *testing.T) {
			assert.True(t, ucxl.IsValidUCXLAddress(addr))
		})
	}

	for _, addr := range invalidAddresses {
		t.Run("Invalid_"+addr, func(t *testing.T) {
			assert.False(t, ucxl.IsValidUCXLAddress(addr))
		})
	}
}

func TestUCXLAddressPatternMatching(t *testing.T) {
	// Create test addresses
	address1, err := ucxl.ParseUCXLAddress("ucxl://agent-001:coordinator@bzzz:config/network")
	require.NoError(t, err)

	address2, err := ucxl.ParseUCXLAddress("ucxl://agent-002:worker@bzzz:deployment/")
	require.NoError(t, err)

	// Create patterns
	wildcardPattern, err := ucxl.ParseUCXLAddress("ucxl://*:*@bzzz:*/*^/")
	require.NoError(t, err)

	specificPattern, err := ucxl.ParseUCXLAddress("ucxl://agent-001:coordinator@bzzz:config/*^/")
	require.NoError(t, err)

	rolePattern, err := ucxl.ParseUCXLAddress("ucxl://*:coordinator@*:*/*^/")
	require.NoError(t, err)

	// Test pattern matching
	testCases := []struct {
		name     string
		address  *ucxl.UCXLAddress
		pattern  *ucxl.UCXLAddress
		expected bool
	}{
		{
			name:     "Wildcard pattern matches address1",
			address:  address1,
			pattern:  wildcardPattern,
			expected: true,
		},
		{
			name:     "Wildcard pattern matches address2",
			address:  address2,
			pattern:  wildcardPattern,
			expected: true,
		},
		{
			name:     "Specific pattern matches address1",
			address:  address1,
			pattern:  specificPattern,
			expected: true,
		},
		{
			name:     "Specific pattern does not match address2",
			address:  address2,
			pattern:  specificPattern,
			expected: false,
		},
		{
			name:     "Role pattern matches coordinator",
			address:  address1,
			pattern:  rolePattern,
			expected: true,
		},
		{
			name:     "Role pattern does not match worker",
			address:  address2,
			pattern:  rolePattern,
			expected: false,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			result := tc.address.MatchesPattern(tc.pattern)
			assert.Equal(t, tc.expected, result)
		})
	}
}

func TestUCXLAddressNormalization(t *testing.T) {
	testCases := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "Already normalized",
			input:    "ucxl://agent:role@project:task/",
			expected: "ucxl://agent:role@project:task/",
		},
		{
			name:     "Missing trailing slash",
			input:    "ucxl://agent:role@project:task",
			expected: "ucxl://agent:role@project:task/",
		},
		{
			name:     "With path normalization",
			input:    "ucxl://agent:role@project:task/path/to/resource",
			expected: "ucxl://agent:role@project:task/path/to/resource/",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			result, err := ucxl.NormalizeUCXLAddress(tc.input)
			assert.NoError(t, err)
			assert.Equal(t, tc.expected, result)
		})
	}
}

func TestUCXLAddressTemporalValidation(t *testing.T) {
	validTemporalCases := []string{
		"ucxl://agent:role@project:task/*^/",     // Latest
		"ucxl://agent:role@project:task/*~/",     // Earliest
		"ucxl://agent:role@project:task/*@1234/", // Specific timestamp
		"ucxl://agent:role@project:task/*~5/",    // 5 versions back
		"ucxl://agent:role@project:task/*^3/",    // 3 versions forward
	}

	invalidTemporalCases := []string{
		"ucxl://agent:role@project:task/*invalid/",
		"ucxl://agent:role@project:task/*@abc/",
		"ucxl://agent:role@project:task/*~-1/",
	}

	for _, addr := range validTemporalCases {
		t.Run("ValidTemporal_"+addr, func(t *testing.T) {
			_, err := ucxl.ParseUCXLAddress(addr)
			assert.NoError(t, err, "Should parse valid temporal syntax")
		})
	}

	for _, addr := range invalidTemporalCases {
		t.Run("InvalidTemporal_"+addr, func(t *testing.T) {
			_, err := ucxl.ParseUCXLAddress(addr)
			assert.Error(t, err, "Should reject invalid temporal syntax")
		})
	}
}

func TestUCXLAddressCrossLanguageConsistency(t *testing.T) {
	// This test ensures UCXL address parsing is consistent across Go implementation
	// When Rust implementation is available, similar tests should produce identical results
	
	testAddresses := []string{
		"ucxl://agent:role@project:task/*^/",
		"ucxl://*:coordinator@bzzz:config/network/settings*~/",
		"ucxl://test-agent-123:worker@production:deployment/services/api*@1640995200/",
	}

	for _, addr := range testAddresses {
		t.Run("CrossLanguage_"+addr, func(t *testing.T) {
			// Parse address
			parsed, err := ucxl.ParseUCXLAddress(addr)
			require.NoError(t, err)

			// Convert to map (similar to JSON serialization)
			addressMap := parsed.ToMap()

			// Verify all components are present
			assert.NotEmpty(t, addressMap["agent"])
			assert.NotEmpty(t, addressMap["role"])
			assert.NotEmpty(t, addressMap["project"])
			assert.NotEmpty(t, addressMap["task"])

			// Verify round-trip consistency
			regenerated, err := ucxl.GenerateUCXLAddress(
				parsed.Agent,
				parsed.Role,
				parsed.Project,
				parsed.Task,
				parsed.Path,
				parsed.Temporal,
			)
			assert.NoError(t, err)

			// Parse regenerated address
			reparsed, err := ucxl.ParseUCXLAddress(regenerated)
			require.NoError(t, err)

			// Should be identical
			assert.Equal(t, parsed.Agent, reparsed.Agent)
			assert.Equal(t, parsed.Role, reparsed.Role)
			assert.Equal(t, parsed.Project, reparsed.Project)
			assert.Equal(t, parsed.Task, reparsed.Task)
		})
	}
}