package bff

import (
	"context"
	"fmt"
	"time"

	"braces.dev/errtrace"
	"github.com/gin-gonic/gin"
	"google.golang.org/grpc"

	bffclusterv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/bff/cluster_management/v1beta1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/app/common"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/domain/service/auth"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/domain/service/rbac"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/infra/accountimpl"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/pkg/apimiddleware/reqmodifier"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/pkg/config"
)

func SetupConsoleApiHandler(eng *gin.Engine, timeout time.Duration) error {
	address := config.Global().TiDBMgmtServiceConfig.Address

	conn, err := common.CreateGrpcConn(address, timeout)
	if err != nil {
		return errtrace.Wrap(fmt.Errorf("failed to dial server: %w", err))
	}

	err = registerHandlers(eng, conn)
	if err != nil {
		return errtrace.Wrap(fmt.Errorf("failed to register gateway: %w", err))
	}
	return nil
}

func registerHandlers(eng *gin.Engine, conn *grpc.ClientConn) error {
	gwmux := common.MakeGatewayMux()

	err := bffclusterv1beta1.RegisterClusterServiceHandler(context.Background(), gwmux, conn)
	if err != nil {
		return errtrace.Wrap(err)
	}

	// init common middlewares
	accountClient := accountimpl.NewAccount()
	authSvc := auth.NewAuthService(accountClient)
	rbacSvc := rbac.NewRBACService(accountClient, nil)

	apiGroup := eng.Group("/api/v2023/clusterManagement/v1beta1")
	apiGroup.Use(
		authSvc.RemoveApiKeyHeader(),
		authSvc.CheckUserAndPermission(), // check auth by auth0 and api key (authentication)
		authSvc.TransformAuthnCtx,        // set grpc metadata (authentication data processing in ctx)
		rbacSvc.CheckRBAC,                // rbac middleware (authorization))
	)

	// strip the base paths of the Kong pre-processed requests, to use the same gRPC handler to serve both OpenAPI and WebUI API.
	apiGroup.Use(reqmodifier.ServePathStartFromAny)
	apiGroup.Any("/*any", gin.WrapF(gwmux.ServeHTTP))

	return nil
}
