package domain

import (
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/dataflow-service/app/repo/entity"
	replay_v1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-server/v1"
	"github.com/tidbcloud/pkgv2/bizerr"
	mgmt_commonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	mgmt_nextgenv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	mgmt "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

type passthroughEncryptor struct{}

func (passthroughEncryptor) EncryptBase64(text string) (string, bizerr.BizError) { return text, nil }
func (passthroughEncryptor) DecryptBase64(text string) (string, bizerr.BizError) { return text, nil }
func (passthroughEncryptor) EncryptBase64Inplace(text *string) bizerr.BizError   { return nil }
func (passthroughEncryptor) DecryptBase64Inplace(text *string) bizerr.BizError   { return nil }

func TestToReplayProtoBuildsPremiumClusterRefs(t *testing.T) {
	replayEnt := &entity.Replay{
		TenantID:  entity.ID(1),
		ProjectID: entity.ID(2),
		Region:    "aws-us-west-2",
		SourceAuditCredential: &entity.AuditLogAddr{
			Aliyun: &entity.AliyunAuditLog{
				BucketURL:       "oss://audit/path/tidb-pod",
				AccessKeyID:     "ak",
				SecretAccessKey: "sk",
			},
		},
	}

	targetCluster := &mgmt.Cluster{
		ClusterId:  1001,
		DeployType: mgmt.DeployType_DEPLOY_TYPE_NEXTGEN,
		ClusterInfo: &mgmt.Cluster_NextGenCluster{
			NextGenCluster: &mgmt_nextgenv1.Cluster{
				ServicePlan: mgmt_commonv1.ServicePlan_Premium,
			},
		},
		CloudProvider:  "aws",
		Region:         "aws-us-west-2",
		OrganizationId: 1,
		ProjectId:      2,
	}

	replayProto, err := ToReplayProto(
		replayEnt,
		nil,
		targetCluster,
		entity.ReplayDesiredStateDeploy,
		passthroughEncryptor{},
		"ack",
		"source-route-region",
		"",
	)
	require.NoError(t, err)
	require.Empty(t, replayProto.Tasks)
	require.Nil(t, replayProto.PremiumTargetEndpoint)
	require.Equal(t, "ack", replayProto.GetProvider())
	require.Equal(t, "source-route-region", replayProto.GetRegion())
	require.Equal(t, replay_v1.Replay_CLUSTER_TYPE_PREMIUM, replayProto.GetTargetCluster().GetClusterType())
	require.Equal(t, "eks", replayProto.GetTargetCluster().GetProvider())
	require.Equal(t, "aws-us-west-2", replayProto.GetTargetCluster().GetRegion())
}

func TestReplayAuditLogAliyunProtoConversions(t *testing.T) {
	t.Run("to replay data source proto with arbitrary url only", func(t *testing.T) {
		dataSource := &entity.ReplayDataSource{
			SourceAuditLogAddrs: []entity.AuditLogAddr{
				{
					Aliyun: &entity.AliyunAuditLog{
						BucketURL: "https://audit.example.com/bucket/path/tidb-*",
					},
				},
			},
		}

		protoDataSource := toReplayDataSourceProto(dataSource)
		require.Len(t, protoDataSource.AuditLogs, 1)
		require.Nil(t, protoDataSource.AuditLogs[0].GetAws())
		require.Equal(
			t,
			"https://audit.example.com/bucket/path/tidb-*",
			protoDataSource.AuditLogs[0].GetAliyun().GetBucketUrl(),
		)
	})

	t.Run("to replay defaults proto with arbitrary aliyun url only", func(t *testing.T) {
		replayEnt := &entity.Replay{
			SourceAuditCredential: &entity.AuditLogAddr{
				Aliyun: &entity.AliyunAuditLog{
					BucketURL: "s3://bucket/path/tidb-*?endpoint=http://oss-ap-southeast-1.aliyuncs.com",
				},
			},
		}
		replayProto := &replay_v1.Replay{}

		toReplayDefaults(replayProto, replayEnt, passthroughEncryptor{})
		require.NotNil(t, replayProto.Defaults)
		require.Equal(
			t,
			"s3://bucket/path/tidb-*?endpoint=http://oss-ap-southeast-1.aliyuncs.com",
			replayProto.GetDefaults().GetSourceAuditLog().GetAliyun().GetBucketUrl(),
		)
	})

	t.Run("from replay audit log addr proto", func(t *testing.T) {
		protoAddr := &replay_v1.Replay_AuditLogAddr{
			Source: &replay_v1.Replay_AuditLogAddr_Aliyun{
				Aliyun: &replay_v1.Replay_AliyunAuditLog{
					BucketUrl: "oss://audit-bucket",
				},
			},
		}

		addr := fromReplayAuditLogAddrProto(protoAddr)
		require.NotNil(t, addr)
		require.Nil(t, addr.AWS)
		require.NotNil(t, addr.Aliyun)
		require.Equal(t, "oss://audit-bucket", addr.Aliyun.BucketURL)
	})
}

func TestRedactAliyunBucketURLForTransport(t *testing.T) {
	tests := map[string]struct {
		input string
		want  string
	}{
		"safe url keeps endpoint query": {
			input: "s3://bucket/path/tidb-*?endpoint=http://oss-ap-southeast-1.aliyuncs.com",
			want:  "s3://bucket/path/tidb-*?endpoint=http://oss-ap-southeast-1.aliyuncs.com",
		},
		"secret query is stripped": {
			input: "s3://bucket/path/tidb-?endpoint=http://oss-ap-southeast-1.aliyuncs.com&access-key=ak&secret-access-key=sk#frag",
			want:  "s3://bucket/path/tidb-",
		},
		"aliyun replay access key query is stripped": {
			input: "s3://bucket/path/tidb-?endpoint=http://oss-cn-hangzhou.aliyuncs.com&access-key=ak&secret-access-key=sk",
			want:  "s3://bucket/path/tidb-",
		},
	}

	for name, tc := range tests {
		t.Run(name, func(t *testing.T) {
			require.Equal(t, tc.want, RedactAliyunBucketURLForTransport(tc.input))
		})
	}
}

func TestFromReplayStatusProtoRedactsAliyunBucketURLCredentials(t *testing.T) {
	status := FromReplayStatusProto(&replay_v1.ReplayStatus{
		Phase: replay_v1.ReplayStatus_PHASE_RUNNING,
		Tasks: []*replay_v1.ReplayStatus_ReplayTaskStatus{
			{
				Name: "task-1",
				ReplicaStatuses: []*replay_v1.ReplayStatus_ReplayReplicaStatus{
					{
						PodName: "pod-1",
						TaskInfo: &replay_v1.ReplayStatus_ReplayTaskInfo{
							SourceAuditLogAddrs: []*replay_v1.Replay_AuditLogAddr{
								{
									Source: &replay_v1.Replay_AuditLogAddr_Aliyun{
										Aliyun: &replay_v1.Replay_AliyunAuditLog{
											BucketUrl: "s3://bucket/path/tidb-*?endpoint=http://oss-ap-southeast-1.aliyuncs.com&access-key=ak&secret-access-key=sk#frag",
										},
									},
								},
								{
									Source: &replay_v1.Replay_AuditLogAddr_Aliyun{
										Aliyun: &replay_v1.Replay_AliyunAuditLog{
											BucketUrl: "s3://bucket/other/tidb-*?endpoint=http://oss-ap-southeast-1.aliyuncs.com",
										},
									},
								},
							},
						},
					},
				},
			},
		},
	})

	require.NotNil(t, status)
	require.Len(t, status.Tasks, 1)
	require.Len(t, status.Tasks[0].ReplicaStatuses, 1)
	addrs := status.Tasks[0].ReplicaStatuses[0].TaskInfo.SourceAuditLogAddrs
	require.Len(t, addrs, 2)
	require.Equal(t, "s3://bucket/path/tidb-*", addrs[0].Aliyun.BucketURL)
	require.Equal(t, "s3://bucket/other/tidb-*?endpoint=http://oss-ap-southeast-1.aliyuncs.com", addrs[1].Aliyun.BucketURL)
}
