package inner

import (
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/dataflow-service/app/repo/entity"
	pbReplay "github.com/tidbcloud/dataflow-service/idl/pbgen/proto/replay"
)

func createReplayRequestWithSourceAliyun(aliyun *pbReplay.AliyunAuditLog) *pbReplay.CreateReplayRequest {
	return &pbReplay.CreateReplayRequest{
		OrgId:     "1",
		ClusterId: "2",
		Replay: &pbReplay.ReplaySpec{
			SourceClusterId: "3",
			TargetClusterId: "4",
			SourceAuditCredential: &pbReplay.AuditLogAddr{
				Storage: &pbReplay.AuditLogAddr_Aliyun{Aliyun: aliyun},
			},
		},
	}
}

func updateReplayRequestWithTaskAliyun(aliyun *pbReplay.AliyunAuditLog) *pbReplay.UpdateReplayRequest {
	return &pbReplay.UpdateReplayRequest{
		OrgId:     "1",
		ClusterId: "2",
		ReplayId:  "5",
		Replay: &pbReplay.ReplaySpec{
			ReplayTasks: []*pbReplay.ReplayTask{
				{
					Name: "task-0",
					DataTarget: &pbReplay.ReplayDataTarget{
						Endpoint: "tidb.example.com:4000",
					},
					DataSource: &pbReplay.ReplayDataSource{
						SourceAuditLogAddrs: []*pbReplay.AuditLogAddr{
							{Storage: &pbReplay.AuditLogAddr_Aliyun{Aliyun: aliyun}},
						},
					},
				},
			},
		},
	}
}

func TestValidateCreateReplayRequestAllowsTopLevelArbitraryAliyunBucketURLWithoutKeys(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	err := controller.validateCreateReplayRequest(createReplayRequestWithSourceAliyun(&pbReplay.AliyunAuditLog{
		BucketUrl: "https://audit.example.com/bucket/path/tidb-*",
	}))
	require.NoError(t, err)
}

func TestValidateUpdateReplayRequestAllowsTaskLevelArbitraryAliyunBucketURLWithoutKeys(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	err := controller.validateUpdateReplayRequest(updateReplayRequestWithTaskAliyun(&pbReplay.AliyunAuditLog{
		BucketUrl: "s3://bucket/path?endpoint=http://oss-ap-southeast-1.aliyuncs.com",
	}))
	require.NoError(t, err)
}

func TestValidateUpdateReplayRequestAllowsTaskLevelEmptyAliyunPatch(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	err := controller.validateUpdateReplayRequest(updateReplayRequestWithTaskAliyun(&pbReplay.AliyunAuditLog{}))
	require.NoError(t, err)
}

func TestValidateUpdateReplayRequestAllowsNonOSSAliyunBucketURLWithKeysAtControllerLayer(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	err := controller.validateUpdateReplayRequest(updateReplayRequestWithTaskAliyun(&pbReplay.AliyunAuditLog{
		BucketUrl:       "s3://bucket/path",
		AccessKeyId:     "ak",
		SecretAccessKey: "sk",
	}))
	require.NoError(t, err)
}

func TestValidateCreateReplayRequestAllowsTopLevelOSSAliyunBucketURLWithKeys(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	err := controller.validateCreateReplayRequest(createReplayRequestWithSourceAliyun(&pbReplay.AliyunAuditLog{
		BucketUrl:       "oss://bucket/path/tidb-*",
		AccessKeyId:     "ak",
		SecretAccessKey: "sk",
	}))
	require.NoError(t, err)
}

func TestValidateCreateReplayRequestAllowsTopLevelAliyunPartialKeyPairAtControllerLayer(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	err := controller.validateCreateReplayRequest(createReplayRequestWithSourceAliyun(&pbReplay.AliyunAuditLog{
		BucketUrl:   "oss://bucket/path/tidb-*",
		AccessKeyId: "ak",
	}))
	require.NoError(t, err)
}

func TestValidateUpdateReplayRequestAllowsTaskLevelAliyunPartialKeyPairAtControllerLayer(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	err := controller.validateUpdateReplayRequest(updateReplayRequestWithTaskAliyun(&pbReplay.AliyunAuditLog{
		BucketUrl:       "oss://bucket/path",
		SecretAccessKey: "sk",
	}))
	require.NoError(t, err)
}

