package urls

import (
	"context"
	"errors"
	"net/http"
	"testing"

	"github.com/jarcoal/httpmock"
	"github.com/stretchr/testify/require"

	"github.com/pingcap-inc/clinic/pkg/db"
	"github.com/pingcap-inc/clinic/pkg/o11y"
)

func newTestResolver(t *testing.T) *ClusterURLsResolver {
	t.Helper()
	cli, err := o11y.New(
		"https://gs.example.com",
		"www.ds.%s.%s.example.com",
		"client-id",
		"client-secret",
		"prod",
	)
	require.NoError(t, err)
	return NewClusterURLsResolver(cli)
}

func TestClusterURLsResolverMetricsURLHelpers(t *testing.T) {
	resolver := newTestResolver(t)

	require.Equal(
		t,
		"https://www.ds.us-west-2.aws.example.com/internal/v2/metrics/multitenant/api/v1/",
		resolver.multitenantMetricsURL("us-west-2", "aws"),
	)
	require.Equal(
		t,
		"https://www.ds.us-west-2.aws.example.com/historical/metrics/api/v1/tidbcloud/orgs/100/projects/200/clusters/300/prometheus/",
		resolver.historicalMetricsURL("us-west-2", "aws", &o11y.ListClusterParam{
			BizType:       "tidbcloud",
			TenantID:      "100",
			ProjectID:     "200",
			ApplicationID: "300",
		}),
	)
	require.Equal(
		t,
		"https://www.ds.us-west-2.aws.example.com/historical/metrics/api/v1/tidbcloud-nextgen/orgs/shared-multi-tenant/clusters/300/prometheus/",
		resolver.historicalMetricsURL("us-west-2", "aws", &o11y.ListClusterParam{
			BizType:       "tidbcloud-nextgen",
			TenantID:      "shared-multi-tenant",
			ApplicationID: "300",
		}),
	)
	require.Equal(
		t,
		"https://metrics.example.com/internal/metrics/cluster-id/api/v1/",
		resolver.internalReadMetricsURL(&o11y.ClusterInfo{
			InternalReadUri: "https://metrics.example.com/internal/metrics/cluster-id/api/v1/",
		}),
	)
	require.Equal(
		t,
		"https://metrics.example.com/internal/v2/metrics/multitenant/api/v1/?extra_filters%5B%5D=vm_account_id%3D%22123%22",
		resolver.multitenantQueryMetricsURL(&o11y.ClusterInfo{
			MultitenantQueryUri: "https://metrics.example.com/internal/v2/metrics/multitenant/api/v1/?extra_filters%5B%5D=vm_account_id%3D%22123%22",
		}),
	)
	require.Empty(t, resolver.internalReadMetricsURL(nil))
	require.Empty(t, resolver.multitenantQueryMetricsURL(nil))
	require.Empty(t, resolver.historicalMetricsURL("us-west-2", "aws", nil))
}

func TestIsMultitenantSharedScopeMetricsTarget(t *testing.T) {
	tests := []struct {
		name    string
		cluster *db.Cluster
		want    bool
	}{
		{name: "nil"},
		{
			name:    "premium resource pool",
			cluster: &db.Cluster{DeployType: o11y.NgHostType, DeployTypeV2: o11y.DeployTypeV2PremiumResourcePool},
			want:    true,
		},
		{
			name:    "premium reserved resource pool",
			cluster: &db.Cluster{DeployType: o11y.NgHostType, DeployTypeV2: o11y.DeployTypeV2PremiumReservedResourcePool},
			want:    true,
		},
		{
			name:    "premium user scope",
			cluster: &db.Cluster{DeployType: o11y.PremiumType, DeployTypeV2: o11y.DeployTypeV2Premium},
		},
		{
			name:    "premium reserved user scope",
			cluster: &db.Cluster{DeployType: o11y.PremiumType, DeployTypeV2: o11y.DeployTypeV2PremiumReserved},
		},
		{
			name:    "essential v2 resource pool",
			cluster: &db.Cluster{DeployType: o11y.NgHostType, DeployTypeV2: o11y.DeployTypeV2EssentialV2ResourcePool},
			want:    true,
		},
		{
			name:    "essential v2 user scope",
			cluster: &db.Cluster{DeployType: o11y.PremiumType, DeployTypeV2: o11y.DeployTypeV2EssentialV2},
		},
		{
			name:    "byoc resource pool",
			cluster: &db.Cluster{DeployType: o11y.ByocHostType, DeployTypeV2: o11y.DeployTypeV2ByocResourcePool},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			require.Equal(t, tt.want, isMultitenantSharedScopeMetricsTarget(tt.cluster))
		})
	}
}

func TestLegacyLokiURLFromMetricsURL(t *testing.T) {
	got, err := legacyLokiURLFromMetricsURL("https://www.ds.us-west-2.aws.example.com/internal/metrics/cluster-id/api/v1/")
	require.NoError(t, err)
	require.Equal(t, "https://www.ds.us-west-2.aws.example.com/loki/self-monitoring", got)

	got, err = legacyLokiURLFromMetricsURL("")
	require.NoError(t, err)
	require.Empty(t, got)

	_, err = legacyLokiURLFromMetricsURL("://bad-url")
	require.Error(t, err)
}

func TestStatementsS3URLFromSlowQueryURL(t *testing.T) {
	tests := []struct {
		name         string
		slowQueryURL string
		want         string
	}{
		{
			name:         "s3",
			slowQueryURL: "s3://bucket/deltalake/100/o11y-id/slowlogs",
			want:         "s3://bucket/deltalake/100/o11y-id/persisted_statements_summary",
		},
		{
			name:         "trailing slash",
			slowQueryURL: "gs://bucket/deltalake/100/o11y-id/slowlogs/",
			want:         "gs://bucket/deltalake/100/o11y-id/persisted_statements_summary",
		},
		{
			name:         "slowlogs is not the terminal path segment",
			slowQueryURL: "s3://bucket/deltalake/100/o11y-id/slowlogs/archive",
		},
		{
			name:         "different terminal path segment",
			slowQueryURL: "s3://bucket/deltalake/100/o11y-id/slowlogs-backup",
		},
		{
			name: "empty",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			require.Equal(t, tt.want, statementsS3URLFromSlowQueryURL(tt.slowQueryURL))
		})
	}
}

