package service

import (
	"context"
	"encoding/json"
	"errors"
	"io"
	"net"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"sync/atomic"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	clinic_internal_api "github.com/pingcap-inc/clinic/clinic_data_service/pkg/clinic-internal-api"
)

type fakeBYOCStatementsTargetResolver struct {
	target      *clinic_internal_api.BYOCDiagnosisTarget
	err         error
	orgID       uint64
	clusterID   string
	calls       int
	deadline    time.Time
	hasDeadline bool
}

func (f *fakeBYOCStatementsTargetResolver) GetBYOCDiagnosisTarget(
	ctx context.Context,
	orgID uint64,
	clusterID string,
) (*clinic_internal_api.BYOCDiagnosisTarget, error) {
	f.calls++
	f.orgID = orgID
	f.clusterID = clusterID
	f.deadline, f.hasDeadline = ctx.Deadline()
	return f.target, f.err
}

func validBYOCStatementsTarget(baseURL, clusterID string, physical bool) *clinic_internal_api.BYOCDiagnosisTarget {
	deployType := "byoc"
	deployTypeV2 := "byoc"
	if physical {
		deployType = "byoc-host"
		deployTypeV2 = "byoc resource pool"
	}
	return &clinic_internal_api.BYOCDiagnosisTarget{
		BaseURL:       baseURL,
		O11yClusterID: "o11y-cluster_a",
		Provider:      "aws",
		Region:        "us-east-1",
		DeployType:    deployType,
		DeployTypeV2:  deployTypeV2,
		Headers: clinic_internal_api.BYOCDiagnosisTargetHeaders{
			Env:           "prod",
			BizType:       "byoc",
			OrgID:         "123",
			RegionID:      "aws-us-east-1",
			ApplicationID: clusterID,
		},
	}
}

func TestNewBYOCStatementsServiceUsesBoundedQueryTimeout(t *testing.T) {
	svc := NewBYOCStatementsService(&fakeBYOCStatementsTargetResolver{})
	require.Equal(t, byocStatementsTimeout, svc.httpClient.Timeout)
}

func TestBYOCStatementsServiceCallsOnlyFixedEndpointsWithServerHeaders(t *testing.T) {
	tests := []struct {
		name      string
		operation BYOCStatementsOperation
		wantPath  string
		wantBody  string
		query     url.Values
	}{
		{
			name:      "available fields",
			operation: BYOCStatementsOperationAvailableFields,
			wantPath:  "/internal/o11y-cluster_a/diagnosis/api/v1/statements/available_fields",
			wantBody:  `["digest","exec_count"]`,
		},
		{
			name:      "statement types",
			operation: BYOCStatementsOperationTypes,
			wantPath:  "/internal/o11y-cluster_a/diagnosis/api/v1/statements/stmt_types",
			wantBody:  `["Select","Insert"]`,
		},
		{
			name:      "list",
			operation: BYOCStatementsOperationList,
			wantPath:  "/internal/o11y-cluster_a/diagnosis/api/v1/statements/list",
			wantBody:  `[{"digest":"digest-a","future":true}]`,
			query: url.Values{
				"begin_time": {"1"},
				"end_time":   {"2"},
				"schemas":    {"db1", "db2"},
				"stmt_types": {"Select"},
				"text":       {"select"},
				"fields":     {"digest,exec_count"},
			},
		},
		{
			name:      "plans",
			operation: BYOCStatementsOperationPlans,
			wantPath:  "/internal/o11y-cluster_a/diagnosis/api/v1/statements/plans",
			wantBody:  `[{"plan_digest":"plan-a"}]`,
			query: url.Values{
				"begin_time":  {"1"},
				"end_time":    {"2"},
				"digest":      {"digest-a"},
				"schema_name": {"db1"},
			},
		},
		{
			name:      "plan detail",
			operation: BYOCStatementsOperationPlanDetail,
			wantPath:  "/internal/o11y-cluster_a/diagnosis/api/v1/statements/plan/detail",
			wantBody:  `{"digest":"digest-a","future":true}`,
			query: url.Values{
				"begin_time":  {"1"},
				"end_time":    {"2"},
				"digest":      {"digest-a"},
				"plans":       {"plan-a", "N/A"},
				"schema_name": {"db1"},
			},
		},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				require.Equal(t, http.MethodGet, r.Method)
				require.Equal(t, tt.wantPath, r.URL.Path)
				require.Equal(t, tt.query.Encode(), r.URL.Query().Encode())
				require.Equal(t, "application/json", r.Header.Get("Accept"))
				require.Equal(t, "prod", r.Header.Get("X-Env"))
				require.Equal(t, "byoc", r.Header.Get("X-Biz-Type"))
				require.Equal(t, "123", r.Header.Get("X-Org-Id"))
				require.Equal(t, "aws-us-east-1", r.Header.Get("X-Region-Id"))
				require.Equal(t, "logical-cluster", r.Header.Get("X-Application-Id"))
				require.Equal(t, "aws", r.Header.Get("X-Provider"))
				require.Equal(t, "us-east-1", r.Header.Get("X-Region"))
				require.Equal(t, "logical-cluster", r.Header.Get("X-Cluster-Id"))
				require.Equal(t, "byoc", r.Header.Get("X-Deploy-Type"))
				require.Empty(t, r.Header.Get("Authorization"))
				require.Empty(t, r.Header.Get("Cookie"))
				require.Empty(t, r.Header.Get("X-Forwarded-Host"))
				require.Empty(t, r.Header.Get("X-Deploy-Type-V2"))
				require.Empty(t, r.Header.Get("X-O11y-Cluster-Id"))
				w.Header().Set("Content-Type", "application/json")
				_, _ = io.WriteString(w, tt.wantBody)
			}))
			defer upstream.Close()

			baseURL, client := byocStatementsTestUpstream(upstream)
			resolver := &fakeBYOCStatementsTargetResolver{
				target: validBYOCStatementsTarget(baseURL, "logical-cluster", false),
			}
			svc := newBYOCStatementsService(resolver, client)
			result, err := svc.Query(context.Background(), 123, "logical-cluster", tt.operation, tt.query)
			require.NoError(t, err)
			require.JSONEq(t, tt.wantBody, string(result.Body))
			require.Equal(t, "aws", result.Provider)
			require.Equal(t, "us-east-1", result.Region)
			require.Equal(t, "byoc", result.DeployTypeV2)
			require.Equal(t, 1, resolver.calls)
			require.Equal(t, uint64(123), resolver.orgID)
			require.Equal(t, "logical-cluster", resolver.clusterID)
		})
	}
}

