package interceptor

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

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/observability/pkg/common/constants"
	globalstructs "github.com/tidbcloud/observability/pkg/globalservice/structs"
	tidbmgmt "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"google.golang.org/grpc"
)

type testMetaCacheGetter struct {
	meta *globalstructs.GlobalServiceMeta
}

func (t testMetaCacheGetter) GetGlobalMetaCache() *globalstructs.GlobalServiceMeta {
	return t.meta
}

type mockOpenAPIClusterServiceClient struct {
	tidbmgmt.ClusterServiceClient
	listClustersFn func(ctx context.Context, in *tidbmgmt.ListClustersRequest, opts ...grpc.CallOption) (*tidbmgmt.ListClustersResponse, error)
}

func (m *mockOpenAPIClusterServiceClient) ListClusters(ctx context.Context, in *tidbmgmt.ListClustersRequest, opts ...grpc.CallOption) (*tidbmgmt.ListClustersResponse, error) {
	return m.listClustersFn(ctx, in, opts...)
}

func TestIsOpenAPIWhitelistedTenant(t *testing.T) {
	originalMetaCacheGetter := metaCacheGetter
	defer func() { metaCacheGetter = originalMetaCacheGetter }()

	metaCacheGetter = testMetaCacheGetter{
		meta: &globalstructs.GlobalServiceMeta{
			ServerConfig: globalstructs.ServerConfig{
				OpenAPIWhitelistTenants: []string{"123", "456"},
			},
		},
	}

	assert.True(t, isOpenAPIWhitelistedTenant("123"))
	assert.False(t, isOpenAPIWhitelistedTenant("789"))

	metaCacheGetter = testMetaCacheGetter{}
	assert.False(t, isOpenAPIWhitelistedTenant("123"))
}

func TestOAPIBasicInfoEnforcesOpenAPIWhitelistTenants(t *testing.T) {
	gin.SetMode(gin.TestMode)
	originalMetaCacheGetter := metaCacheGetter
	defer func() { metaCacheGetter = originalMetaCacheGetter }()

	tests := []struct {
		name        string
		whitelist   []string
		wantAllowed bool
	}{
		{
			name:        "allowed tenant continues",
			whitelist:   []string{"123"},
			wantAllowed: true,
		},
		{
			name:        "non-whitelisted tenant aborts",
			whitelist:   []string{"456"},
			wantAllowed: false,
		},
	}

	for idx, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			metaCacheGetter = testMetaCacheGetter{
				meta: &globalstructs.GlobalServiceMeta{
					ServerConfig: globalstructs.ServerConfig{
						OpenAPIWhitelistTenants: tt.whitelist,
					},
				},
			}

			bizType := fmt.Sprintf("openapi-test-%d", idx)
			oapiClients.Store(bizType, &mockOpenAPIClusterServiceClient{
				listClustersFn: func(ctx context.Context, in *tidbmgmt.ListClustersRequest, opts ...grpc.CallOption) (*tidbmgmt.ListClustersResponse, error) {
					assert.Equal(t, []uint64{1001}, in.GetFilter().GetClusterIds())
					return &tidbmgmt.ListClustersResponse{
						Clusters: []*tidbmgmt.Cluster{
							{
								ClusterId:      1001,
								OrganizationId: 123,
								ProjectId:      987,
								Region:         "us-west-2",
								CloudProvider:  string(constants.AWS),
								DeployType:     tidbmgmt.DeployType_DEPLOY_TYPE_DEDICATED,
							},
						},
					}, nil
				},
			})
			defer oapiClients.Delete(bizType)

			w := httptest.NewRecorder()
			c, _ := gin.CreateTestContext(w)
			req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%s/v1beta1/clusters/1001/monitoringPipelines", bizType), nil)
			assert.NoError(t, err)
			c.Request = req
			c.Params = gin.Params{
				{Key: "bizType", Value: bizType},
				{Key: "applicationID", Value: "1001"},
			}

			OAPIBasicInfo(c)

			if tt.wantAllowed {
				assert.False(t, c.IsAborted())
				assert.Equal(t, "123", c.Request.Header.Get(string(constants.OpenAPIHeaderOrgId)))
				assert.Equal(t, "987", c.GetString(authMetaProjectId))
				assert.Equal(t, "tidbcloud", c.Request.Header.Get(string(constants.OpenAPIHeaderBizType)))
				return
			}
			assert.True(t, c.IsAborted())
			assert.Equal(t, http.StatusBadRequest, w.Code)
		})
	}
}
