package regionalserver

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"

	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/encryptionchange"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver/mock_regionalserver"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func TestGenMeta(t *testing.T) {
	// Real stdout captured from production request log.
	const genMetaStdout = `{
  "keyspace_id": 11,
  "mode": "enable_cmek",
  "generated": {
    "file_id": 465040308846985318,
    "new_data_key_generated": null
  },
  "pd_updated": true,
  "pd_after": {
    "current_file_id": 0,
    "next_file_id": 465040308846985318
  },
  "owner_store_id": 12,
  "encryption_config": {
    "enabled": true,
    "cmek_id": "arn:aws:kms:us-east-1:886436925895:key/399dafbf-8f16-4dfb-9ae5-3b68f489e6de",
    "vendor": "aws",
    "region": "us-east-1",
    "endpoint": null
  }
}`

	t.Run("Success", func(t *testing.T) {
		ctrl := gomock.NewController(t)
		mockCli := mock_regionalserver.NewMockClient(ctrl)
		executor := NewExecutor(mockCli)
		ctx := context.Background()
		region := &entity.Region{}

		mockCli.EXPECT().ExecuteClusterAgentCommand(
			ctx, region,
			"10497663804318628033", "pool-1",
			"cse-ctl encryption gen-meta --keyspace-name 10497663804318628033 --pd https://db-pd:2379 --mode enable-cmek --vendor aws --region us-east-1 --cmek-id arn:aws:kms:us-east-1:886436925895:key/399dafbf-8f16-4dfb-9ae5-3b68f489e6de",
		).
			Return(&regionalserver.ExecuteCommandResponse{
				StdoutMsg: genMetaStdout,
				StderrMsg: "",
			}, nil)

		resp, err := executor.GenMeta(ctx, region, &encryptionchange.GenMetaRequest{
			ClusterId:    "10497663804318628033",
			SharedPoolId: "pool-1",
			KeyspaceName: "10497663804318628033",
			Mode:         "enable-cmek",
			Vendor:       "aws",
			KmsRegion:    "us-east-1",
			CmekId:       "arn:aws:kms:us-east-1:886436925895:key/399dafbf-8f16-4dfb-9ae5-3b68f489e6de",
		})

		require.NoError(t, err)
		assert.Equal(t, uint64(465040308846985318), resp.FileId)
		assert.True(t, resp.PdUpdated)
		assert.Equal(t, uint64(0), resp.PdAfter.CurrentFileId)
		assert.Equal(t, uint64(465040308846985318), resp.PdAfter.NextFileId)
		assert.Equal(t, uint64(12), resp.OwnerStoreId)
		assert.True(t, resp.EncryptionConfig.Enabled)
		assert.Equal(t, "arn:aws:kms:us-east-1:886436925895:key/399dafbf-8f16-4dfb-9ae5-3b68f489e6de", resp.EncryptionConfig.CmekId)
		assert.Equal(t, "aws", resp.EncryptionConfig.Vendor)
		assert.Equal(t, "us-east-1", resp.EncryptionConfig.Region)
	})

	t.Run("WithExternalId", func(t *testing.T) {
		ctrl := gomock.NewController(t)
		mockCli := mock_regionalserver.NewMockClient(ctrl)
		executor := NewExecutor(mockCli)
		ctx := context.Background()
		region := &entity.Region{}

		mockCli.EXPECT().ExecuteClusterAgentCommand(
			ctx, region,
			"10497663804318628033", "pool-1",
			`cse-ctl encryption gen-meta --keyspace-name 10497663804318628033 --pd https://db-pd:2379 --mode enable-cmek --vendor aws --region us-east-1 --cmek-id arn:aws:kms:us-east-1:886436925895:key/399dafbf-8f16-4dfb-9ae5-3b68f489e6de --encryption-context '{"ExternalId":"42"}'`,
		).
			Return(&regionalserver.ExecuteCommandResponse{
				StdoutMsg: genMetaStdout,
				StderrMsg: "",
			}, nil)

		_, err := executor.GenMeta(ctx, region, &encryptionchange.GenMetaRequest{
			ClusterId:    "10497663804318628033",
			SharedPoolId: "pool-1",
			KeyspaceName: "10497663804318628033",
			Mode:         "enable-cmek",
			Vendor:       "aws",
			KmsRegion:    "us-east-1",
			CmekId:       "arn:aws:kms:us-east-1:886436925895:key/399dafbf-8f16-4dfb-9ae5-3b68f489e6de",
			ExternalId:   "42",
		})
		require.NoError(t, err)
	})

	t.Run("EmptyStdout", func(t *testing.T) {
		ctrl := gomock.NewController(t)
		mockCli := mock_regionalserver.NewMockClient(ctrl)
		executor := NewExecutor(mockCli)
		ctx := context.Background()
		region := &entity.Region{}

		mockCli.EXPECT().ExecuteClusterAgentCommand(
			gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
		).Return(&regionalserver.ExecuteCommandResponse{
			StdoutMsg: "",
			StderrMsg: "",
		}, nil)

		_, err := executor.GenMeta(ctx, region, &encryptionchange.GenMetaRequest{
			ClusterId:    "123",
			SharedPoolId: "pool-1",
			KeyspaceName: "123",
			Mode:         "enable-cmek",
			Vendor:       "aws",
			KmsRegion:    "us-east-1",
			CmekId:       "arn:aws:kms:us-east-1:123:key/abc",
		})

		require.Error(t, err)
		assert.Contains(t, err.Error(), "gen-meta parse stdout failed")
	})

	t.Run("CommandError", func(t *testing.T) {
		ctrl := gomock.NewController(t)
		mockCli := mock_regionalserver.NewMockClient(ctrl)
		executor := NewExecutor(mockCli)
		ctx := context.Background()
		region := &entity.Region{}

		mockCli.EXPECT().ExecuteClusterAgentCommand(
			gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
		).Return(nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "connection refused"))

		_, err := executor.GenMeta(ctx, region, &encryptionchange.GenMetaRequest{
			ClusterId:    "123",
			SharedPoolId: "pool-1",
			KeyspaceName: "123",
			Mode:         "enable-cmek",
			Vendor:       "aws",
			KmsRegion:    "us-east-1",
			CmekId:       "arn:aws:kms:us-east-1:123:key/abc",
		})

		require.Error(t, err)
		assert.Contains(t, err.Error(), "gen-meta command failed")
	})
}
