package route

import (
	"reflect"
	"testing"
)

func TestParsePath(t *testing.T) {
	tests := []struct {
		name     string
		path     string
		wantType PathType
		wantVars *PathVars
		wantErr  bool
	}{
		{
			name:     "cluster pattern",
			path:     "/proxy/cluster/42/pd/api/v1/status",
			wantType: PathTypeCluster,
			wantVars: &PathVars{
				ClusterID:  "42",
				ServiceKey: "pd",
				Namespace:  "tidb42",
				Remaining:  "/api/v1/status",
			},
		},
		{
			name:     "service pattern",
			path:     "/proxy/services/my-svc/namespaces/default/ports/8080/api",
			wantType: PathTypeService,
			wantVars: &PathVars{
				ServiceKey:      "my-svc",
				ResolvedService: "my-svc", // Service routes have pre-resolved service name
				Namespace:       "default",
				Port:            "8080",
				Remaining:       "/api",
			},
		},
		{
			name:     "cluster pattern no remaining path",
			path:     "/proxy/cluster/123/tikv",
			wantType: PathTypeCluster,
			wantVars: &PathVars{
				ClusterID:  "123",
				ServiceKey: "tikv",
				Namespace:  "tidb123",
				Remaining:  "",
			},
		},
		{
			name:    "invalid path",
			path:    "/invalid/path",
			wantErr: true,
		},
		{
			name:    "empty path",
			path:    "",
			wantErr: true,
		},
		{
			name:    "partial cluster path",
			path:    "/proxy/cluster/42",
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			vars, pt, err := parsePath(tt.path)

			if tt.wantErr {
				if err == nil {
					t.Error("expected error, got nil")
				}
				return
			}

			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			if pt != tt.wantType {
				t.Fatalf("PathType: got %v, want %v", pt, tt.wantType)
			}

			if vars.ClusterID != tt.wantVars.ClusterID {
				t.Errorf("ClusterID: got %q, want %q", vars.ClusterID, tt.wantVars.ClusterID)
			}
			if vars.ServiceKey != tt.wantVars.ServiceKey {
				t.Errorf("ServiceKey: got %q, want %q", vars.ServiceKey, tt.wantVars.ServiceKey)
			}
			if vars.ResolvedService != tt.wantVars.ResolvedService {
				t.Errorf("ResolvedService: got %q, want %q", vars.ResolvedService, tt.wantVars.ResolvedService)
			}
			if vars.Namespace != tt.wantVars.Namespace {
				t.Errorf("Namespace: got %q, want %q", vars.Namespace, tt.wantVars.Namespace)
			}
			if vars.Port != tt.wantVars.Port {
				t.Errorf("Port: got %q, want %q", vars.Port, tt.wantVars.Port)
			}
			if vars.Remaining != tt.wantVars.Remaining {
				t.Errorf("Remaining: got %q, want %q", vars.Remaining, tt.wantVars.Remaining)
			}
		})
	}
}

func TestParsePrefixPattern(t *testing.T) {
	tests := []struct {
		name       string
		prefix     string
		wantNil    bool // pattern and params should both be nil
		wantParams []string
		wantRegex  string // expected regex pattern string
		wantErr    bool
	}{
		// No params scenarios
		{
			name:    "no params - simple prefix",
			prefix:  "/pd",
			wantNil: true,
		},
		{
			name:    "no params - nested prefix",
			prefix:  "/api/v1/status",
			wantNil: true,
		},
		{
			name:    "no params - empty prefix",
			prefix:  "",
			wantNil: true,
		},
		// Single param scenarios
		{
			name:       "single param at end",
			prefix:     "/tibr/{name}",
			wantParams: []string{"name"},
			wantRegex:  `^/tibr/([^/]+)(/.*)?$`,
		},
		{
			name:       "single param with trailing slash",
			prefix:     "/tibr/{name}/",
			wantParams: []string{"name"},
			wantRegex:  `^/tibr/([^/]+)(/.*)?$`,
		},
		{
			name:       "single param in middle",
			prefix:     "/api/{version}/status",
			wantParams: []string{"version"},
			wantRegex:  `^/api/([^/]+)/status(/.*)?$`,
		},
		{
			name:       "single param without leading slash",
			prefix:     "tibr/{name}",
			wantParams: []string{"name"},
			wantRegex:  `^/tibr/([^/]+)(/.*)?$`,
		},
		// Multiple params scenarios
		{
			name:       "multiple params",
			prefix:     "/api/{version}/{resource}",
			wantParams: []string{"version", "resource"},
			wantRegex:  `^/api/([^/]+)/([^/]+)(/.*)?$`,
		},
		{
			name:       "multiple params with static suffix",
			prefix:     "/{org}/{repo}/issues",
			wantParams: []string{"org", "repo"},
			wantRegex:  `^/([^/]+)/([^/]+)/issues(/.*)?$`,
		},
		// Special character escaping
		{
			name:       "prefix with regex special chars",
			prefix:     "/api.v1/{id}",
			wantParams: []string{"id"},
			wantRegex:  `^/api\.v1/([^/]+)(/.*)?$`,
		},
		// Error scenarios
		{
			name:    "unmatched open brace",
			prefix:  "/tibr/{name",
			wantErr: true,
		},
		{
			name:    "empty param name",
			prefix:  "/tibr/{}",
			wantErr: true,
		},
		{
			name:    "param name with slash",
			prefix:  "/tibr/{na/me}",
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pattern, params, err := parsePrefixPattern(tt.prefix)

			// Check error case
			if tt.wantErr {
				if err == nil {
					t.Fatal("expected error, got nil")
				}
				return
			}

			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			// Check nil return case
			if tt.wantNil {
				if pattern != nil {
					t.Errorf("expected nil pattern, got %v", pattern)
				}
				if params != nil {
					t.Errorf("expected nil params, got %v", params)
				}
				return
			}

			// Check normal return case
			if pattern == nil {
				t.Fatal("expected non-nil pattern, got nil")
			}
			if !reflect.DeepEqual(params, tt.wantParams) {
				t.Errorf("params mismatch\ngot:  %v\nwant: %v", params, tt.wantParams)
			}
			if pattern.String() != tt.wantRegex {
				t.Errorf("regex mismatch\ngot:  %s\nwant: %s", pattern.String(), tt.wantRegex)
			}
		})
	}
}
