package server

import (
	"context"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"github.com/stretchr/testify/require"
	"google.golang.org/protobuf/types/known/emptypb"

	apiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-server/v1"
	apicommon "github.com/tidbcloud/infra-provider/api/tidbcloud/common"
)

type recordingGKEHeterogeneousGatewayServer struct {
	apiv1.UnimplementedClusterServiceServer

	createDeployReq   *apiv1.CreateGKEHeterogeneousDeployRequest
	updateDeployReq   *apiv1.UpdateGKEHeterogeneousDeployRequest
	listDeploysReq    *apiv1.ListGKEHeterogeneousDeploysRequest
	getDeployReq      *apiv1.GetGKEHeterogeneousDeployRequest
	deleteDeployReq   *apiv1.DeleteGKEHeterogeneousDeployRequest
	createNodePoolReq *apiv1.CreateGKEHeterogeneousNodePoolRequest
	listNodePoolsReq  *apiv1.ListGKEHeterogeneousNodePoolsRequest
	getNodePoolsReq   *apiv1.GetGKEHeterogeneousNodePoolsRequest
}

func (s *recordingGKEHeterogeneousGatewayServer) CreateGKEHeterogeneousDeploy(_ context.Context, req *apiv1.CreateGKEHeterogeneousDeployRequest) (*apiv1.GKEHeterogeneousDeploy, error) {
	s.createDeployReq = req
	return &apiv1.GKEHeterogeneousDeploy{}, nil
}

func (s *recordingGKEHeterogeneousGatewayServer) UpdateGKEHeterogeneousDeploy(_ context.Context, req *apiv1.UpdateGKEHeterogeneousDeployRequest) (*apiv1.GKEHeterogeneousDeploy, error) {
	s.updateDeployReq = req
	return &apiv1.GKEHeterogeneousDeploy{}, nil
}

func (s *recordingGKEHeterogeneousGatewayServer) ListGKEHeterogeneousDeploys(_ context.Context, req *apiv1.ListGKEHeterogeneousDeploysRequest) (*apiv1.ListGKEHeterogeneousDeploysResponse, error) {
	s.listDeploysReq = req
	return &apiv1.ListGKEHeterogeneousDeploysResponse{}, nil
}

func (s *recordingGKEHeterogeneousGatewayServer) GetGKEHeterogeneousDeploy(_ context.Context, req *apiv1.GetGKEHeterogeneousDeployRequest) (*apiv1.GKEHeterogeneousDeploy, error) {
	s.getDeployReq = req
	return &apiv1.GKEHeterogeneousDeploy{}, nil
}

func (s *recordingGKEHeterogeneousGatewayServer) DeleteGKEHeterogeneousDeploy(_ context.Context, req *apiv1.DeleteGKEHeterogeneousDeployRequest) (*emptypb.Empty, error) {
	s.deleteDeployReq = req
	return &emptypb.Empty{}, nil
}

func (s *recordingGKEHeterogeneousGatewayServer) CreateGKEHeterogeneousNodePool(_ context.Context, req *apiv1.CreateGKEHeterogeneousNodePoolRequest) (*apiv1.GKEHeterogeneousNodePool, error) {
	s.createNodePoolReq = req
	return &apiv1.GKEHeterogeneousNodePool{}, nil
}

func (s *recordingGKEHeterogeneousGatewayServer) ListGKEHeterogeneousNodePools(_ context.Context, req *apiv1.ListGKEHeterogeneousNodePoolsRequest) (*apiv1.ListGKEHeterogeneousNodePoolsResponse, error) {
	s.listNodePoolsReq = req
	return &apiv1.ListGKEHeterogeneousNodePoolsResponse{}, nil
}

func (s *recordingGKEHeterogeneousGatewayServer) GetGKEHeterogeneousNodePools(_ context.Context, req *apiv1.GetGKEHeterogeneousNodePoolsRequest) (*apiv1.ListGKEHeterogeneousNodePoolsResponse, error) {
	s.getNodePoolsReq = req
	return &apiv1.ListGKEHeterogeneousNodePoolsResponse{}, nil
}