func TestBYOCStatementsServiceSupportsLogicalAndPhysicalTargets(t *testing.T) {
	for _, tt := range []struct {
		name             string
		clusterID        string
		physical         bool
		wantDeployType   string
		wantDeployTypeV2 string
	}{
		{name: "logical BYOC", clusterID: "logical-cluster", wantDeployType: "byoc", wantDeployTypeV2: "byoc"},
		{name: "physical byoc-host", clusterID: "physical-cluster", physical: true, wantDeployType: "byoc-host", wantDeployTypeV2: "byoc resource pool"},
	} {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				require.Equal(t, "/internal/o11y-cluster_a/diagnosis/api/v1/statements/list", r.URL.Path)
				require.Equal(t, tt.clusterID, r.Header.Get("X-Application-Id"))
				require.Equal(t, tt.clusterID, r.Header.Get("X-Cluster-Id"))
				require.Equal(t, tt.wantDeployType, r.Header.Get("X-Deploy-Type"))
				_, _ = io.WriteString(w, `[{"digest":"digest-a"}]`)
			}))
			defer upstream.Close()

			baseURL, client := byocStatementsTestUpstream(upstream)
			resolver := &fakeBYOCStatementsTargetResolver{
				target: validBYOCStatementsTarget(baseURL, tt.clusterID, tt.physical),
			}
			svc := newBYOCStatementsService(resolver, client)
			result, err := svc.Query(context.Background(), 123, tt.clusterID, BYOCStatementsOperationList, nil)
			require.NoError(t, err)
			require.Equal(t, tt.wantDeployTypeV2, result.DeployTypeV2)
			require.Equal(t, tt.clusterID, resolver.clusterID)
		})
	}
}

func TestBYOCStatementsServiceUsesOneEndToEndDeadline(t *testing.T) {
	resolver := &fakeBYOCStatementsTargetResolver{
		target: validBYOCStatementsTarget("https://byoc-ds.example.test", "logical-cluster", false),
	}
	var requestDeadline time.Time
	var requestHasDeadline bool
	client := &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
		requestDeadline, requestHasDeadline = req.Context().Deadline()
		return &http.Response{
			StatusCode: http.StatusOK,
			Header:     make(http.Header),
			Body:       io.NopCloser(strings.NewReader(`[]`)),
			Request:    req,
		}, nil
	})}

	started := time.Now()
	svc := newBYOCStatementsService(resolver, client)
	_, err := svc.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperationList, nil)
	require.NoError(t, err)
	require.True(t, resolver.hasDeadline, "target resolution must be inside the query deadline")
	require.True(t, requestHasDeadline, "O11y request must be inside the query deadline")
	require.Equal(t, resolver.deadline, requestDeadline, "target resolution and O11y must share one deadline")
	require.Greater(t, resolver.deadline.Sub(started), byocStatementsTimeout-time.Second)
	require.WithinDuration(t, started.Add(byocStatementsTimeout), resolver.deadline, 100*time.Millisecond)
}

