package resourcemanager

import (
	"context"
	"testing"

	helmv2 "github.com/fluxcd/helm-controller/api/v2"
	"github.com/stretchr/testify/assert"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"

	commonerrors "github.com/tidbcloud/observability/pkg/common/errors"
	"github.com/tidbcloud/observability/pkg/common/message"
)

func newHelmClient(t *testing.T) client.Client {
	scheme := runtime.NewScheme()
	assert.NoError(t, helmv2.AddToScheme(scheme))
	return fake.NewClientBuilder().WithScheme(scheme).Build()
}

func Test_HelmRelease_Create_Get_Update_Delete_Exists(t *testing.T) {
	cli := newHelmClient(t)
	rw := helmReleaseReaderWriter{cli: cli, readCli: cli}

	hr := &helmv2.HelmRelease{ObjectMeta: metav1.ObjectMeta{Namespace: "hns1", Name: "hr1"}}
	err := rw.Create(context.TODO(), hr)
	assert.NoError(t, err)

	got, err := rw.Get(context.TODO(), "hns1", "hr1")
	assert.NoError(t, err)
	assert.Equal(t, "hr1", got.Name)

	got.Labels = map[string]string{"x": "y"}
	err = rw.Update(context.TODO(), got)
	assert.NoError(t, err)

	got2, err := rw.Get(context.TODO(), "hns1", "hr1")
	assert.NoError(t, err)
	assert.Equal(t, "y", got2.Labels["x"])

	exists, err := rw.IsHelmReleaseExists(context.TODO(), "hns1", "hr1")
	assert.NoError(t, err)
	assert.True(t, exists)

	err = rw.Delete(context.TODO(), "hns1", "hr1")
	assert.NoError(t, err)

	_, err = rw.Get(context.TODO(), "hns1", "hr1")
	assert.Error(t, err)
	assert.True(t, commonerrors.Is(err, commonerrors.HelmReleaseNotFound))

	exists, err = rw.IsHelmReleaseExists(context.TODO(), "hns1", "hr1")
	assert.NoError(t, err)
	assert.False(t, exists)
}

func Test_HelmRelease_Delete_NotFound(t *testing.T) {
	cli := newHelmClient(t)
	rw := helmReleaseReaderWriter{cli: cli, readCli: cli}

	err := rw.Delete(context.TODO(), "hns1", "missing")
	assert.Error(t, err)
	assert.True(t, commonerrors.Is(err, commonerrors.HelmReleaseNotFound))
}

func Test_HelmRelease_List_and_ListAll(t *testing.T) {
	cli := newHelmClient(t)
	rw := helmReleaseReaderWriter{cli: cli, readCli: cli}

	hr1 := &helmv2.HelmRelease{ObjectMeta: metav1.ObjectMeta{Namespace: "nsA", Name: "a1"}}
	hr2 := &helmv2.HelmRelease{ObjectMeta: metav1.ObjectMeta{Namespace: "nsA", Name: "a2"}}
	hr3 := &helmv2.HelmRelease{ObjectMeta: metav1.ObjectMeta{Namespace: "nsB", Name: "b1"}}
	assert.NoError(t, cli.Create(context.TODO(), hr1))
	assert.NoError(t, cli.Create(context.TODO(), hr2))
	assert.NoError(t, cli.Create(context.TODO(), hr3))

	listA, err := rw.List(context.TODO(), "nsA", message.ContinueRequest{Limit: 2, ContinueToken: "token"})
	assert.NoError(t, err)
	namesA := make([]string, 0, len(listA.Items))
	for _, it := range listA.Items {
		namesA = append(namesA, it.Name)
	}
	assert.Contains(t, namesA, "a1")
	assert.Contains(t, namesA, "a2")

	listAll, err := rw.ListAll(context.TODO(), message.ContinueRequest{Limit: 10})
	assert.NoError(t, err)
	namesAll := make([]string, 0, len(listAll.Items))
	for _, it := range listAll.Items {
		namesAll = append(namesAll, it.Name)
	}
	assert.Contains(t, namesAll, "a1")
	assert.Contains(t, namesAll, "a2")
	assert.Contains(t, namesAll, "b1")
}