func TestClusterServer_GKEHeterogeneousGatewayMappings(t *testing.T) {
	server := &recordingGKEHeterogeneousGatewayServer{}
	mux := runtime.NewServeMux()
	require.NoError(t, apiv1.RegisterClusterServiceHandlerServer(context.Background(), mux, server))

	serveGKEHeterogeneousGatewayRequest(t, mux, http.MethodPost, "/clusters/path-cluster/gkeHeterogeneous/deploys", `{
		"clusterId": "body-cluster",
		"componentType": "TIDB",
		"nodePoolNames": ["np-a", "np-b"],
		"instanceTypes": ["n2-standard-8", "n2-standard-16"],
		"tidbGroupId": "group-a",
		"computeClassName": "priority-a"
	}`)
	require.Equal(t, "path-cluster", server.createDeployReq.GetClusterId())
	require.Equal(t, apicommon.ComponentType_TIDB, server.createDeployReq.GetComponentType())
	require.Equal(t, []string{"np-a", "np-b"}, server.createDeployReq.GetNodePoolNames())
	require.Equal(t, []string{"n2-standard-8", "n2-standard-16"}, server.createDeployReq.GetInstanceTypes())
	require.Equal(t, "group-a", server.createDeployReq.GetTidbGroupId())
	require.Equal(t, "priority-a", server.createDeployReq.GetComputeClassName())

	serveGKEHeterogeneousGatewayRequest(t, mux, http.MethodPatch, "/clusters/path-cluster/gkeHeterogeneous/deploys/TIDB", `{
		"clusterId": "body-cluster",
		"componentType": "TIKV",
		"nodePoolNames": ["np-c"],
		"instanceTypes": ["n2-standard-32"],
		"tidbGroupId": "group-b",
		"computeClassName": "priority-b"
	}`)
	require.Equal(t, "path-cluster", server.updateDeployReq.GetClusterId())
	require.Equal(t, apicommon.ComponentType_TIDB, server.updateDeployReq.GetComponentType())
	require.Equal(t, []string{"np-c"}, server.updateDeployReq.GetNodePoolNames())
	require.Equal(t, []string{"n2-standard-32"}, server.updateDeployReq.GetInstanceTypes())
	require.Equal(t, "group-b", server.updateDeployReq.GetTidbGroupId())
	require.Equal(t, "priority-b", server.updateDeployReq.GetComputeClassName())

	serveGKEHeterogeneousGatewayRequest(t, mux, http.MethodGet, "/clusters/path-cluster/gkeHeterogeneous/deploys", "")
	require.Equal(t, "path-cluster", server.listDeploysReq.GetClusterId())

	serveGKEHeterogeneousGatewayRequest(t, mux, http.MethodGet, "/clusters/path-cluster/gkeHeterogeneous/deploys/TIDB?tidb_group_id=group-c", "")
	require.Equal(t, "path-cluster", server.getDeployReq.GetClusterId())
	require.Equal(t, apicommon.ComponentType_TIDB, server.getDeployReq.GetComponentType())
	require.Equal(t, "group-c", server.getDeployReq.GetTidbGroupId())

	serveGKEHeterogeneousGatewayRequest(t, mux, http.MethodDelete, "/clusters/path-cluster/gkeHeterogeneous/deploys/TIDB?tidb_group_id=group-d", "")
	require.Equal(t, "path-cluster", server.deleteDeployReq.GetClusterId())
	require.Equal(t, apicommon.ComponentType_TIDB, server.deleteDeployReq.GetComponentType())
	require.Equal(t, "group-d", server.deleteDeployReq.GetTidbGroupId())

	serveGKEHeterogeneousGatewayRequest(t, mux, http.MethodPost, "/clusters/path-cluster/gkeHeterogeneous/nodePools", `{
		"clusterId": "body-cluster",
		"instanceType": "n2-standard-64"
	}`)
	require.Equal(t, "path-cluster", server.createNodePoolReq.GetClusterId())
	require.Equal(t, "n2-standard-64", server.createNodePoolReq.GetInstanceType())

	serveGKEHeterogeneousGatewayRequest(t, mux, http.MethodGet, "/clusters/path-cluster/gkeHeterogeneous/nodePools?component_type=TIDB&tidb_group_id=group-e", "")
	require.Equal(t, "path-cluster", server.listNodePoolsReq.GetClusterId())
	require.Equal(t, apicommon.ComponentType_TIDB, server.listNodePoolsReq.GetComponentType())
	require.Equal(t, "group-e", server.listNodePoolsReq.GetTidbGroupId())

	serveGKEHeterogeneousGatewayRequest(t, mux, http.MethodGet, "/clusters/path-cluster/gkeHeterogeneous/nodePools/TIKV?tidb_group_id=ignored", "")
	require.Equal(t, "path-cluster", server.getNodePoolsReq.GetClusterId())
	require.Equal(t, apicommon.ComponentType_TIKV, server.getNodePoolsReq.GetComponentType())
	require.Equal(t, "ignored", server.getNodePoolsReq.GetTidbGroupId())
}

func serveGKEHeterogeneousGatewayRequest(t *testing.T, handler http.Handler, method, path, body string) {
	t.Helper()

	req := httptest.NewRequest(method, path, strings.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()
	handler.ServeHTTP(rec, req)
	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
}