func TestIsPremiumStatementsCluster(t *testing.T) {
	tests := []struct {
		name    string
		cluster *db.Cluster
		want    bool
	}{
		{name: "nil cluster"},
		{
			name:    "premium",
			cluster: &db.Cluster{DeployType: o11y.PremiumType, DeployTypeV2: o11y.DeployTypeV2Premium},
			want:    true,
		},
		{
			name:    "premium reserved",
			cluster: &db.Cluster{DeployType: o11y.PremiumType, DeployTypeV2: o11y.DeployTypeV2PremiumReserved},
			want:    true,
		},
		{
			name:    "premium resource pool",
			cluster: &db.Cluster{DeployType: o11y.NgHostType, DeployTypeV2: o11y.DeployTypeV2PremiumResourcePool},
			want:    true,
		},
		{
			name:    "premium reserved resource pool",
			cluster: &db.Cluster{DeployType: o11y.NgHostType, DeployTypeV2: o11y.DeployTypeV2PremiumReservedResourcePool},
			want:    true,
		},
		{
			name:    "legacy premium without deploy type v2",
			cluster: &db.Cluster{DeployType: o11y.PremiumType},
			want:    true,
		},
		{
			name:    "essential v2 with legacy premium deploy type",
			cluster: &db.Cluster{DeployType: o11y.PremiumType, DeployTypeV2: o11y.DeployTypeV2EssentialV2},
		},
		{
			name:    "essential v2 resource pool",
			cluster: &db.Cluster{DeployType: o11y.NgHostType, DeployTypeV2: o11y.DeployTypeV2EssentialV2ResourcePool},
		},
		{
			name:    "byoc",
			cluster: &db.Cluster{DeployType: o11y.ByocType, DeployTypeV2: o11y.DeployTypeV2Byoc},
		},
		{
			name:    "dedicated",
			cluster: &db.Cluster{DeployType: o11y.DedicatedType, DeployTypeV2: o11y.DeployTypeV2Dedicated},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			require.Equal(t, tt.want, isPremiumStatementsCluster(tt.cluster))
		})
	}
}

func TestLogStatsS3URL(t *testing.T) {
	resolver := newTestResolver(t)

	require.Equal(
		t,
		"s3://o11y-prod-shared-us-east-1/deltalake/global/log-stats/",
		resolver.logStatsS3URL(&db.Cluster{
			Provider:   "aws",
			Region:     "us-east-1",
			DeployType: o11y.DedicatedType,
		}, "s3", ""),
	)
	require.Equal(
		t,
		"s3://o11y-prod-shared-ap-southeast-2-ng/deltalake/global/log-stats/",
		resolver.logStatsS3URL(&db.Cluster{
			Provider:   "aws",
			Region:     "ap-southeast-2",
			DeployType: o11y.PremiumType,
		}, "s3", ""),
	)
	require.Equal(
		t,
		"s3://o11y-prod-shared-us-west-2-premium-staging/deltalake/global/log-stats/",
		resolver.logStatsS3URL(&db.Cluster{
			Provider:   "aws",
			Region:     "us-west-2",
			DeployType: o11y.PremiumType,
		}, "s3", "-staging"),
	)
	require.Empty(t, resolver.logStatsS3URL(&db.Cluster{
		Provider:   "alicloud",
		Region:     "ap-southeast-1",
		DeployType: o11y.PremiumType,
	}, "oss", ""))
}

func TestParseControlPlaneInfo(t *testing.T) {
	projectID, provider, region, applicationID, err := parseControlPlaneInfo("us-east-1-f03/aws-us-east-1/devtier-infra")
	require.NoError(t, err)
	require.Equal(t, "us-east-1-f03", projectID)
	require.Equal(t, "aws", provider)
	require.Equal(t, "us-east-1", region)
	require.Equal(t, "devtier-infra", applicationID)

	badProjectID, badProvider, badRegion, badApplicationID, err := parseControlPlaneInfo("us-east-1-f03/aws-us-east-1")
	require.Error(t, err)
	require.Empty(t, badProjectID)
	require.Empty(t, badProvider)
	require.Empty(t, badRegion)
	require.Empty(t, badApplicationID)
}

func newHTTPMockResolver(t *testing.T) *ClusterURLsResolver {
	t.Helper()
	cli, err := o11y.New(
		"www.gs.us-west-2.aws.observability-staging.pingcap.cloud",
		"www.ds.%s.%s.observability-staging.pingcap.cloud",
		"foo",
		"bar",
		"dev",
	)
	require.NoError(t, err)
	return NewClusterURLsResolver(cli)
}

func registerAccessTokenResponder() {
	httpmock.RegisterResponder(http.MethodPost, "https://tidb-soc2.us.auth0.com/oauth/token", func(r *http.Request) (*http.Response, error) {
		return httpmock.NewStringResponse(http.StatusOK, `{"access_token":"xxx","scope":"read:client_grants","expires_in":86400,"token_type":"Bearer"}`), nil
	})
}

func TestResolveLogsTargetCluster(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-devtier", query.Get("bizType"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-west-2", query.Get("region"))
			require.Equal(t, "300", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"300","bizType":"dev-devtier","id":"serverless-o11y-id","projectID":"us-west-2-f01","region":"us-west-2","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"https://www.ds.us-west-2.aws.observability-staging.pingcap.cloud/internal/metrics/serverless-o11y-id/api/v1/","logQueryUri":"https://logs.us-west-2.aws.example.com/loki/self-monitoring"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveLogsTarget(
		context.Background(),
		&db.Cluster{
			DeployType:   o11y.SharedType,
			DeployTypeV2: o11y.DeployTypeV2Starter,
			Provider:     "aws",
			Region:       "us-west-2",
			TenantID:     100,
			ClusterID:    "300",
		},
	)

	require.NoError(t, err)
	require.Equal(t, "https://logs.us-west-2.aws.example.com/loki/self-monitoring", target.LogQueryURL)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "serverless-o11y-id", target.O11yCluster.ID)
}

func TestResolveLogsTargetPremiumUsesLogQueryUri(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud-nextgen", query.Get("bizType"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "azure", query.Get("vendor"))
			require.Equal(t, "westus3", query.Get("region"))
			require.Equal(t, "premium-cluster", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"premium-cluster","bizType":"dev-tidbcloud-nextgen","id":"premium-o11y-id","projectID":"","region":"westus3","tenantID":"100","vendor":"azure","version":"1","internalReadUri":"https://www.ds.westus3.azure.observability-staging.pingcap.cloud/internal/metrics/premium-o11y-id/api/v1/","logQueryUri":"https://logs.westus3.azure.example.com/loki/self-monitoring"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveLogsTarget(
		context.Background(),
		&db.Cluster{
			DeployType:   o11y.PremiumType,
			DeployTypeV2: o11y.DeployTypeV2Premium,
			Provider:     "azure",
			Region:       "westus3",
			ClusterID:    "premium-cluster",
			Organization: db.Organization{
				DbaasOrgID: 100,
			},
		},
	)

	require.NoError(t, err)
	require.Equal(t, "https://logs.westus3.azure.example.com/loki/self-monitoring", target.LogQueryURL)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "premium-o11y-id", target.O11yCluster.ID)
}

