package clustergateway

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	pdapiv2 "github.com/tikv/pd/server/apiv2/handlers"
)

func TestGetKeySpaceReturnsWrappedNotFoundError(t *testing.T) {
	tests := []struct {
		name       string
		statusCode int
		body       string
	}{
		{
			name:       "http 404",
			statusCode: http.StatusNotFound,
			body:       `{"error":"not found"}`,
		},
		{
			name:       "pd body says keyspace does not exist",
			statusCode: http.StatusInternalServerError,
			body:       `{"error":"keyspace does not exist"}`,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				assert.Equal(t, "/proxy/cluster/sp-1/pd/pd/api/v2/keyspaces/ks-1", r.URL.Path)
				w.WriteHeader(tt.statusCode)
				_, _ = w.Write([]byte(tt.body))
			}))
			defer server.Close()

			got, err := newTestClient().GetKeySpace(context.Background(), newTestSharedPool("sp-1", server.URL), "ks-1")

			require.Nil(t, got)
			require.Error(t, err)
			require.ErrorIs(t, err, ErrKeyspaceNotFound)
			assert.True(t, IsNotFoundError(err))
			assert.Contains(t, err.Error(), "get keyspace ks-1")
		})
	}
}

func TestCreateKeySpace_WithEncryptionContextExternalID(t *testing.T) {
	var reqBody pdapiv2.CreateKeyspaceParams
	var decodeErr error

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		decodeErr = json.NewDecoder(r.Body).Decode(&reqBody)
		w.WriteHeader(http.StatusOK)
	}))
	defer server.Close()

	client := newTestClient()
	sharedPool := &entity.SharedPool{
		SharedPool: apiv1.SharedPool{
			Id:             "sp-1",
			GatewayBaseUrl: server.URL,
			Provider: &apiv1.Provider{
				Type:      apiv1.ProviderType_AWS,
				Region:    "US-WEST-2",
				AuthToken: "test-token",
			},
		},
	}

	err := client.CreateKeySpace(
		context.Background(),
		sharedPool,
		"keyspace-foo",
		"arn:aws:kms:us-west-2:178851224597:key/ee305274-0cbb-47fa-a5af-7b214199926c",
		map[string]string{"ExternalId": "654321"},
	)
	require.NoError(t, err)
	require.NoError(t, decodeErr)

	require.Contains(t, reqBody.Config, "encryption")

	var encryptionConfig keyspaceEncryptionConfig
	require.NoError(t, json.Unmarshal([]byte(reqBody.Config["encryption"]), &encryptionConfig))
	assert.Equal(t, "arn:aws:kms:us-west-2:178851224597:key/ee305274-0cbb-47fa-a5af-7b214199926c", encryptionConfig.CmekId)
	assert.Equal(t, "aws", encryptionConfig.Vendor)
	assert.Equal(t, "us-west-2", encryptionConfig.Region)
	assert.Equal(t, map[string]string{"ExternalId": "654321"}, encryptionConfig.EncryptionContext)
}

func TestCreateKeySpace_WithoutExternalIDOmitsEncryptionContext(t *testing.T) {
	var reqBody pdapiv2.CreateKeyspaceParams
	var decodeErr error

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		decodeErr = json.NewDecoder(r.Body).Decode(&reqBody)
		w.WriteHeader(http.StatusOK)
	}))
	defer server.Close()

	client := newTestClient()
	sharedPool := &entity.SharedPool{
		SharedPool: apiv1.SharedPool{
			Id:             "sp-1",
			GatewayBaseUrl: server.URL,
			Provider: &apiv1.Provider{
				Type:      apiv1.ProviderType_AWS,
				Region:    "US-WEST-2",
				AuthToken: "test-token",
			},
		},
	}

	err := client.CreateKeySpace(
		context.Background(),
		sharedPool,
		"keyspace-foo",
		"arn:aws:kms:us-west-2:178851224597:key/ee305274-0cbb-47fa-a5af-7b214199926c",
		map[string]string{"Ignored": "x"},
	)
	require.NoError(t, err)
	require.NoError(t, decodeErr)

	require.Contains(t, reqBody.Config, "encryption")

	var encryptionPayload map[string]any
	require.NoError(t, json.Unmarshal([]byte(reqBody.Config["encryption"]), &encryptionPayload))
	_, ok := encryptionPayload["encryption_context"]
	assert.False(t, ok)
}
