package allowlist

import (
	"context"
	"strings"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/log"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/common"
	"github.com/tidbcloud/serverless-service/internal/controller"
	"github.com/tidbcloud/serverless-service/internal/domain/facade"
	grpc_http "github.com/tidbcloud/serverless-service/internal/grpc/http"
	"github.com/tidbcloud/serverless-service/internal/service/cluster_svc"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"go.uber.org/zap"
	"google.golang.org/grpc"
)

const (
	ChangefeedFeaturePath            = "changefeeds"
	PrivateLinkConnectionFeaturePath = "privateLinkConnections"
	AuditLogFeaturePath              = "auditLog"
)

func UnaryServerInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
		if err := controller.DiCall(func(mgmtFacade facade.MgmtSvcFacade, featureGate facade.FeatureGate) error {
			return checkFeatureGate(ctx, mgmtFacade, featureGate)
		}); err != nil {
			return &common.EmptyResponse{}, err
		}
		return handler(ctx, req)
	}
}

func checkFeatureGate(
	ctx context.Context,
	mgmtFacade facade.MgmtSvcFacade,
	featureGate facade.FeatureGate,
) bizerr.BizError {
	path := grpc_http.HttpPath(ctx)
	if strings.Contains(path, ChangefeedFeaturePath) {
		cluster, err := getCluster(ctx, mgmtFacade)
		if err != nil {
			return err
		}
		return cluster_svc.ChangefeedFeatureGate(ctx, cluster)
	}
	if strings.Contains(path, PrivateLinkConnectionFeaturePath) {
		cluster, err := getCluster(ctx, mgmtFacade)
		if err != nil {
			return err
		}
		return cluster_svc.PrivateLinkConnectionFeatureGate(ctx, cluster)
	}
	if strings.Contains(path, AuditLogFeaturePath) {
		cluster, err := getCluster(ctx, mgmtFacade)
		if err != nil {
			return err
		}
		return cluster_svc.DatabaseAuditLogFeatureGate(ctx, cluster, featureGate)
	}
	return nil
}

func getCluster(ctx context.Context, mgmtFacade facade.MgmtSvcFacade) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	logger := log.WithContextL(ctx)
	path := grpc_http.HttpPath(ctx)
	cid, cidFound := grpc_http.GetClusterIdFromPath(path)
	if !cidFound {
		logger.Error("missing cluster id")
		return nil, bizerr.New(bizerr.ErrCodeParams, "missing cluster id")
	}
	cluster, err := mgmtFacade.GetCluster(ctx, cid, tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC)
	if err != nil {
		logger.Error("failed to get cluster", zap.Uint64("cluster_id", cid), zap.Error(err))
		return nil, err
	}
	return cluster, nil
}