func TestResolveLogsTargetByocResourcePoolUsesLogQueryURI(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "https://www.gs.us-west-2.aws.observability-dev.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "byoc", query.Get("bizType"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-west-2", query.Get("region"))
			require.Equal(t, "2034353583595458560", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"2034353583595458560","bizType":"byoc","id":"byoc-resource-pool-o11y-id","projectID":"","region":"us-west-2","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"https://www.ds.observability.tidbcloud.com/internal/metrics/byoc-resource-pool-o11y-id/api/v1/","logQueryUri":"https://www.ds.observability.tidbcloud.com/internal/loki/self-monitoring"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveLogsTarget(
		context.Background(),
		&db.Cluster{
			DeployType:   o11y.ByocHostType,
			DeployTypeV2: o11y.DeployTypeV2ByocResourcePool,
			Provider:     "aws",
			Region:       "us-west-2",
			ClusterID:    "2034353583595458560",
			Organization: db.Organization{DbaasOrgID: 100},
		},
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.observability.tidbcloud.com/internal/loki/self-monitoring", target.LogQueryURL)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "byoc-resource-pool-o11y-id", target.O11yCluster.ID)
}

func TestResolveUsesBYOCLogQueryURI(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "https://www.gs.us-west-2.aws.observability-dev.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "byoc", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-west-2", query.Get("region"))
			require.Equal(t, "true", query.Get("showDeleted"))
			require.Empty(t, query.Get("projectID"))

			applicationID := query.Get("applicationID")
			tenantID := query.Get("tenantID")
			var content string
			switch applicationID + ":" + tenantID {
			case "10954487466006069299:200":
				content = `{"code":0,"data":{"clusters":[{"applicationID":"10954487466006069299","bizType":"byoc","id":"byoc-k8s-o11y-id","projectID":"","region":"us-west-2","tenantID":"200","vendor":"aws","version":"1","internalReadUri":"https://metrics.example.com/internal/metrics/byoc-k8s-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			case "byoc-data-plane:200":
				content = `{"code":0,"data":{"clusters":[{"applicationID":"byoc-data-plane","bizType":"byoc","id":"byoc-infra-o11y-id","projectID":"","region":"us-west-2","tenantID":"200","vendor":"aws","version":"1","internalReadUri":"https://metrics.example.com/internal/metrics/byoc-infra-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			case "10954487466006069299:100":
				content = `{"code":0,"data":{"clusters":[{"applicationID":"10954487466006069299","bizType":"byoc","id":"byoc-tidb-o11y-id","projectID":"","region":"us-west-2","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"https://www.ds.observability.tidbcloud.com/internal/metrics/byoc-tidb-o11y-id/api/v1/","logQueryUri":"https://www.ds.observability.tidbcloud.com/internal/loki/self-monitoring"}],"continueToken":"","remainingItemCount":0},"message":""}`
			default:
				t.Fatalf("unexpected BYOC o11y lookup application=%s tenant=%s", applicationID, tenantID)
			}
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	clusterURLs, err := newHTTPMockResolver(t).Resolve(
		context.Background(),
		&db.Cluster{
			DeployType:    o11y.ByocType,
			DeployTypeV2:  o11y.DeployTypeV2Byoc,
			Provider:      "aws",
			Region:        "us-west-2",
			TenantID:      200,
			ClusterID:     "10954487466006069299",
			DataPlaneName: "byoc-data-plane",
			Organization:  db.Organization{DbaasOrgID: 100},
		},
	)

	require.NoError(t, err)
	require.Equal(t, "https://metrics.example.com/internal/metrics/byoc-k8s-o11y-id/api/v1/", clusterURLs.ClusterMetricsURL)
	require.Equal(t, "https://www.ds.observability.tidbcloud.com/internal/loki/self-monitoring", clusterURLs.LokiURL)
	require.NotEqual(t, "https://metrics.example.com/loki/self-monitoring", clusterURLs.LokiURL)
}

func TestResolveUsesLogQueryURIForNonByocLokiURL(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud-nextgen", query.Get("bizType"))
			require.Equal(t, "azure", query.Get("vendor"))
			require.Equal(t, "westus3", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "premium-cluster", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"premium-cluster","bizType":"dev-tidbcloud-nextgen","id":"premium-o11y-id","projectID":"","region":"westus3","tenantID":"100","vendor":"azure","version":"1","internalReadUri":"https://www.ds.westus3.azure.observability-staging.pingcap.cloud/internal/metrics/premium-o11y-id/api/v1/","logQueryUri":"https://logs.westus3.azure.example.com/loki/self-monitoring"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	clusterURLs, err := newHTTPMockResolver(t).Resolve(
		context.Background(),
		&db.Cluster{
			DeployType:   o11y.PremiumType,
			DeployTypeV2: o11y.DeployTypeV2Premium,
			Provider:     "azure",
			Region:       "westus3",
			ClusterID:    "premium-cluster",
			Organization: db.Organization{
				DbaasOrgID: 100,
			},
		},
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.westus3.azure.observability-staging.pingcap.cloud/internal/v2/metrics/multitenant/api/v1/", clusterURLs.ClusterMetricsURL)
	require.Equal(t, clusterURLs.ClusterMetricsURL, clusterURLs.DataPlaneMetricsURL)
	require.Equal(t, "abfss://o11y-prod-shared-westus3-staging/deltalake/100/premium-o11y-id/slowlogs", clusterURLs.SlowQueryS3URL)
	require.Equal(t, "abfss://o11y-prod-shared-westus3-staging/deltalake/100/premium-o11y-id/persisted_statements_summary", clusterURLs.StatementsS3URL)
	require.Equal(t, "https://logs.westus3.azure.example.com/loki/self-monitoring", clusterURLs.LokiURL)
}

func TestResolveEssentialV2UsesCanonicalStorageInfo(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud-nextgen", query.Get("bizType"))
			require.Equal(t, "1372813089209332080", query.Get("tenantID"))
			require.Equal(t, "essential-v2-cluster", query.Get("applicationID"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"essential-v2-cluster","bizType":"dev-tidbcloud-nextgen","id":"essential-v2-o11y-id","projectID":"","region":"ap-northeast-1","tenantID":"1372813089209332080","vendor":"aws","version":"1","internalReadUri":"https://metrics.example.com/internal/metrics/essential-v2-o11y-id/api/v1/","logQueryUri":"https://logs.example.com/loki/self-monitoring","storageInfo":[{"bucketName":"canonical-slow-query-bucket","category":"slowquery","objectPath":"canonical/path/slowlogs"}]}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	clusterURLs, err := newHTTPMockResolver(t).Resolve(context.Background(), &db.Cluster{
		DeployType:   o11y.PremiumType,
		DeployTypeV2: o11y.DeployTypeV2EssentialV2,
		Provider:     "aws",
		Region:       "ap-northeast-1",
		TenantID:     1372813089209332080,
		ClusterID:    "essential-v2-cluster",
		Organization: db.Organization{DbaasOrgID: 0},
	})

	require.NoError(t, err)
	require.Equal(t, "s3://canonical-slow-query-bucket/canonical/path/slowlogs", clusterURLs.SlowQueryS3URL)
	require.Equal(t, "s3://o11y-prod-shared-ap-northeast-1-staging/deltalake/org=1372813089209332080/cluster=essential-v2-cluster/type=topsql/", clusterURLs.TopSQLS3URL)
	require.NotContains(t, clusterURLs.SlowQueryS3URL, "/0/")
	require.NotContains(t, clusterURLs.TopSQLS3URL, "org=0")
}

func TestResolveEssentialV2FallsBackToCanonicalTenantTemplate(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			content := `{"code":0,"data":{"clusters":[{"applicationID":"essential-v2-cluster","bizType":"dev-tidbcloud-nextgen","id":"essential-v2-o11y-id","projectID":"","region":"ap-northeast-1","tenantID":"1372813089209332080","vendor":"aws","version":"1","internalReadUri":"https://metrics.example.com/internal/metrics/essential-v2-o11y-id/api/v1/","logQueryUri":"https://logs.example.com/loki/self-monitoring"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	clusterURLs, err := newHTTPMockResolver(t).Resolve(context.Background(), &db.Cluster{
		DeployType:   o11y.PremiumType,
		DeployTypeV2: o11y.DeployTypeV2EssentialV2,
		Provider:     "aws",
		Region:       "ap-northeast-1",
		TenantID:     1372813089209332080,
		ClusterID:    "essential-v2-cluster",
		Organization: db.Organization{DbaasOrgID: 0},
	})

	require.NoError(t, err)
	require.Equal(t, "s3://o11y-prod-shared-ap-northeast-1-premium-staging/deltalake/1372813089209332080/essential-v2-o11y-id/slowlogs", clusterURLs.SlowQueryS3URL)
	require.Equal(t, "s3://o11y-prod-shared-ap-northeast-1-staging/deltalake/org=1372813089209332080/cluster=essential-v2-cluster/type=topsql/", clusterURLs.TopSQLS3URL)
}

func TestResolveEssentialV2RejectsZeroCanonicalTenant(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			content := `{"code":0,"data":{"clusters":[{"applicationID":"essential-v2-cluster","bizType":"dev-tidbcloud-nextgen","id":"essential-v2-o11y-id","projectID":"","region":"ap-northeast-1","tenantID":"0","vendor":"aws","version":"1"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	clusterURLs, err := newHTTPMockResolver(t).Resolve(context.Background(), &db.Cluster{
		DeployType:   o11y.PremiumType,
		DeployTypeV2: o11y.DeployTypeV2EssentialV2,
		Provider:     "aws",
		Region:       "ap-northeast-1",
		ClusterID:    "essential-v2-cluster",
		Organization: db.Organization{DbaasOrgID: 0},
	})

	require.ErrorContains(t, err, "canonical Essential V2 storage tenant is empty")
	require.Nil(t, clusterURLs)
}

func TestResolvePremiumAWSSouth1UsesInternalReadURI(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud-nextgen", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "ap-south-1", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "premium-cluster", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"premium-cluster","bizType":"dev-tidbcloud-nextgen","id":"premium-o11y-id","projectID":"","region":"ap-south-1","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"https://www.ds.ap-south-1.aws.observability-staging.pingcap.cloud/internal/v2/metrics/premium-o11y-id/api/v1/","logQueryUri":"https://logs.ap-south-1.aws.example.com/loki/self-monitoring"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	clusterURLs, err := newHTTPMockResolver(t).Resolve(
		context.Background(),
		&db.Cluster{
			DeployType: o11y.PremiumType,
			Provider:   "aws",
			Region:     "ap-south-1",
			ClusterID:  "premium-cluster",
			Organization: db.Organization{
				DbaasOrgID: 100,
			},
		},
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.ap-south-1.aws.observability-staging.pingcap.cloud/internal/v2/metrics/premium-o11y-id/api/v1/", clusterURLs.ClusterMetricsURL)
	require.Equal(t, clusterURLs.ClusterMetricsURL, clusterURLs.DataPlaneMetricsURL)
	require.NotContains(t, clusterURLs.ClusterMetricsURL, "/internal/v2/metrics/multitenant/")
	require.Equal(t, "https://logs.ap-south-1.aws.example.com/loki/self-monitoring", clusterURLs.LokiURL)
}

func TestResolveMetricsTargetDedicatedUsesShowDeletedURL(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	seen := map[string]bool{}
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-east-1", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Empty(t, query.Get("projectID"))

			key := query.Get("applicationID") + ":" + query.Get("showDeleted")
			seen[key] = true

			switch key {
			case "300:true":
				content := `{"code":0,"data":{"clusters":[{"applicationID":"300","bizType":"dev-tidbcloud","id":"deleted-cluster-o11y-id","projectID":"old-project","region":"us-east-1","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"https://www.ds.us-east-1.aws.observability-staging.pingcap.cloud/historical/metrics/api/v1/dev-tidbcloud/orgs/100/projects/old-project/clusters/300/prometheus/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
				return httpmock.NewStringResponse(http.StatusOK, content), nil
			default:
				t.Fatalf("unexpected o11y cluster lookup: %s", r.URL.RawQuery)
				return nil, nil
			}
		})

	target, err := newHTTPMockResolver(t).ResolveMetricsTarget(
		context.Background(),
		&db.Cluster{
			DeployType: o11y.DedicatedType,
			Provider:   "aws",
			Region:     "us-east-1",
			TenantID:   100,
			ProjectID:  "new-project",
			ClusterID:  "300",
			Organization: db.Organization{
				DbaasOrgID: 100,
			},
		},
		MetricsScopeChild,
		"",
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.us-east-1.aws.observability-staging.pingcap.cloud/historical/metrics/api/v1/dev-tidbcloud/orgs/100/projects/old-project/clusters/300/prometheus/api/v1/", target.MetricsURL)
	require.True(t, seen["300:true"])
	require.False(t, seen["300:"])
	require.False(t, seen["k8s-infra:true"])
	require.False(t, seen["k8s-infra:"])
}

func TestResolveDedicatedUsesO11YProjectForK8sLookup(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-east-1", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			switch query.Get("applicationID") {
			case "300":
				require.Empty(t, query.Get("projectID"))
				content := `{"code":0,"data":{"clusters":[{"applicationID":"300","bizType":"dev-tidbcloud","id":"cluster-o11y-id","projectID":"old-project","region":"us-east-1","tenantID":"100","vendor":"aws","internalReadUri":"https://metrics.example.com/cluster/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
				return httpmock.NewStringResponse(http.StatusOK, content), nil
			case "k8s-infra":
				require.Equal(t, "old-project", query.Get("projectID"))
				content := `{"code":0,"data":{"clusters":[{"applicationID":"k8s-infra","bizType":"dev-tidbcloud","id":"k8s-o11y-id","projectID":"old-project","region":"us-east-1","tenantID":"100","vendor":"aws","internalReadUri":"https://metrics.example.com/data-plane/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
				return httpmock.NewStringResponse(http.StatusOK, content), nil
			default:
				t.Fatalf("unexpected o11y cluster lookup: %s", r.URL.RawQuery)
				return nil, nil
			}
		})

	clusterURLs, err := newHTTPMockResolver(t).Resolve(
		context.Background(),
		&db.Cluster{
			DeployType: o11y.DedicatedType,
			Provider:   "aws",
			Region:     "us-east-1",
			TenantID:   100,
			ProjectID:  "new-project",
			ClusterID:  "300",
			Organization: db.Organization{
				DbaasOrgID: 100,
			},
		},
	)

	require.NoError(t, err)
	require.Equal(t, "https://metrics.example.com/cluster/api/v1/", clusterURLs.ClusterMetricsURL)
	require.Equal(t, "https://metrics.example.com/data-plane/api/v1/", clusterURLs.DataPlaneMetricsURL)
	require.Empty(t, clusterURLs.StatementsS3URL)
}

func TestResolveMetricsTargetPremiumResourcePoolUsesMultitenantQueryURI(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud-nextgen", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "ap-south-1", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "premium-cluster", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"premium-cluster","bizType":"dev-tidbcloud-nextgen","id":"premium-o11y-id","projectID":"","region":"ap-south-1","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"https://www.ds.ap-south-1.aws.observability-staging.pingcap.cloud/internal/v2/metrics/premium-o11y-id/api/v1/","multitenantQueryUri":"https://www.ds.ap-south-1.aws.observability-staging.pingcap.cloud/internal/v2/metrics/multitenant/api/v1/?extra_filters%5B%5D=vm_account_id%3D%22123%22"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveMetricsTarget(
		context.Background(),
		&db.Cluster{
			DeployType:   o11y.NgHostType,
			DeployTypeV2: o11y.DeployTypeV2PremiumResourcePool,
			Provider:     "aws",
			Region:       "ap-south-1",
			ClusterID:    "premium-cluster",
			Organization: db.Organization{
				DbaasOrgID: 100,
			},
		},
		MetricsScopeChild,
		"",
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.ap-south-1.aws.observability-staging.pingcap.cloud/internal/v2/metrics/multitenant/api/v1/?extra_filters%5B%5D=vm_account_id%3D%22123%22", target.MetricsURL)
	require.NotContains(t, target.MetricsURL, "ap-south-1-ng")
	require.Contains(t, target.MetricsURL, "/internal/v2/metrics/multitenant/")
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "premium-o11y-id", target.O11yCluster.ID)
	require.Equal(t, target.MetricsURL, target.O11yCluster.MultitenantQueryUri)
}

func TestResolveMetricsTargetPremiumResourcePoolRequiresMultitenantQueryURI(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			content := `{"code":0,"data":{"clusters":[{"applicationID":"premium-cluster","bizType":"dev-tidbcloud-nextgen","id":"premium-o11y-id","projectID":"","region":"us-west-2","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"https://metrics.example.com/internal/v2/metrics/premium-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveMetricsTarget(
		context.Background(),
		&db.Cluster{
			DeployType:   o11y.NgHostType,
			DeployTypeV2: o11y.DeployTypeV2PremiumResourcePool,
			Provider:     "aws",
			Region:       "us-west-2",
			ClusterID:    "premium-cluster",
			Organization: db.Organization{DbaasOrgID: 100},
		},
		MetricsScopeChild,
		"",
	)

	require.Nil(t, target)
	require.EqualError(t, err, "shared scope multitenant metrics URL is empty")
}

func TestResolveMetricsTargetPremiumUserScopeUsesInternalReadURI(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			content := `{"code":0,"data":{"clusters":[{"applicationID":"premium-cluster","bizType":"dev-tidbcloud-nextgen","id":"premium-o11y-id","projectID":"","region":"us-west-2","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"https://metrics.example.com/internal/v2/metrics/premium-o11y-id/api/v1/","multitenantQueryUri":"https://metrics.example.com/internal/v2/metrics/multitenant/api/v1/?extra_filters%5B%5D=vm_account_id%3D%22123%22"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveMetricsTarget(
		context.Background(),
		&db.Cluster{
			DeployType:   o11y.PremiumType,
			DeployTypeV2: o11y.DeployTypeV2Premium,
			Provider:     "aws",
			Region:       "us-west-2",
			ClusterID:    "premium-cluster",
			Organization: db.Organization{DbaasOrgID: 100},
		},
		MetricsScopeChild,
		"",
	)

	require.NoError(t, err)
	require.Equal(t, "https://metrics.example.com/internal/v2/metrics/premium-o11y-id/api/v1/", target.MetricsURL)
}

func TestResolveMetricsTargetNextGenPremiumUsesClusterTenantIDWhenOrgIDMissing(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud-nextgen", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "ap-northeast-1", query.Get("region"))
			require.Equal(t, "1372813089209332080", query.Get("tenantID"))
			require.Equal(t, "10670278233587177350", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"10670278233587177350","bizType":"dev-tidbcloud-nextgen","id":"essential-v2-o11y-id","projectID":"","region":"ap-northeast-1","tenantID":"1372813089209332080","vendor":"aws","version":"1","internalReadUri":"https://www.ds.ap-northeast-1.aws.observability-staging.pingcap.cloud/internal/v2/metrics/essential-v2-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveMetricsTarget(
		context.Background(),
		&db.Cluster{
			DeployType:   o11y.PremiumType,
			DeployTypeV2: o11y.DeployTypeV2EssentialV2,
			Provider:     "aws",
			Region:       "ap-northeast-1",
			TenantID:     1372813089209332080,
			ClusterID:    "10670278233587177350",
			Organization: db.Organization{
				DbaasOrgID: 0,
			},
		},
		MetricsScopeChild,
		"",
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.ap-northeast-1.aws.observability-staging.pingcap.cloud/internal/v2/metrics/essential-v2-o11y-id/api/v1/", target.MetricsURL)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "essential-v2-o11y-id", target.O11yCluster.ID)
}

func TestResolveMetricsTargetEssentialV2ResourcePoolUsesMultitenantQueryURI(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud-nextgen", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "ap-northeast-1", query.Get("region"))
			require.Equal(t, "shared-multi-tenant", query.Get("tenantID"))
			require.Equal(t, "2070062522060242944", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"2070062522060242944","bizType":"dev-tidbcloud-nextgen","id":"resource-pool-o11y-id","projectID":"","region":"ap-northeast-1","tenantID":"shared-multi-tenant","vendor":"aws","version":"1","internalReadUri":"https://www.ds.ap-northeast-1-premium.aws.observability-staging.pingcap.cloud/internal/v2/metrics/resource-pool-o11y-id/api/v1/","multitenantQueryUri":"https://www.ds.ap-northeast-1-premium.aws.observability-staging.pingcap.cloud/internal/v2/metrics/multitenant/api/v1/?extra_filters%5B%5D=vm_account_id%3D%22456%22"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveMetricsTarget(
		context.Background(),
		&db.Cluster{
			DeployType:   o11y.NgHostType,
			DeployTypeV2: o11y.DeployTypeV2EssentialV2ResourcePool,
			Provider:     "aws",
			Region:       "ap-northeast-1",
			ClusterID:    "2070062522060242944",
			Organization: db.Organization{
				DbaasOrgID: 0,
			},
		},
		MetricsScopeChild,
		"",
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.ap-northeast-1-premium.aws.observability-staging.pingcap.cloud/internal/v2/metrics/multitenant/api/v1/?extra_filters%5B%5D=vm_account_id%3D%22456%22", target.MetricsURL)
	require.Contains(t, target.MetricsURL, "/internal/v2/metrics/multitenant/")
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "resource-pool-o11y-id", target.O11yCluster.ID)
	require.Equal(t, target.MetricsURL, target.O11yCluster.MultitenantQueryUri)
}

func TestResolveInternalReadTargetServerlessUsesO11YURLs(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-devtier", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-east-1", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "300", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))
			require.Empty(t, query.Get("projectID"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"300","bizType":"dev-devtier","id":"serverless-o11y-id","projectID":"us-east-1-f01","region":"us-east-1","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"https://www.ds.us-east-1.aws.observability-staging.pingcap.cloud/internal/metrics/serverless-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveInternalReadTarget(
		context.Background(),
		&db.Cluster{
			DeployType: o11y.SharedType,
			Provider:   "aws",
			Region:     "us-east-1",
			TenantID:   100,
			ClusterID:  "300",
		},
		MetricsScopeChild,
		"",
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.us-east-1.aws.observability-staging.pingcap.cloud/internal/metrics/serverless-o11y-id/api/v1/", target.InternalReadURL)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "serverless-o11y-id", target.O11yCluster.ID)
}

func TestResolveInternalReadTargetPremiumUsesInternalReadURI(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud-nextgen", query.Get("bizType"))
			require.Equal(t, "azure", query.Get("vendor"))
			require.Equal(t, "westus3", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "premium-cluster", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"premium-cluster","bizType":"dev-tidbcloud-nextgen","id":"premium-o11y-id","projectID":"","region":"westus3","tenantID":"100","vendor":"azure","version":"1","internalReadUri":"https://www.ds.westus3.azure.observability-staging.pingcap.cloud/internal/metrics/premium-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveInternalReadTarget(
		context.Background(),
		&db.Cluster{
			DeployType: o11y.PremiumType,
			Provider:   "azure",
			Region:     "westus3",
			ClusterID:  "premium-cluster",
			Organization: db.Organization{
				DbaasOrgID: 100,
			},
		},
		MetricsScopeChild,
		"",
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.westus3.azure.observability-staging.pingcap.cloud/internal/metrics/premium-o11y-id/api/v1/", target.InternalReadURL)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "premium-o11y-id", target.O11yCluster.ID)
}

func TestResolveInternalReadTargetByocHostUsesByocCluster(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "https://www.gs.us-west-2.aws.observability-dev.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "byoc", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-east-1", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "byoc-host-cluster", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))
			require.Empty(t, query.Get("projectID"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"byoc-host-cluster","bizType":"byoc","id":"byoc-host-o11y-id","projectID":"","region":"us-east-1","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"https://byoc-ds.example.com/internal/v2/metrics/byoc-host-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveInternalReadTarget(
		context.Background(),
		&db.Cluster{
			DeployType: o11y.ByocHostType,
			Provider:   "aws",
			Region:     "us-east-1",
			ClusterID:  "byoc-host-cluster",
			Organization: db.Organization{
				DbaasOrgID: 100,
			},
		},
		MetricsScopeChild,
		"",
	)

	require.NoError(t, err)
	require.Equal(t, "https://byoc-ds.example.com/internal/v2/metrics/byoc-host-o11y-id/api/v1/", target.InternalReadURL)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "byoc-host-o11y-id", target.O11yCluster.ID)
}

func TestResolveNodeMetricsTargetByocUsesDataPlaneIdentity(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "https://www.gs.us-west-2.aws.observability-dev.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "byoc", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "ap-southeast-1", query.Get("region"))
			require.Equal(t, "200", query.Get("tenantID"))
			require.Equal(t, "byoc-data-plane", query.Get("applicationID"))
			require.Empty(t, query.Get("projectID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"byoc-data-plane","bizType":"byoc","id":"byoc-infra-o11y-id","projectID":"","region":"ap-southeast-1","tenantID":"200","vendor":"aws","version":"1","internalReadUri":"https://metrics.example.com/internal/metrics/byoc-infra-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveNodeMetricsTarget(context.Background(), &db.Cluster{
		DeployType:    o11y.ByocType,
		DeployTypeV2:  o11y.DeployTypeV2Byoc,
		Provider:      "aws",
		Region:        "ap-southeast-1",
		TenantID:      200,
		ProjectID:     "mutable-dbaas-project",
		DataPlaneName: "byoc-data-plane",
	}, "")

	require.NoError(t, err)
	require.Equal(t, "https://metrics.example.com/internal/metrics/byoc-infra-o11y-id/api/v1/", target.InternalReadURL)
	require.Equal(t, "byoc-infra-o11y-id", target.O11yCluster.ID)
}

func TestResolveNodeMetricsTargetNonByocKeepsProjectLookup(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-west-2-staging", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Equal(t, "dbaas-project", query.Get("projectID"))
			require.Equal(t, o11y.ShootID, query.Get("applicationID"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"k8s-infra","bizType":"dev-tidbcloud","id":"project-infra-o11y-id","projectID":"dbaas-project","region":"us-west-2-staging","tenantID":"100","vendor":"aws","version":"1"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveNodeMetricsTarget(context.Background(), &db.Cluster{
		DeployType: o11y.DedicatedType,
		Provider:   "aws",
		Region:     "us-west-2",
		TenantID:   100,
		ProjectID:  "dbaas-project",
	}, "us-west-2-staging")

	require.NoError(t, err)
	require.Empty(t, target.InternalReadURL)
	require.Equal(t, "project-infra-o11y-id", target.O11yCluster.ID)
}

func TestResolveNodeMetricsTargetByocRejectsMissingDataPlane(t *testing.T) {
	target, err := newTestResolver(t).ResolveNodeMetricsTarget(context.Background(), &db.Cluster{
		DeployType: o11y.ByocHostType,
		Provider:   "aws",
		Region:     "us-east-1",
		TenantID:   100,
	}, "")

	require.ErrorContains(t, err, "data plane name is empty")
	require.Nil(t, target)
}

func TestResolveNodeMetricsTargetByocRejectsInvalidInternalReadURL(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "https://www.gs.us-west-2.aws.observability-dev.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			content := `{"code":0,"data":{"clusters":[{"applicationID":"byoc-data-plane","bizType":"byoc","id":"byoc-infra-o11y-id","projectID":"","region":"us-east-1","tenantID":"100","vendor":"aws","version":"1","internalReadUri":"ftp://metrics.example.com/internal/metrics/byoc-infra-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveNodeMetricsTarget(context.Background(), &db.Cluster{
		DeployType:    o11y.ByocType,
		Provider:      "aws",
		Region:        "us-east-1",
		TenantID:      100,
		DataPlaneName: "byoc-data-plane",
	}, "")

	require.ErrorContains(t, err, "internal read URL is invalid")
	require.Nil(t, target)
}

func TestResolveLogicalControlPlaneMetricsTargetSeed(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud", query.Get("bizType"))
			require.Equal(t, "infra-dev", query.Get("tenantID"))
			require.Equal(t, "gcp", query.Get("vendor"))
			require.Equal(t, "us-west1", query.Get("region"))
			require.Equal(t, "seed", query.Get("projectID"))
			require.Equal(t, "seed", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"seed","bizType":"dev-tidbcloud","id":"seed-o11y-id","projectID":"seed","region":"us-west1","tenantID":"infra-dev","vendor":"gcp","version":"1","internalReadUri":"https://www.ds.us-west1.gcp.observability-staging.pingcap.cloud/internal/metrics/seed-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveLogicalControlPlaneMetricsTarget(
		context.Background(),
		"seed/gcp-us-west1/seed",
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.us-west1.gcp.observability-staging.pingcap.cloud/internal/metrics/seed-o11y-id/api/v1/", target.MetricsURL)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "seed-o11y-id", target.O11yCluster.ID)
}

func TestResolveLogicalControlPlaneMetricsTargetNormalizesTwoPartSeed(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud", query.Get("bizType"))
			require.Equal(t, "infra-dev", query.Get("tenantID"))
			require.Equal(t, "gcp", query.Get("vendor"))
			require.Equal(t, "us-west1", query.Get("region"))
			require.Equal(t, "seed", query.Get("projectID"))
			require.Equal(t, "seed", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"seed","bizType":"dev-tidbcloud","id":"seed-o11y-id","projectID":"seed","region":"us-west1","tenantID":"infra-dev","vendor":"gcp","version":"1","internalReadUri":"https://www.ds.us-west1.gcp.observability-staging.pingcap.cloud/internal/metrics/seed-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveLogicalControlPlaneMetricsTarget(
		context.Background(),
		"seed/gcp-us-west1",
	)

	require.NoError(t, err)
	require.Equal(t, "seed-o11y-id", target.O11yCluster.ID)
}

func TestResolveLogicalControlPlaneMetricsTargetRejectsMalformedSelector(t *testing.T) {
	for _, selector := range []string{
		"seed",
		"project/aws-us-west-2",
		"seed/aws-us-west-2/seed/extra",
		"seed//seed",
		"seed/aws/seed",
	} {
		t.Run(selector, func(t *testing.T) {
			target, err := newTestResolver(t).ResolveLogicalControlPlaneMetricsTarget(context.Background(), selector)
			require.Error(t, err)
			require.ErrorIs(t, err, ErrInvalidControlPlaneInfo)
			require.Nil(t, target)
		})
	}
}

func TestResolveLogicalControlPlaneMetricsTargetFallsBackToNextGenSeed(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	requestedBizTypes := make([]string, 0, 2)
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			requestedBizTypes = append(requestedBizTypes, query.Get("bizType"))
			require.Equal(t, "infra-dev", query.Get("tenantID"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-west-2", query.Get("region"))
			require.Equal(t, "seed", query.Get("projectID"))
			require.Equal(t, "usw2-byoc-pm", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			switch query.Get("bizType") {
			case "dev-tidbcloud":
				return httpmock.NewStringResponse(http.StatusOK, `{"code":0,"data":{"clusters":[],"continueToken":"","remainingItemCount":0},"message":""}`), nil
			case "dev-tidbcloud-nextgen":
				content := `{"code":0,"data":{"clusters":[{"applicationID":"usw2-byoc-pm","bizType":"dev-tidbcloud-nextgen","id":"nextgen-seed-o11y-id","projectID":"seed","region":"us-west-2","tenantID":"infra-dev","vendor":"aws","version":"1","internalReadUri":"https://www.ds.us-west-2.aws.observability-staging.pingcap.cloud/internal/metrics/nextgen-seed-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
				return httpmock.NewStringResponse(http.StatusOK, content), nil
			default:
				return httpmock.NewStringResponse(http.StatusBadRequest, `{}`), nil
			}
		})

	target, err := newHTTPMockResolver(t).ResolveLogicalControlPlaneMetricsTarget(
		context.Background(),
		"seed/aws-us-west-2/usw2-byoc-pm",
	)

	require.NoError(t, err)
	require.Equal(t, []string{"dev-tidbcloud", "dev-tidbcloud-nextgen"}, requestedBizTypes)
	require.Equal(t, "https://www.ds.us-west-2.aws.observability-staging.pingcap.cloud/internal/metrics/nextgen-seed-o11y-id/api/v1/", target.MetricsURL)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "nextgen-seed-o11y-id", target.O11yCluster.ID)
}

func TestResolveLogicalControlPlaneMetricsTargetBothSeedBizTypesNotFound(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	requestedBizTypes := make([]string, 0, 2)
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			requestedBizTypes = append(requestedBizTypes, r.URL.Query().Get("bizType"))
			return httpmock.NewStringResponse(http.StatusOK, `{"code":0,"data":{"clusters":[],"continueToken":"","remainingItemCount":0},"message":""}`), nil
		})

	_, err := newHTTPMockResolver(t).ResolveLogicalControlPlaneMetricsTarget(
		context.Background(),
		"seed/aws-us-west-2/missing-seed",
	)

	require.Error(t, err)
	require.ErrorIs(t, err, o11y.ErrNotFound)
	require.Equal(t, []string{"dev-tidbcloud", "dev-tidbcloud-nextgen"}, requestedBizTypes)
}

func TestResolveLogicalControlPlaneMetricsTargetDoesNotFallbackOnDedicatedFailure(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	requestedBizTypes := make([]string, 0, 1)
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			requestedBizTypes = append(requestedBizTypes, r.URL.Query().Get("bizType"))
			return nil, errors.New("dedicated lookup transport failure")
		})

	_, err := newHTTPMockResolver(t).ResolveLogicalControlPlaneMetricsTarget(
		context.Background(),
		"seed/aws-us-west-2/usw2-byoc-pm",
	)

	require.Error(t, err)
	require.NotErrorIs(t, err, o11y.ErrNotFound)
	require.Equal(t, []string{"dev-tidbcloud"}, requestedBizTypes)
}

func TestResolveServerlessControlPlaneMetricsTarget(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-devtier", query.Get("bizType"))
			require.Equal(t, "dev-devtier", query.Get("tenantID"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-east-1", query.Get("region"))
			require.Equal(t, "us-east-1-f01", query.Get("projectID"))
			require.Equal(t, "devtier-infra", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"devtier-infra","bizType":"dev-devtier","id":"control-plane-o11y-id","projectID":"us-east-1-f01","region":"us-east-1","tenantID":"dev-devtier","vendor":"aws","version":"1","internalReadUri":"https://www.ds.us-east-1.aws.observability-staging.pingcap.cloud/internal/metrics/control-plane-o11y-id/api/v1/"}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveServerlessControlPlaneMetricsTarget(
		context.Background(),
		"us-east-1-f01/aws-us-east-1/devtier-infra",
	)

	require.NoError(t, err)
	require.Equal(t, "https://www.ds.us-east-1.aws.observability-staging.pingcap.cloud/internal/metrics/control-plane-o11y-id/api/v1/", target.MetricsURL)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "control-plane-o11y-id", target.O11yCluster.ID)
}

func TestResolveStorageTargetServerless(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-devtier", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-west-2", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Empty(t, query.Get("projectID"))
			require.Equal(t, "300", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"300","bizType":"dev-devtier","id":"serverless-o11y-id","projectID":"200","region":"us-west-2","tenantID":"100","vendor":"aws","version":"1","storageInfo":[{"bucketName":"bucket-from-o11y","category":"slowquery","objectPath":"path/from/o11y"},{"bucketName":"statement-bucket","category":"statements","objectPath":"statement/path"}]}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveStorageTarget(context.Background(), StorageTargetParam{
		Provider:   "aws",
		Region:     "us-west-2",
		TenantID:   "100",
		ClusterID:  "300",
		DeployType: o11y.ServerlessType,
	}, o11y.StorageCategorySlowQuery)

	require.NoError(t, err)
	require.Equal(t, "bucket-from-o11y", target.BucketName)
	require.Equal(t, "path/from/o11y", target.ObjectPath)
	require.NotNil(t, target.O11yCluster)
	require.Equal(t, "serverless-o11y-id", target.O11yCluster.ID)

	statementsTarget, err := newHTTPMockResolver(t).ResolveStorageTarget(context.Background(), StorageTargetParam{
		Provider:   "aws",
		Region:     "us-west-2",
		TenantID:   "100",
		ClusterID:  "300",
		DeployType: o11y.ServerlessType,
	}, o11y.StorageCategoryStatements)

	require.NoError(t, err)
	require.Equal(t, "statement-bucket", statementsTarget.BucketName)
	require.Equal(t, "statement/path", statementsTarget.ObjectPath)
}

func TestResolveStorageTargetRejectsNonServerless(t *testing.T) {
	target, err := newTestResolver(t).ResolveStorageTarget(context.Background(), StorageTargetParam{
		DeployType: o11y.PremiumType,
	}, o11y.StorageCategorySlowQuery)

	require.Error(t, err)
	require.Nil(t, target)
	require.Contains(t, err.Error(), "dedicated/starter/essential")
}

func TestResolveStorageTargetDedicatedOmitsDBaaSProject(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			query := r.URL.Query()
			require.Equal(t, "dev-tidbcloud", query.Get("bizType"))
			require.Equal(t, "aws", query.Get("vendor"))
			require.Equal(t, "us-east-1", query.Get("region"))
			require.Equal(t, "100", query.Get("tenantID"))
			require.Empty(t, query.Get("projectID"))
			require.Equal(t, "300", query.Get("applicationID"))
			require.Equal(t, "true", query.Get("showDeleted"))

			content := `{"code":0,"data":{"clusters":[{"applicationID":"300","bizType":"dev-tidbcloud","id":"dedicated-o11y-id","projectID":"historical-o11y-project","region":"us-east-1","tenantID":"100","vendor":"aws","version":"1","storageInfo":[{"bucketName":"dedicated-statement-bucket","category":"statements","objectPath":"canonical/statement/path"}]}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveStorageTarget(context.Background(), StorageTargetParam{
		Provider:   "aws",
		Region:     "us-east-1",
		TenantID:   "100",
		ClusterID:  "300",
		DeployType: o11y.DedicatedType,
	}, o11y.StorageCategoryStatements)

	require.NoError(t, err)
	require.Equal(t, "dedicated-statement-bucket", target.BucketName)
	require.Equal(t, "canonical/statement/path", target.ObjectPath)
	require.Equal(t, "historical-o11y-project", target.O11yCluster.ProjectID)
}

func TestResolveStorageTargetRequiresUniqueCategory(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	registerAccessTokenResponder()
	httpmock.RegisterResponder(http.MethodGet, "http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/",
		func(r *http.Request) (*http.Response, error) {
			content := `{"code":0,"data":{"clusters":[{"applicationID":"300","bizType":"dev-devtier","id":"serverless-o11y-id-1","projectID":"200","region":"us-west-2","tenantID":"100","vendor":"aws","version":"1","storageInfo":[{"bucketName":"bucket-1","category":"statements","objectPath":"path-1"}]},{"applicationID":"300","bizType":"dev-devtier","id":"serverless-o11y-id-2","projectID":"200","region":"us-west-2","tenantID":"100","vendor":"aws","version":"1","storageInfo":[{"bucketName":"bucket-2","category":"statements","objectPath":"path-2"}]}],"continueToken":"","remainingItemCount":0},"message":""}`
			return httpmock.NewStringResponse(http.StatusOK, content), nil
		})

	target, err := newHTTPMockResolver(t).ResolveStorageTarget(context.Background(), StorageTargetParam{
		Provider:   "aws",
		Region:     "us-west-2",
		TenantID:   "100",
		ClusterID:  "300",
		DeployType: o11y.SharedType,
	}, o11y.StorageCategoryStatements)

	require.Error(t, err)
	require.Nil(t, target)
	require.Contains(t, err.Error(), "more than one storage target")
}