func TestBYOCStatementsServiceRejectsCallerSelectedDataSourceBeforeResolution(t *testing.T) {
	for _, key := range []string{"data_source", "dataSource", "unknown"} {
		t.Run(key, func(t *testing.T) {
			resolver := &fakeBYOCStatementsTargetResolver{
				target: validBYOCStatementsTarget("https://byoc-ds.example.test", "logical-cluster", false),
			}
			svc := newBYOCStatementsService(resolver, &http.Client{})
			_, err := svc.Query(
				context.Background(),
				123,
				"logical-cluster",
				BYOCStatementsOperationList,
				url.Values{key: {"v2"}},
			)
			require.Error(t, err)
			require.Contains(t, err.Error(), key)
			require.Zero(t, resolver.calls, "rejected routing parameters must not reach target resolution")
		})
	}
}

func TestBYOCStatementsServiceDoesNotFollowRedirect(t *testing.T) {
	var destinationCalled atomic.Bool
	destination := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		destinationCalled.Store(true)
		_, _ = io.WriteString(w, `[]`)
	}))
	defer destination.Close()
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, destination.URL, http.StatusFound)
	}))
	defer upstream.Close()

	baseURL, client := byocStatementsTestUpstream(upstream)
	svc := newBYOCStatementsService(
		&fakeBYOCStatementsTargetResolver{target: validBYOCStatementsTarget(baseURL, "logical-cluster", false)},
		client,
	)
	_, err := svc.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperationList, nil)
	var upstreamError *BYOCStatementsUpstreamError
	require.ErrorAs(t, err, &upstreamError)
	require.Equal(t, http.StatusFound, upstreamError.StatusCode)
	require.False(t, destinationCalled.Load())
}

func TestBYOCStatementsServiceReturnsOnlyUpstreamStatus(t *testing.T) {
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		http.Error(w, "sensitive customer SQL and storage detail", http.StatusServiceUnavailable)
	}))
	defer upstream.Close()

	baseURL, client := byocStatementsTestUpstream(upstream)
	svc := newBYOCStatementsService(
		&fakeBYOCStatementsTargetResolver{target: validBYOCStatementsTarget(baseURL, "logical-cluster", false)},
		client,
	)
	_, err := svc.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperationList, nil)
	var upstreamError *BYOCStatementsUpstreamError
	require.ErrorAs(t, err, &upstreamError)
	require.Equal(t, http.StatusServiceUnavailable, upstreamError.StatusCode)
	require.NotContains(t, err.Error(), "sensitive customer SQL")
	require.NotContains(t, err.Error(), "storage detail")
}

func TestBYOCStatementsServiceEnforcesResponseLimit(t *testing.T) {
	tests := []struct {
		name      string
		bodyBytes int
		wantError bool
	}{
		{name: "exactly 16 MiB", bodyBytes: maxBYOCStatementsResponse},
		{name: "over 16 MiB", bodyBytes: maxBYOCStatementsResponse + 1, wantError: true},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			body := `[]` + strings.Repeat(" ", tt.bodyBytes-2)
			upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
				_, _ = io.WriteString(w, body)
			}))
			defer upstream.Close()

			baseURL, client := byocStatementsTestUpstream(upstream)
			svc := newBYOCStatementsService(
				&fakeBYOCStatementsTargetResolver{target: validBYOCStatementsTarget(baseURL, "logical-cluster", false)},
				client,
			)
			result, err := svc.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperationList, nil)
			if tt.wantError {
				require.ErrorIs(t, err, ErrInvalidBYOCStatementsResponse)
				require.Nil(t, result)
				return
			}
			require.NoError(t, err)
			require.Len(t, result.Body, maxBYOCStatementsResponse)
		})
	}
}