func TestValidateUpdateReplayRequestAllowsExplicitEmptyReplayTasks(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	err := controller.validateUpdateReplayRequest(&pbReplay.UpdateReplayRequest{
		OrgId:     "1",
		ClusterId: "2",
		ReplayId:  "5",
		Replay: &pbReplay.ReplaySpec{
			ReplayTasks: []*pbReplay.ReplayTask{},
		},
	})
	require.NoError(t, err)
}

func TestPBReplaySpec2DomainUpdateReqSkipsExplicitEmptyReplayTasks(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	updateReq, err := controller.pbReplaySpec2DomainUpdateReq(&pbReplay.UpdateReplayRequest{
		ReplayId: "5",
		Replay: &pbReplay.ReplaySpec{
			ReplayTasks: []*pbReplay.ReplayTask{},
		},
	})
	require.NoError(t, err)
	require.Nil(t, updateReq.ReplayTasks)
}

func TestPBReplaySpec2DomainUpdateReqLeavesOmittedReplayTasksNil(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	updateReq, err := controller.pbReplaySpec2DomainUpdateReq(&pbReplay.UpdateReplayRequest{
		ReplayId: "5",
		Replay:   &pbReplay.ReplaySpec{},
	})
	require.NoError(t, err)
	require.Nil(t, updateReq.ReplayTasks)
}

func TestEntityAuditLogAddr2PBMasksAliyunCredentials(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	pbAddr := controller.entityAuditLogAddr2PB(entity.AuditLogAddr{
		Aliyun: &entity.AliyunAuditLog{
			BucketURL:       "oss://bucket/path/tidb-*",
			AccessKeyID:     "abcdefghijklmnop",
			SecretAccessKey: "secret",
		},
	})
	aliyun := pbAddr.GetAliyun()
	require.NotNil(t, aliyun)
	require.Equal(t, "oss://bucket/path/tidb-*", aliyun.BucketUrl)
	require.Equal(t, accessKeyIDPlaceholder, aliyun.AccessKeyId)
	require.Equal(t, secretAccessKeyPlaceholder, aliyun.SecretAccessKey)
}

func TestEntityAuditLogAddr2PBReturnsArbitraryAliyunBucketURL(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	pbAddr := controller.entityAuditLogAddr2PB(entity.AuditLogAddr{
		Aliyun: &entity.AliyunAuditLog{
			BucketURL: "s3://bucket/path/tidb-?endpoint=http://oss-ap-southeast-1.aliyuncs.com",
		},
	})
	aliyun := pbAddr.GetAliyun()
	require.NotNil(t, aliyun)
	require.Equal(t, "s3://bucket/path/tidb-?endpoint=http://oss-ap-southeast-1.aliyuncs.com", aliyun.BucketUrl)
	require.Empty(t, aliyun.AccessKeyId)
	require.Empty(t, aliyun.SecretAccessKey)
}

func TestEntityAuditLogAddr2PBShowsOnlyBucketURLWhenAliyunBucketURLContainsQueryCredentials(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	pbAddr := controller.entityAuditLogAddr2PB(entity.AuditLogAddr{
		Aliyun: &entity.AliyunAuditLog{
			BucketURL: "s3://bucket/path/tidb-?endpoint=http://oss-ap-southeast-1.aliyuncs.com&access-key=ak&secret-access-key=sk&OSSAccessKeyId=oss-ak&AWSAccessKeyId=aws-ak&AccessKeySecret=oss-sk&access_key_secret=oss-sk-2&Signature=sig&accessKeyId=camel-ak&secretAccessKey=camel-sk&x-oss-security-token=token#frag",
		},
	})
	aliyun := pbAddr.GetAliyun()
	require.NotNil(t, aliyun)
	require.Equal(t, "s3://bucket/path/tidb-", aliyun.BucketUrl)
	require.Empty(t, aliyun.AccessKeyId)
	require.Empty(t, aliyun.SecretAccessKey)
}

func TestEntityAuditLogAddr2PBShowsOnlyBucketURLForAliyunSignedS3URL(t *testing.T) {
	controller := &replayInnerControllerImpl{}
	pbAddr := controller.entityAuditLogAddr2PB(entity.AuditLogAddr{
		Aliyun: &entity.AliyunAuditLog{
			BucketURL: "s3://bucket/path/tidb-*?endpoint=http://oss-ap-southeast-1.aliyuncs.com&sig=signed-value",
		},
	})
	aliyun := pbAddr.GetAliyun()
	require.NotNil(t, aliyun)
	require.Equal(t, "s3://bucket/path/tidb-*", aliyun.BucketUrl)
	require.Empty(t, aliyun.AccessKeyId)
	require.Empty(t, aliyun.SecretAccessKey)
}
