package api

import (
	"context"
	adminapiv1beta1 "dataservices/idl/pbgen/proto/v1beta1"
	adminapigrpc "dataservices/internal/controller/adminapi_grpc"
	"fmt"

	"github.com/gin-gonic/gin"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"github.com/tidbcloud/tiinf/sdk/auth"
	"github.com/tidbcloud/tiinf/tiinf"
	tigrpcclient "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"google.golang.org/grpc"
	"google.golang.org/protobuf/encoding/protojson"
)

func RegisterGrpcServices(server *grpc.Server) {
	// Register handlers here
	adminapiv1beta1.RegisterApiKeyServiceServer(server, &adminapigrpc.ApiKeyService{})
	adminapiv1beta1.RegisterApiSpecServiceServer(server, &adminapigrpc.ApiSpecService{})
	adminapiv1beta1.RegisterDataAppsServiceServer(server, &adminapigrpc.DataAppService{})
	adminapiv1beta1.RegisterDataSourcesServiceServer(server, &adminapigrpc.DataSourceService{})
	adminapiv1beta1.RegisterEndpointsServiceServer(server, &adminapigrpc.EndpointsService{})
	adminapiv1beta1.RegisterDeploymentsServiceServer(server, &adminapigrpc.DeploymentsService{})
}

func registerGrpcGateway(cfg tiinf.Config) (*runtime.ServeMux, error) {
	address := fmt.Sprintf("%s:%d", "localhost", cfg.TiInfConfig.GrpcServerConfig.Port)
	conn, err := tigrpcclient.NewClientConn(address, tigrpcclient.WithPrometheus(), tigrpcclient.WithTracing())
	if err != nil {
		return nil, err
	}
	mux := runtime.NewServeMux(
		runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{
			MarshalOptions: protojson.MarshalOptions{
				// UseProtoNames:   true,
				EmitUnpopulated: true,
			},
			UnmarshalOptions: protojson.UnmarshalOptions{
				DiscardUnknown: true,
			},
		}),
		runtime.WithErrorHandler(CustomGrpcGatewayHTTPErrorHandler),
		runtime.WithIncomingHeaderMatcher(CustomGrpcGatewayHeaderMatcher),
		runtime.WithMetadata(auth.AddHttpMetadata2GrpcContext),
	)
	// Register handlers here
	err = adminapiv1beta1.RegisterApiKeyServiceHandler(context.Background(), mux, conn)
	if err != nil {
		return nil, err
	}
	err = adminapiv1beta1.RegisterApiSpecServiceHandler(context.Background(), mux, conn)
	if err != nil {
		return nil, err
	}
	err = adminapiv1beta1.RegisterDataSourcesServiceHandler(context.Background(), mux, conn)
	if err != nil {
		return nil, err
	}
	err = adminapiv1beta1.RegisterEndpointsServiceHandler(context.Background(), mux, conn)
	if err != nil {
		return nil, err
	}
	err = adminapiv1beta1.RegisterDataAppsServiceHandler(context.Background(), mux, conn)
	if err != nil {
		return nil, err
	}
	err = adminapiv1beta1.RegisterDeploymentsServiceHandler(context.Background(), mux, conn)
	if err != nil {
		return nil, err
	}
	return mux, nil
}

func SetupHttpServer(router *gin.Engine, cfg tiinf.Config) error {
	auth.InitAccountClientByEndpoint(cfg.TiInfConfig.RPCConnConfig.AccountProvider, 0)

	mux, err := registerGrpcGateway(cfg)
	if err != nil {
		return err
	}
	router.Any("/healthz", func(c *gin.Context) {
		c.Status(200)
	})
	openApiPathWhitelist := []string{
		"/orgs/(\\d+)/healthz",
	}
	openApiPathPattern2ResourceMap := map[string]string{
		"/orgs/(\\d+)/v1beta1/.*": "dataservice/adminapi/orgs/{org_id}/all",
	}

	router.Use(
		Metric(),
		auth.GinGetIdentityInfoInterceptor(&auth.IdentityAuthOption{
			EnableLog: true,
		}),
		auth.GinPermissionCheckInterceptor(&auth.PermissionCheckerCommonParam{
			IdentityF:               auth.GinGetIdentityFunc,
			PathPattern2ResourceMap: openApiPathPattern2ResourceMap,
			PathWhitelist:           openApiPathWhitelist,
			GenAccessParam4GinF:     auth.GenAccessParam4GinWithPrefixOrgId,
		}),
		CheckApiKeyAppPermission,
	)
	router.Any("v1beta1/*any", gin.WrapF(mux.ServeHTTP))
	return nil
}