func TestBYOCStatementsServiceValidatesResponseShapes(t *testing.T) {
	valid := []struct {
		name      string
		operation BYOCStatementsOperation
		body      string
		wantBody  string
	}{
		{name: "empty available fields", operation: BYOCStatementsOperationAvailableFields, body: `[]`},
		{name: "statement type strings", operation: BYOCStatementsOperationTypes, body: `["Select"]`},
		{name: "empty list", operation: BYOCStatementsOperationList, body: `[]`},
		{name: "null list normalizes to empty array", operation: BYOCStatementsOperationList, body: `null`, wantBody: `[]`},
		{name: "list objects", operation: BYOCStatementsOperationList, body: `[{"future":true}]`},
		{name: "empty plans", operation: BYOCStatementsOperationPlans, body: `[]`},
		{name: "null plans normalize to empty array", operation: BYOCStatementsOperationPlans, body: " null\n", wantBody: `[]`},
		{name: "empty detail object", operation: BYOCStatementsOperationPlanDetail, body: `{}`},
	}
	for _, tt := range valid {
		tt := tt
		t.Run("valid "+tt.name, func(t *testing.T) {
			result, err := queryBYOCStatementsBody(t, tt.operation, tt.body)
			require.NoError(t, err)
			require.NotNil(t, result)
			wantBody := tt.wantBody
			if wantBody == "" {
				wantBody = tt.body
			}
			require.JSONEq(t, wantBody, string(result.Body))
		})
	}

	invalid := []struct {
		name      string
		operation BYOCStatementsOperation
		body      string
	}{
		{name: "empty body", operation: BYOCStatementsOperationList, body: ``},
		{name: "invalid JSON", operation: BYOCStatementsOperationList, body: `not-json`},
		{name: "trailing JSON", operation: BYOCStatementsOperationList, body: `[] {}`},
		{name: "top-level null available fields", operation: BYOCStatementsOperationAvailableFields, body: `null`},
		{name: "null available field", operation: BYOCStatementsOperationAvailableFields, body: `[null]`},
		{name: "object available fields", operation: BYOCStatementsOperationAvailableFields, body: `{}`},
		{name: "top-level null statement types", operation: BYOCStatementsOperationTypes, body: `null`},
		{name: "numeric statement type", operation: BYOCStatementsOperationTypes, body: `[1]`},
		{name: "object list", operation: BYOCStatementsOperationList, body: `{}`},
		{name: "scalar list item", operation: BYOCStatementsOperationList, body: `[1]`},
		{name: "null list item", operation: BYOCStatementsOperationList, body: `[null]`},
		{name: "string plan item", operation: BYOCStatementsOperationPlans, body: `["plan"]`},
		{name: "null detail", operation: BYOCStatementsOperationPlanDetail, body: `null`},
		{name: "array detail", operation: BYOCStatementsOperationPlanDetail, body: `[]`},
	}
	for _, tt := range invalid {
		tt := tt
		t.Run("invalid "+tt.name, func(t *testing.T) {
			result, err := queryBYOCStatementsBody(t, tt.operation, tt.body)
			require.ErrorIs(t, err, ErrInvalidBYOCStatementsResponse)
			require.Nil(t, result)
		})
	}
}

func TestBYOCStatementsServiceRejectsUnsafeTargets(t *testing.T) {
	for _, tt := range []struct {
		name   string
		mutate func(*clinic_internal_api.BYOCDiagnosisTarget)
	}{
		{name: "userinfo", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.BaseURL = "https://user@example.com" }},
		{name: "path", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.BaseURL = "https://example.com/caller" }},
		{name: "query", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) {
			target.BaseURL = "https://example.com?route=caller"
		}},
		{name: "fragment", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.BaseURL = "https://example.com#caller" }},
		{name: "localhost", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.BaseURL = "http://localhost:8080" }},
		{name: "localhost trailing dot", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.BaseURL = "http://localhost.:8080" }},
		{name: "IPv4 loopback", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.BaseURL = "http://127.0.0.1:8080" }},
		{name: "cluster traversal", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.O11yClusterID = "../other" }},
	} {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			target := validBYOCStatementsTarget("https://example.com", "logical-cluster", false)
			tt.mutate(target)
			svc := newBYOCStatementsService(&fakeBYOCStatementsTargetResolver{target: target}, nil)
			_, err := svc.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperationList, nil)
			require.ErrorIs(t, err, ErrInvalidBYOCStatementsTarget)
		})
	}
}

func TestBYOCStatementsServiceRejectsMismatchedTargetScope(t *testing.T) {
	for _, tt := range []struct {
		name   string
		mutate func(*clinic_internal_api.BYOCDiagnosisTarget)
	}{
		{name: "org", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.Headers.OrgID = "456" }},
		{name: "application", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.Headers.ApplicationID = "other" }},
		{name: "region", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.Headers.RegionID = "gcp-us-east1" }},
		{name: "biz type", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.Headers.BizType = "premium" }},
		{name: "other tier", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) {
			target.DeployType = "premium"
			target.DeployTypeV2 = "premium"
		}},
		{name: "conflicting v2 tier", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.DeployTypeV2 = "byoc resource pool" }},
		{name: "surrounding whitespace", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.Provider = " aws" }},
		{name: "missing env", mutate: func(target *clinic_internal_api.BYOCDiagnosisTarget) { target.Headers.Env = "" }},
	} {
		t.Run(tt.name, func(t *testing.T) {
			target := validBYOCStatementsTarget("https://byoc-statements.example.test", "logical-cluster", false)
			tt.mutate(target)
			resolver := &fakeBYOCStatementsTargetResolver{target: target}
			svc := newBYOCStatementsService(resolver, nil)
			_, err := svc.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperationList, nil)
			require.ErrorIs(t, err, ErrInvalidBYOCStatementsTarget)
			require.Equal(t, 1, resolver.calls)
		})
	}
}

