package utils

import (
	"context"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"

	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/tempo-wrapper/internal/config"
	"github.com/tidbcloud/tidb-management-service/tempo-wrapper/pkg/grpc"
	mgmtgrpc "github.com/tidbcloud/tidb-management-service/tempo-wrapper/pkg/grpc/mgmt"
	"github.com/tidbcloud/tidb-management-service/utilities/allowlist"
)

var allowList *allowlist.AllowLockList

func SetAllowList(allow *allowlist.AllowLockList) {
	allowList = allow
}

func IsServerlessAllow(ctx context.Context, orgId uint64) (bool, error) {
	// serverless allow all
	return true, nil
}

func IsLockOrgId(orgId uint64) bool {
	if allowList != nil {
		return allowList.LockList.IsLockOrgId(orgId)
	}
	return false
}

func IsDedicatedAllow(ctx context.Context, orgId, projectId uint64) (bool, error) {
	// allowlist not enabled
	if config.Config().AllowAndLockList.Enable && allowList != nil {
		allow, err := allowList.DedicatedAllowList().IsAllow(ctx, orgId, projectId)
		if err != nil {
			return false, err
		}
		return allow, nil
	}

	tilog.Warn(ctx, "allowlist not enabled", zap.Uint64("orgId", orgId), zap.Uint64("projectId", projectId))
	return false, nil
}

func IsNewServerlessCluster(ctx *gin.Context, clusterId, projectId, orgId uint64) (bool, error) {
	// serverless
	allow, err := IsServerlessAllow(ctx.Request.Context(), orgId)
	if err != nil {
		tilog.Warn(
			ctx.Request.Context(),
			"check is serverless cluster failed",
			zap.Uint64("clusterid", clusterId),
			zap.Error(err),
		)
		return false, err
	}

	if allow {
		// use internal api to show deleted clusters
		serverlessCluser, err := mgmtgrpc.InternalListClusters(
			grpc.NewContext(ctx),
			&tidbmgmtservicev1.ListClustersRequest{
				Filter: &tidbmgmtservicev1.ClusterFilter{
					ClusterIds:  []uint64{clusterId},
					ProjectIds:  []uint64{projectId},
					TenantIds:   []uint64{orgId},
					ClusterType: tidbmgmtservicev1.DeployType_DEPLOY_TYPE_SERVERLESS.Enum(),
					View:        tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC,
					Deleted:     proto.Bool(true),
				},
			},
		)
		if err != nil {
			tilog.Warn(
				ctx.Request.Context(),
				"get serverless cluster failed",
				zap.Uint64("clusterid", clusterId),
				zap.Error(err),
			)
			return false, err
		}

		if serverlessCluser == nil || serverlessCluser.Total == 0 || len(serverlessCluser.Clusters) == 0 {
			tilog.Warn(ctx.Request.Context(), "serverless cluster not found", zap.Uint64("clusterid", clusterId))
			return false, nil
		}

		if serverlessCluser.Clusters[0].ClusterId == clusterId {
			return true, nil
		}
	}
	return false, nil
}

func IsNewDedicatedCluster(ctx *gin.Context, clusterId, projectId, orgId uint64) (bool, error) {
	// dedicated
	allow, err := IsDedicatedAllow(ctx.Request.Context(), orgId, projectId)
	if err != nil {
		tilog.Warn(
			ctx.Request.Context(),
			"check is dedicated cluster failed",
			zap.Uint64("clusterid", clusterId),
			zap.Error(err),
		)
		return false, err
	}
	tilog.Info(
		ctx.Request.Context(),
		"check is new dedicated cluster",
		zap.Uint64("clusterid", clusterId),
		zap.Bool("allow", allow),
	)

	if allow {
		// use internal api to show deleted clusters
		dedicatedCluser, err := mgmtgrpc.InternalListClusters(
			grpc.NewContext(ctx),
			&tidbmgmtservicev1.ListClustersRequest{
				Filter: &tidbmgmtservicev1.ClusterFilter{
					ClusterIds:  []uint64{clusterId},
					ProjectIds:  []uint64{projectId},
					TenantIds:   []uint64{orgId},
					ClusterType: tidbmgmtservicev1.DeployType_DEPLOY_TYPE_DEDICATED.Enum(),
					Deleted:     proto.Bool(true),
				},
			},
		)
		if err != nil {
			tilog.Warn(
				ctx.Request.Context(),
				"get dedicated cluster failed",
				zap.Uint64("clusterid", clusterId),
				zap.Error(err),
			)
			return false, err
		}

		if dedicatedCluser == nil || dedicatedCluser.Total == 0 || len(dedicatedCluser.Clusters) == 0 {
			tilog.Warn(ctx.Request.Context(), "dedicated cluster not found", zap.Uint64("clusterid", clusterId))
			return false, nil
		}

		if dedicatedCluser.Clusters[0].ClusterId == clusterId {
			return true, nil
		}
	}
	return false, nil
}
