package proxy

import (
	"net/http"
	"net/http/httptest"
	"testing"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/config"
)

func Test_parseRegionAndCloudProvider(t *testing.T) {
	tests := []struct {
		name       string
		urlPath    string
		wantCP     globalcommonv1.CloudProvider_Name
		wantRegion globalcommonv1.Region_Name
		wantErr    bool
	}{
		{
			name:       "aws",
			urlPath:    "/proxy/regions/ap-southeast-5/cloudProviders/aws/api/dedicated/v1/k8sInfos",
			wantCP:     globalcommonv1.CloudProvider_AWS,
			wantRegion: globalcommonv1.Region_AWS_AP_SOUTHEAST_5,
		},
		{
			name:       "gcp",
			urlPath:    "/proxy/regions/us-west1/cloudProviders/gcp/api/",
			wantCP:     globalcommonv1.CloudProvider_GCP,
			wantRegion: globalcommonv1.Region_GCP_US_WEST1,
		},
		{
			name:       "azure",
			urlPath:    "/proxy/regions/eastus/cloudProviders/azure/api/",
			wantCP:     globalcommonv1.CloudProvider_AZURE,
			wantRegion: globalcommonv1.Region_AZURE_EASTUS,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotCp, gotRegion, err := parseRegionAndCloudProvider(tt.urlPath)
			if (err != nil) != tt.wantErr {
				t.Errorf("parseRegionAndCloudProvider() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if gotCp != tt.wantCP {
				t.Errorf("parseRegionAndCloudProvider() gotCp = %v, want %v", gotCp, tt.wantCP)
			}
			if gotRegion != tt.wantRegion {
				t.Errorf("parseRegionAndCloudProvider() gotRegion = %v, want %v", gotRegion, tt.wantRegion)
			}
		})
	}
}

func Test_parseNextRegionAndCloudProvider(t *testing.T) {
	tests := []struct {
		name       string
		urlPath    string
		wantCP     globalcommonv1.CloudProvider_Name
		wantRegion string
		wantErr    bool
	}{
		{
			name:       "aws",
			urlPath:    "/proxy/nextgen/regions/ap-southeast-5/cloudProviders/aws/dataplane-manager/api/",
			wantCP:     globalcommonv1.CloudProvider_AWS,
			wantRegion: "ap-southeast-5",
		},
		{
			name:       "gcp",
			urlPath:    "/proxy/nextgen/regions/us-west1/cloudProviders/gcp/dataplane-manager/api/",
			wantCP:     globalcommonv1.CloudProvider_GCP,
			wantRegion: "us-west1",
		},
		{
			name:       "azure",
			urlPath:    "/proxy/nextgen/regions/eastus/cloudProviders/azure/api/",
			wantCP:     globalcommonv1.CloudProvider_AZURE,
			wantRegion: "eastus",
		},
		{
			name:       "readonly aws",
			urlPath:    "/proxy/readonly/nextgen/regions/us-east-1-ng/cloudProviders/aws/dataplane-manager/api/v1/proxy/controlPlane/api/v1/namespaces",
			wantCP:     globalcommonv1.CloudProvider_AWS,
			wantRegion: "us-east-1-ng",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotCp, gotRegion, err := parseNextRegionAndCloudProvider(tt.urlPath)
			if (err != nil) != tt.wantErr {
				t.Errorf("parseNextRegionAndCloudProvider() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if gotCp != tt.wantCP {
				t.Errorf("parseNextRegionAndCloudProvider() gotCp = %v, want %v", gotCp, tt.wantCP)
			}
			if gotRegion != tt.wantRegion {
				t.Errorf("parseNextRegionAndCloudProvider() gotRegion = %v, want %v", gotRegion, tt.wantRegion)
			}
		})
	}
}

