package openapi

import (
	"context"
	"fmt"
	"time"

	"braces.dev/errtrace"
	"github.com/gin-gonic/gin"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"

	openapingv1beta2 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/nextgen/v1beta2"
	"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"
	tidbmgmtservice "github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/infra/mgmtservicecmock"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/pkg/apimiddleware/reqmodifier"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/pkg/config"
)

// SetupTiDBXApiHandler sets up the TiDBX API handler with authentication and authorization middlewares.
func SetupTiDBXApiHandler(eng *gin.Engine, timeout time.Duration) error {
	address := config.Global().TiDBMgmtServiceConfig.Address
	fakeGRPC := config.Global().TiDBMgmtServiceConfig.LocalMode

	if fakeGRPC {
		tidbmgmtservice.SetupFakeMgmtGRPCServer(address)
		return nil
	}

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

	gwmux := common.MakeGatewayMux()

	err = registerServiceHandlers(conn, gwmux)

	if err != nil {
		return errtrace.Wrap(fmt.Errorf("failed to register gRPC handlers: %w", err))
	}

	accountClient := accountimpl.NewAccount()

	var authNService = auth.NewTiDBXAuthNService(accountClient)
	var authZService = rbac.NewTiDBXAuthZService(accountClient)

	openAPIMiddlewares(eng, authNService, authZService, gwmux)
	uiAPIMiddlewares(eng, authNService, authZService, gwmux)

	return nil
}

// registerServiceHandlers registers all the gRPC service handlers to the gateway mux.
func registerServiceHandlers(
	conn *grpc.ClientConn,
	gwmux *runtime.ServeMux,
) error {
	if err := openapingv1beta2.RegisterTidbServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterPublicConnectionSettingServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterByocVpcPeeringServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterBackupServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterPrivateEndpointConnectionServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterRegionServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterResourcePoolServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterDatabaseAuditLogServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterLogRedactionPolicyServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterQuotaServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterCustomerManagedEncryptionKeyServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterExportServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}
	if err := openapingv1beta2.RegisterPlanReplayerServiceHandler(context.Background(), gwmux, conn); err != nil {
		return errtrace.Wrap(err)
	}

	return nil
}

// openAPIMiddlewares applies the authentication and authorization middlewares for OpenAPI routes.
func openAPIMiddlewares(eng *gin.Engine, authNService auth.TiDBXAuthNService, authZService rbac.TiDBXAuthZService, gwmux *runtime.ServeMux) {
	openapiGroup := eng.Group("/v1beta2")
	openapiGroup.Use(
		authNService.GetIdentityInfo(),
		authNService.TransOpenAPIUser,
		authZService.CheckUriRbac,
		authZService.SetGrpcGatewayMetaData,
	)
	openapiGroup.Use(reqmodifier.ServePathStartFromAny)
	openapiGroup.Use(reqmodifier.RewriteBracketParams())
	openapiGroup.Any("/*any", gin.WrapF(gwmux.ServeHTTP))
}

// uiAPIMiddlewares applies the authentication and authorization middlewares for UI API routes.
func uiAPIMiddlewares(eng *gin.Engine, authNService auth.TiDBXAuthNService, authZService rbac.TiDBXAuthZService, gwmux *runtime.ServeMux) {
	uiApiGroup := eng.Group("/api/v2025/cloud/v1beta2")
	uiApiGroup.Use(
		authNService.RemoveApiKeyHeader,
		authNService.GetIdentityInfo(),
		authNService.TransTokenUser,
		authZService.CheckUriRbac,
		authZService.SetGrpcGatewayMetaData,
	)
	uiApiGroup.Use(reqmodifier.ServePathStartFromAny)
	uiApiGroup.Use(reqmodifier.RewriteBracketParams())
	uiApiGroup.Any("/*any", gin.WrapF(gwmux.ServeHTTP))
}