func TestBYOCStatementsServiceEnforcesLimitAfterBinaryPlanEnrichment(t *testing.T) {
	binaryPlan := `{"main":{"name":"TableScan_1","estRows":1,"cost":1,"taskType":"root","storeType":"tidb","accessObjects":[],"operatorInfo":"","children":[]},"ctes":[],"withRuntimeStats":false}`
	body, err := json.Marshal(map[string]string{"binary_plan": binaryPlan, "padding": ""})
	require.NoError(t, err)
	paddingBytes := maxBYOCStatementsResponse - len(body)
	require.Positive(t, paddingBytes)
	body, err = json.Marshal(map[string]string{"binary_plan": binaryPlan, "padding": strings.Repeat("x", paddingBytes)})
	require.NoError(t, err)
	require.Len(t, body, maxBYOCStatementsResponse)

	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		_, _ = w.Write(body)
	}))
	defer upstream.Close()
	baseURL, client := byocStatementsTestUpstream(upstream)
	svc := newBYOCStatementsService(
		&fakeBYOCStatementsTargetResolver{target: validBYOCStatementsTarget(baseURL, "logical-cluster", false)},
		client,
	)
	result, err := svc.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperationPlanDetail, nil)
	require.ErrorIs(t, err, ErrInvalidBYOCStatementsResponse)
	require.Nil(t, result)
}

func TestBYOCStatementsServicePreservesTargetResolutionErrors(t *testing.T) {
	want := &clinic_internal_api.UpstreamError{StatusCode: http.StatusNotFound}
	svc := newBYOCStatementsService(&fakeBYOCStatementsTargetResolver{err: want}, nil)
	_, err := svc.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperationList, nil)
	var got *clinic_internal_api.UpstreamError
	require.True(t, errors.As(err, &got))
	require.Equal(t, http.StatusNotFound, got.StatusCode)
}

func TestBYOCStatementsServiceRejectsUnavailableAndUnknownOperation(t *testing.T) {
	var nilService *BYOCStatementsService
	_, err := nilService.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperationList, nil)
	require.ErrorIs(t, err, ErrBYOCStatementsUnavailable)

	svc := newBYOCStatementsService(nil, nil)
	_, err = svc.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperationList, nil)
	require.ErrorIs(t, err, ErrBYOCStatementsUnavailable)

	resolver := &fakeBYOCStatementsTargetResolver{}
	svc = newBYOCStatementsService(resolver, nil)
	_, err = svc.Query(context.Background(), 123, "logical-cluster", BYOCStatementsOperation("unknown"), nil)
	require.Error(t, err)
	require.Contains(t, err.Error(), "unsupported BYOC Statements operation")
	require.Zero(t, resolver.calls)
}

func queryBYOCStatementsBody(
	t *testing.T,
	operation BYOCStatementsOperation,
	body string,
) (*BYOCStatementsResult, error) {
	t.Helper()
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		_, _ = io.WriteString(w, body)
	}))
	t.Cleanup(upstream.Close)

	baseURL, client := byocStatementsTestUpstream(upstream)
	svc := newBYOCStatementsService(
		&fakeBYOCStatementsTargetResolver{target: validBYOCStatementsTarget(baseURL, "logical-cluster", false)},
		client,
	)
	return svc.Query(context.Background(), 123, "logical-cluster", operation, nil)
}

func byocStatementsTestUpstream(upstream *httptest.Server) (string, *http.Client) {
	client := *upstream.Client()
	transport := client.Transport.(*http.Transport).Clone()
	upstreamAddress := upstream.Listener.Addr().String()
	transport.DialContext = func(ctx context.Context, network, _ string) (net.Conn, error) {
		return (&net.Dialer{}).DialContext(ctx, network, upstreamAddress)
	}
	client.Transport = transport
	return "http://byoc-statements.example.test", &client
}

type roundTripFunc func(*http.Request) (*http.Response, error)

func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
	return f(req)
}