func Test_getByBaseURL(t *testing.T) {
	tests := []struct {
		name       string
		reqPath    string
		wantPath   string
		wantHost   string
		wantScheme string
	}{
		{
			name:       "nextgen region path",
			reqPath:    "/proxy/nextgen/regions/us-east-1-ng/cloudProviders/aws/dataplane-manager/api/v1/proxy/controlPlane/api/v1/namespaces",
			wantPath:   "/dataplane-manager/api/v1/proxy/controlPlane/api/v1/namespaces",
			wantHost:   "us-east-1-ng.regional-api.dev.aws.tidbcloud.com",
			wantScheme: "https",
		},
		{
			name:       "readonly dataplane path",
			reqPath:    "/proxy/readonly/nextgen/regions/us-east-1-ng/cloudProviders/aws/dataplane-manager/api/v1/proxy/dataplanes/dp-1/api/v1/namespaces",
			wantPath:   "/dataplane-manager/api/v1/proxy/dataplanes/dp-1/api/v1/namespaces",
			wantHost:   "us-east-1-ng.regional-api.dev.aws.tidbcloud.com",
			wantScheme: "https",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			req := httptest.NewRequest(http.MethodGet, tt.reqPath, http.NoBody)

			target, err := getByBaseURL(req, "https://us-east-1-ng.regional-api.dev.aws.tidbcloud.com")
			if err != nil {
				t.Fatalf("getByBaseURL() error = %v", err)
			}

			if req.URL.Path != tt.wantPath {
				t.Fatalf("getByBaseURL() req.URL.Path = %q, want %q", req.URL.Path, tt.wantPath)
			}
			if target.Host != tt.wantHost {
				t.Fatalf("getByBaseURL() target.Host = %q, want %q", target.Host, tt.wantHost)
			}
			if target.Scheme != tt.wantScheme {
				t.Fatalf("getByBaseURL() target.Scheme = %q, want %q", target.Scheme, tt.wantScheme)
			}
		})
	}
}

func Test_authHandler(t *testing.T) {
	cfg := &config.Config{
		ServerRuntimeConfig: &config.ServerRuntimeConfig{APIKey: "expected-token"},
	}

	t.Run("rejects missing token", func(t *testing.T) {
		req := httptest.NewRequest(
			http.MethodGet,
			"/proxy/nextgen/regions/us-east-1-ng/cloudProviders/aws/dataplane-manager/api/v1/ping",
			http.NoBody,
		)
		rec := httptest.NewRecorder()

		authHandler(cfg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusNoContent)
		})).ServeHTTP(rec, req)

		if rec.Code != http.StatusUnauthorized {
			t.Fatalf("authHandler() status = %d, want %d", rec.Code, http.StatusUnauthorized)
		}
	})

	t.Run("accepts valid token", func(t *testing.T) {
		req := httptest.NewRequest(
			http.MethodGet,
			"/proxy/nextgen/regions/us-east-1-ng/cloudProviders/aws/dataplane-manager/api/v1/ping",
			http.NoBody,
		)
		req.Header.Set("Authorization", "Bearer expected-token")
		rec := httptest.NewRecorder()

		authHandler(cfg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusNoContent)
		})).ServeHTTP(rec, req)

		if rec.Code != http.StatusNoContent {
			t.Fatalf("authHandler() status = %d, want %d", rec.Code, http.StatusNoContent)
		}
	})
}

func Test_noAuthHandler(t *testing.T) {
	req := httptest.NewRequest(http.MethodGet, readonlyControlPlaneProxyRoute, http.NoBody)
	rec := httptest.NewRecorder()

	noAuthHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNoContent)
	})).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent {
		t.Fatalf("noAuthHandler() status = %d, want %d", rec.Code, http.StatusNoContent)
	}
}

func Test_parseNextGenCluterId(t *testing.T) {
	tests := []struct {
		name    string
		urlPath string
		wantId  uint64
		wantErr bool
	}{
		{
			name:    "valid cluster ID",
			urlPath: "proxy/clusters/10516237222821039467/cluster-agent/tidb10516237222821039467/api/v1/clusters/10516237222821039467/topology",
			wantId:  10516237222821039467,
		},
		{
			name:    "valid next gen cluster ID",
			urlPath: "/proxy/nextgen/clusters/10369150639897641647/dataplane/proxy/clusters/10369150639897641647/cluster-agent/tidb1811334817119513/api/v1/clusters/1811334817119513/topology",
			wantId:  10369150639897641647,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotId, err := parseClusterID(tt.urlPath)
			if (err != nil) != tt.wantErr {
				t.Errorf("parseNextGenClusterId() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if gotId != tt.wantId {
				t.Errorf("parseNextGenClusterId() gotId = %v, want %v", gotId, tt.wantId)
			}
		})
	}
}
