package appbox

import (
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	eksv1beta1 "github.com/crossplane-contrib/provider-aws/apis/eks/v1beta1"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	regionalapiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/message"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	appboxsvc "github.com/tidbcloud/infra-provider/regional-server/pkg/service/appbox"
	"google.golang.org/protobuf/encoding/protojson"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestAPPBoxHandler_CreateAPPBox(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	s := runtime.NewScheme()
	v1alpha1.AddToScheme(s)
	mockcli := fake.NewClientBuilder().WithScheme(s).Build()
	svc := appboxsvc.NewAPPBoxService(mockcli, config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			Provider: consts.ProviderTypeEKS,
		},
	}, config.TemplatesConfig{})
	handler := NewAPPBoxHandler(svc)

	t.Run("request is not valid", func(t *testing.T) {
		in := &message.CreateAPPBoxReq{}
		buf, err := json.Marshal(in)
		assert.Nil(t, err)
		body := bytes.NewReader(buf)
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request = httptest.NewRequest("POST", "/api/dedicated/v1/appboxes", body)
		handler.CreateAPPBox(ctx)
		assert.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
	})

	t.Run("create appbox failed", func(t *testing.T) {
		in := &message.CreateAPPBoxReq{
			VPCID:     "1",
			Name:      "appbox-test",
			Namespace: "default",
		}
		buf, err := json.Marshal(in)
		assert.Nil(t, err)
		body := bytes.NewReader(buf)
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request = httptest.NewRequest("POST", "/api/dedicated/v1/appboxes", body)
		handler.CreateAPPBox(ctx)
		assert.Equal(t, http.StatusNotFound, w.Result().StatusCode)
	})
	t.Run("create appbox success", func(t *testing.T) {
		assert.Nil(t, mockcli.Create(context.Background(), &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "vcp-test",
				Namespace: "test",
				Labels: map[string]string{
					v1alpha1.VPCIDKey: "1",
				},
			},
		}))
		in := &message.CreateAPPBoxReq{
			VPCID:     "1",
			Name:      "appbox-test",
			Namespace: "default",
		}
		buf, err := json.Marshal(in)
		assert.Nil(t, err)
		body := bytes.NewReader(buf)
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request = httptest.NewRequest("POST", "/api/dedicated/v1/appboxes", body)
		handler.CreateAPPBox(ctx)
		assert.Equal(t, http.StatusAccepted, w.Result().StatusCode)
	})
}

func TestAPPBoxHandler_DeleteAPPBox(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	s := runtime.NewScheme()
	v1alpha1.AddToScheme(s)
	mockcli := fake.NewClientBuilder().WithScheme(s).Build()
	svc := appboxsvc.NewAPPBoxService(mockcli, config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			Provider: consts.ProviderTypeEKS,
			Env:      "test",
			RegionConfig: config.RegionConfig{
				Name: "us-west-2",
			},
		},
	}, config.TemplatesConfig{})
	handler := NewAPPBoxHandler(svc)
	t.Run("request is not valid", func(t *testing.T) {
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request = httptest.NewRequest("DELETE", "/api/dedicated/v1/appboxes", nil)
		handler.DeleteAPPBox(ctx)
		assert.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
	})

	t.Run("delete appbox success", func(t *testing.T) {
		assert.Nil(t, mockcli.Create(context.Background(), &v1alpha1.APPBox{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "appbox-test",
				Namespace: "dedicated-test-us-west-2",
			},
		}))
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request = httptest.NewRequest("DELETE", "/api/dedicated/v1/appboxes/appbox-test", nil)
		ctx.Params = []gin.Param{
			{Key: "name", Value: "appbox-test"},
		}
		handler.DeleteAPPBox(ctx)
		assert.Equal(t, http.StatusAccepted, w.Result().StatusCode)
	})
}

func TestAPPBoxHandler_GetAPPBox(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	s := runtime.NewScheme()
	v1alpha1.AddToScheme(s)
	eksv1beta1.SchemeBuilder.AddToScheme(s)
	mockcli := fake.NewClientBuilder().WithScheme(s).Build()
	svc := appboxsvc.NewAPPBoxService(mockcli, config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			Provider: consts.ProviderTypeEKS,
			Env:      "test",
			RegionConfig: config.RegionConfig{
				Name: "us-west-2",
			},
		},
	}, config.TemplatesConfig{})
	handler := NewAPPBoxHandler(svc)

	t.Run("request is not valid", func(t *testing.T) {
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request = httptest.NewRequest("GET", "/api/dedicated/v1/appboxes", nil)
		handler.GetAPPBox(ctx)
		assert.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
	})
	t.Run("get appbox success", func(t *testing.T) {
		assert.Nil(t, mockcli.Create(context.Background(), &v1alpha1.APPBox{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "appbox-test",
				Namespace: "dedicated-test-us-west-2",
			},
			Spec: v1alpha1.APPBoxSpec{
				Provider: consts.ProviderTypeEKS,
				VPCRef: &v1alpha1.Reference{
					Name:      "vcp-test",
					Namespace: "dedicated-test-us-west-2",
				},
			},
			Status: v1alpha1.APPBoxStatus{
				KubernetesClusterName: "kubernetes-123",
			},
		}))
		assert.Nil(t, mockcli.Create(context.Background(), &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "vcp-test",
				Namespace: "dedicated-test-us-west-2",
				Labels: map[string]string{
					v1alpha1.VPCIDKey: "1",
				},
			},
			Spec: v1alpha1.VPCSpec{
				AccountRef: &v1alpha1.AccountReference{
					AccountID: "2",
				},
			},
		}))
		assert.Nil(t, mockcli.Create(context.TODO(), &eksv1beta1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Name: "kubernetes-123",
			},
			Status: eksv1beta1.ClusterStatus{
				AtProvider: eksv1beta1.ClusterObservation{
					Endpoint:                 "https://kubernetes-123.us-west-1.eks.amazonaws.com",
					CertificateAuthorityData: "Y2EtZGF0YQ==",
					ResourcesVpcConfig: eksv1beta1.VpcConfigResponse{
						ClusterSecurityGroupID: "sg-123",
					},
				},
			},
		}))
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request = httptest.NewRequest("GET", "/api/dedicated/v1/appboxes/appbox-test", nil)
		ctx.Params = []gin.Param{
			{Key: "name", Value: "appbox-test"},
		}
		handler.GetAPPBox(ctx)
		assert.Equal(t, http.StatusOK, w.Result().StatusCode)
	})
}

func TestAPPBoxHandler_ListAPPBoxes(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	s := runtime.NewScheme()
	v1alpha1.AddToScheme(s)
	mockcli := fake.NewClientBuilder().WithScheme(s).Build()
	svc := appboxsvc.NewAPPBoxService(mockcli, config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			Provider: consts.ProviderTypeEKS,
		},
	}, config.TemplatesConfig{})
	handler := NewAPPBoxHandler(svc)
	t.Run("request is not valid", func(t *testing.T) {
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request = httptest.NewRequest("GET", "/api/dedicated/v1/appboxes", nil)
		handler.ListAPPBoxes(ctx)
		assert.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
	})
	t.Run("list appboxes success", func(t *testing.T) {
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request = httptest.NewRequest("GET", "/api/dedicated/v1/appboxes?tenantId=2", nil)
		handler.ListAPPBoxes(ctx)
		assert.Equal(t, http.StatusOK, w.Result().StatusCode)
		res := &regionalapiv1.ListAPPBoxesResponse{}
		err := protojson.Unmarshal(w.Body.Bytes(), res)
		assert.Nil(t, err)
		assert.Equal(t, 0, len(res.Appboxes))
	})
}
