package validators_test

import (
	"encoding/json"
	"os"
	"regexp"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const serverSchemaPath = "../../docs/reference/server-json/server.schema.json"

// schemaHelper provides utilities for extracting values from the JSON schema
type schemaHelper struct {
	t      *testing.T
	schema map[string]interface{}
}

func loadSchema(t *testing.T) *schemaHelper {
	t.Helper()
	data, err := os.ReadFile(serverSchemaPath)
	require.NoError(t, err, "Failed to read schema file")

	var schema map[string]interface{}
	err = json.Unmarshal(data, &schema)
	require.NoError(t, err, "Failed to parse schema JSON")

	return &schemaHelper{t: t, schema: schema}
}

// getDefinition returns a definition from the schema by name
func (s *schemaHelper) getDefinition(name string) map[string]interface{} {
	s.t.Helper()
	definitions := s.schema["definitions"].(map[string]interface{})
	def, ok := definitions[name].(map[string]interface{})
	require.True(s.t, ok, "Definition %q not found in schema", name)
	return def
}

// getPropertyPattern extracts a regex pattern from a definition's property
func (s *schemaHelper) getPropertyPattern(definitionName, propertyName string) string {
	s.t.Helper()
	def := s.getDefinition(definitionName)
	props := def["properties"].(map[string]interface{})
	prop, ok := props[propertyName].(map[string]interface{})
	require.True(s.t, ok, "Property %q not found in %s", propertyName, definitionName)
	pattern, ok := prop["pattern"].(string)
	require.True(s.t, ok, "Pattern not found for %s.%s", definitionName, propertyName)
	return pattern
}

// TestTransportURLPattern validates the URL pattern used by StreamableHttpTransport and SseTransport.
// URLs must start with http://, https://, or a template variable like {baseUrl}.
func TestTransportURLPattern(t *testing.T) {
	schema := loadSchema(t)

	streamablePattern := schema.getPropertyPattern("StreamableHttpTransport", "url")
	ssePattern := schema.getPropertyPattern("SseTransport", "url")

	// Verify both transport types use the same pattern
	assert.Equal(t, streamablePattern, ssePattern,
		"StreamableHttpTransport and SseTransport should use identical URL patterns")

	t.Logf("Pattern: %s", streamablePattern)

	re, err := regexp.Compile(streamablePattern)
	require.NoError(t, err, "Pattern should be valid regex")

	// Test cases that SHOULD match
	validCases := []string{
		// Standard URLs
		"https://api.example.com/mcp",
		"http://localhost:8080/sse",
		"https://example.com/path?query=value",
		"https://api.example.com/v1/mcp",
		// Template variables
		"{baseUrl}",
		"{baseUrl}/mcp",
		"{server_url}/api/v1",
		"{API_ENDPOINT}",
		"{a}",
		"{_private}/endpoint",
	}

	for _, tc := range validCases {
		assert.True(t, re.MatchString(tc), "Expected %q to match pattern", tc)
	}

	// Test cases that should NOT match
	invalidCases := []string{
		"ftp://example.com",       // wrong protocol
		"example.com",             // missing protocol or variable
		"/relative/path",          // relative path
		"{invalid-name}/path",     // hyphen in variable name
		"{123invalid}",            // variable starts with number
		"",                        // empty string
		"mailto:test@example.com", // wrong protocol
		"file:///path/to/file",    // wrong protocol
		"{}/empty",                // empty variable name
		"{{nested}}/path",         // nested braces
	}

	for _, tc := range invalidCases {
		assert.False(t, re.MatchString(tc), "Expected %q to NOT match pattern", tc)
	}
}
