package framework

import (
	"context"
	"fmt"
	"time"

	"github.com/pingcap/test-infra/sdk/resource"

	"github.com/onsi/ginkgo"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/pingcap/test-infra/sdk/resource/impl/k8s"
)

type Framework struct {
	BaseName  string
	Namespace string

	KubeClientSetPack *k8s.KubeClientSet
	DeletionTimeout   time.Duration

	beforeEachStarted bool

	// To make sure that this framework cleans up after itself, no matter what,
	// we install a Cleanup action before each test and clear it after.  If we
	// should abort, the AfterSuite hook should run all Cleanup actions.
	cleanupHandle CleanupActionHandle
}

func NewDefaultFramework(baseName string) *Framework {
	// TODO: add some default config
	return NewFramework(baseName, 5*time.Minute, nil)
}

func NewFramework(baseName string, deletionTimeout time.Duration, k *k8s.KubeClientSet) *Framework {
	f := &Framework{
		BaseName:          baseName,
		KubeClientSetPack: k,
		DeletionTimeout:   deletionTimeout,
	}

	ginkgo.BeforeEach(f.BeforeEach)
	ginkgo.AfterEach(f.AfterEach)

	return f
}

func (f *Framework) BeforeEach() {
	f.beforeEachStarted = true

	// The fact that we need this feels like a bug in ginkgo.
	// https://github.com/onsi/ginkgo/issues/222
	f.cleanupHandle = AddCleanupAction(f.AfterEach)

	ginkgo.By("[Init]: " + "Creating a kubernetes client; " + "config = " + TestContext.KubeConfig)
	f.Namespace = f.BaseName + "-" + resource.UniqueStr()
	k, err := k8s.NewKubeClientFromKubeConfig(f.Namespace, TestContext.KubeConfig)

	ginkgo.By(fmt.Sprintf("Creating namespace %s", f.Namespace))

	ExpectNoError(err)
	f.KubeClientSetPack = k

	_, err = f.KubeClientSetPack.ClientSet.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name: f.Namespace,
			Labels: map[string]string{
				"app.kubernetes.io/managed-by": "test-infra",
			},
		},
	}, metav1.CreateOptions{})
	ExpectNoError(err)
}

func (f *Framework) AfterEach() {
	if !f.beforeEachStarted {
		return
	}

	RemoveCleanupAction(f.cleanupHandle)

	ginkgo.By(fmt.Sprintf("Destroying namespace %s", f.Namespace))
	policy := metav1.DeletePropagationForeground
	ExpectNoError(f.KubeClientSetPack.ClientSet.CoreV1().Namespaces().Delete(context.TODO(), f.Namespace, metav1.DeleteOptions{PropagationPolicy: &policy}))
}

func (f *Framework) Testbed() string {
	return f.Namespace
}
