package central

import (
	"bytes"
	"context"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/tidbcloud/clusterproxy/resolve"
	"github.com/tidbcloud/clusterproxy/route"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes/fake"
)

func TestChangePassword(t *testing.T) {
	ctx := &route.Context{
		Cli: fake.NewSimpleClientset(),
	}
	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "db-tidb",
			Namespace: "tidb42",
		},
		Spec: v1.ServiceSpec{
			Ports: []v1.ServicePort{
				{
					Name:       "mysql",
					Port:       5000,
					TargetPort: intstr.FromInt(4000),
				},
			},
		},
	}
	stop := make(chan struct{})
	factory := informers.NewSharedInformerFactory(ctx.Cli, 0)
	svcLister := factory.Core().V1().Services().Lister()
	factory.Start(stop)
	factory.WaitForCacheSync(stop)
	ctx.SvcLister = svcLister
	ctx.Cli.CoreV1().Services("tidb42").Create(context.TODO(), svc, metav1.CreateOptions{})

	doRequest := func(body []byte) *httptest.ResponseRecorder {
		req, _ := http.NewRequest(
			"POST", "/",
			bytes.NewBuffer(body))
		recorder := httptest.NewRecorder()
		cl := resolve.Cluster{Name: "db", Namespace: "tidb42"}
		getHandler(ctx, cl).ServeHTTP(recorder, req)
		return recorder
	}

	password := "password"
	newPassword := "password2"
	encodedPassword := base64.URLEncoding.EncodeToString([]byte(password))
	encodedNewPassword := base64.URLEncoding.EncodeToString([]byte(newPassword))

	recorder := doRequest([]byte(`{}`))
	if recorder.Code != http.StatusBadRequest {
		t.Fatalf("Response code got = %v, want = %v. %v", recorder.Code, http.StatusBadRequest, recorder.Body)
	}

	errMsg := "dial tcp: lookup"
	// V1 no password
	recorder = doRequest([]byte(fmt.Sprintf("{\"password\":\"%s\"}", encodedPassword)))
	if recorder.Code != http.StatusInternalServerError {
		t.Fatalf("Response code got = %v, want = %v. %v", recorder.Code, http.StatusInternalServerError, recorder.Body)
	}
	if !strings.Contains(string(recorder.Body.Bytes()), errMsg) {
		t.Fatalf("Expected "+errMsg+", got = %v", string(recorder.Body.Bytes()))
	}

	// V2 password
	recorder = doRequest([]byte(
		fmt.Sprintf("{\"currentPassword\":\"%s\",\"newPassword\":\"%s\"}", encodedPassword, encodedNewPassword)))
	if recorder.Code != http.StatusInternalServerError {
		t.Fatalf("Response code got = %v, want = %v. %v", recorder.Code, http.StatusInternalServerError, recorder.Body)
	}
	if !strings.Contains(string(recorder.Body.Bytes()), errMsg) {
		t.Fatalf("Expected "+errMsg+", got = %v", string(recorder.Body.Bytes()))
	}
}

func TestConnectionTest(t *testing.T) {
	ctx := &route.Context{
		Cli: fake.NewSimpleClientset(),
	}
	req, _ := http.NewRequest("POST", "/", bytes.NewBuffer([]byte(`{"Type":"kafka", "User":"root"}`)))
	recorder := httptest.NewRecorder()

	cl := resolve.Cluster{Namespace: "tidb42"}
	connectionGetHandler(ctx, cl).ServeHTTP(recorder, req)
	if recorder.Code != http.StatusBadRequest {
		t.Fatalf("Response code got = %v, want = %v.", recorder.Code, http.StatusBadRequest)
	}

	req, _ = http.NewRequest("POST", "/", bytes.NewBuffer([]byte(`{"Type":"mysql", "User":"root"}`)))
	connectionGetHandler(ctx, cl).ServeHTTP(recorder, req)
	if recorder.Code != http.StatusBadRequest {
		t.Fatalf("Response code got = %v, want = %v.", recorder.Code, http.StatusBadRequest)
	}
}
