package account

import (
	"context"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/constants"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/models"
	"gopkg.in/yaml.v3"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"
	fake2 "sigs.k8s.io/controller-runtime/pkg/client/fake"
)

var (
	accountRW      accountReaderWriter
	sharedBucketRW SharedBucketReaderWriter
)
var cli = fake2.NewClientBuilder().Build()

func TestMain(m *testing.M) {
	initData(cli)
	sharedBucketRW = sharedBucketReaderWriter{
		sharedBucketReader{
			readCli: cli,
		},
		sharedBucketWriter{
			cli: cli,
		},
	}

	accountRW = accountReaderWriter{
		accountReader{
			readCli: cli,
		},
		accountWriter{
			cli: cli,
		},
	}
	code := m.Run()
	os.Exit(code)
}

func Test_Account(t *testing.T) {
	t.Run("list", func(t *testing.T) {
		list, err := accountRW.List(context.TODO())
		assert.NoError(t, err)
		assert.NotEmpty(t, list)
		assert.Equal(t, 3, len(list))
	})
	t.Run("list with filter", func(t *testing.T) {
		list, err := accountRW.List(context.TODO(), models.AccountFilter{CMEKEnabled: true})
		assert.NoError(t, err)
		assert.Equal(t, []models.DataPlaneAccountInfo{
			{AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount:  "account1",
				AWSRole:     "role1",
				CMEKEnabled: true,
			}}}, list)
	})
	t.Run("get default", func(t *testing.T) {
		a, err := accountRW.GetDefault(context.TODO())
		assert.NoError(t, err)
		assert.NotEmpty(t, a)
		assert.Equal(t, "account2", a.AWSAccount)
		assert.Equal(t, "role2", a.AWSRole)
	})
	t.Run("create", func(t *testing.T) {
		err := accountRW.Create(context.TODO(), &models.DataPlaneAccountInfo{
			AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount: "newAccount",
				AWSRole:    "newRole",
			},
		})
		assert.NoError(t, err)
	})
	t.Run("set to default", func(t *testing.T) {
		err := accountRW.SetToDefault(context.TODO(), &models.DataPlaneAccountInfo{
			AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount: "newAccount",
				AWSRole:    "newRole",
			},
		})
		assert.NoError(t, err)
		a, err := accountRW.GetDefault(context.TODO())
		assert.NoError(t, err)
		assert.NotEmpty(t, a)
		assert.Equal(t, "newAccount", a.AWSAccount)
		assert.Equal(t, "newRole", a.AWSRole)
	})
}

func Test_Binding(t *testing.T) {
	t.Run("get empty", func(t *testing.T) {
		b, err := accountRW.GetBinding(context.TODO(), models.AccountBindingTarget{
			TargetType: constants.AccountBindingVPC,
			TargetKey:  "test-vpc",
		})
		assert.NoError(t, err)
		assert.Empty(t, b)
	})
	t.Run("bind and unbind", func(t *testing.T) {
		err := accountRW.Bind(context.TODO(), models.DataPlaneAccountInfo{
			AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount: "account1",
				AWSRole:    "role1",
			},
		}, models.AccountBindingTarget{
			TargetType: constants.AccountBindingVPC,
			TargetKey:  "test-vpc-1",
		})
		assert.NoError(t, err)

		err = accountRW.Bind(context.TODO(), models.DataPlaneAccountInfo{
			AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount: "account2",
				AWSRole:    "role2",
			},
		}, models.AccountBindingTarget{
			TargetType: constants.AccountBindingVPC,
			TargetKey:  "test-vpc-2",
		})
		assert.NoError(t, err)

		b, err := accountRW.GetBinding(context.TODO(), models.AccountBindingTarget{
			TargetType: constants.AccountBindingVPC,
			TargetKey:  "test-vpc-2",
		})
		assert.NoError(t, err)
		assert.NotEmpty(t, b)
		assert.Equal(t, "account2", b.GetKey())

		err = accountRW.Unbind(context.TODO(), models.AccountBindingTarget{
			TargetType: constants.AccountBindingVPC,
			TargetKey:  "test-vpc-2",
		})
		assert.NoError(t, err)

		b, err = accountRW.GetBinding(context.TODO(), models.AccountBindingTarget{
			TargetType: constants.AccountBindingVPC,
			TargetKey:  "test-vpc-2",
		})
		assert.NoError(t, err)
		assert.Empty(t, b)
	})
}

func initData(cli client.Client) {
	accountList := []models.DataPlaneAccountInfo{
		{
			AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount:  "account1",
				AWSRole:     "role1",
				CMEKEnabled: true,
			},
		},
		{
			AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount: "account2",
				AWSRole:    "role2",
			},
		},
		{
			GCPAccountInfo: &models.GCPAccountInfo{
				ProjectID: "project1",
			},
		},
	}

	listYaml, err := yaml.Marshal(accountList)
	if err != nil {
		panic(err)
	}
	configData := map[string]string{}
	configData[constants.DataPlaneAccountConfigMapDefaultKey] = "account2"
	configData[constants.DataPlaneAccountConfigMapListKey] = string(listYaml)

	configMap := &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: constants.DataPlaneAccountNamespace,
			Name:      constants.DataPlaneAccountConfigMapName,
		},

		Data: configData,
	}
	cli.Create(context.TODO(), configMap)
}
