package feature

import (
	"context"

	"github.com/pingcap/errcode"
	"github.com/pingcap/errors"
	"github.com/tidbcloud/dataflow-service/app/repo/entity"
	"github.com/tidbcloud/dataflow-service/idl/pbgen/proto/dataflow"
	"github.com/tidbcloud/dataflow-service/internal/db/dbutil"
	"github.com/tidbcloud/dataflow-service/internal/service"
	pkgrpcli "github.com/tidbcloud/dataflow-service/pkg/grpc/grpcli"
	"github.com/tidbcloud/pkgv2/log"
	"github.com/tidbcloud/pkgv2/tracing"
	"go.opentelemetry.io/otel"
	"go.uber.org/zap"

	"github.com/tidbcloud/dataflow-service/internal/model/backup"
	featureSwitcher "github.com/tidbcloud/dataflow-service/internal/model/feature"
)

var Tracer = otel.Tracer("central/service/feature")

type dpFeatureService struct {
	dataflow.UnsafeDPFeatureServiceServer
	service.OperationsService
	backupModel backup.Model

	mgmtCli pkgrpcli.MgmtSvcGrpcClient
}

// NewService constructs a importService
func NewService(s service.OperationsService, backupModel backup.Model, mgmtCli pkgrpcli.MgmtSvcGrpcClient) dataflow.DPFeatureServiceServer {
	return &dpFeatureService{OperationsService: s, backupModel: backupModel, mgmtCli: mgmtCli}
}

func (s *dpFeatureService) GetEnabledDPFeaturesForCluster(ctx context.Context, req *dataflow.EnabledDPFeaturesForClusterReq) (_ *dataflow.EnabledDPFeaturesForClusterResp, err error) {
	ctx, span := Tracer.Start(ctx, "dpFeatureService.GetEnabledDPFeaturesForCluster")
	defer func() { tracing.EndSpan(span, err) }()
	logger := log.WithContextL(ctx)
	logger.Sugar().Debugf("start")

	orgID := entity.ID(req.OrgId)
	projectID := entity.ID(req.ProjectId)

	user := s.CurrentUser(ctx)
	if err = dbutil.CheckProjectID(user.Projects, projectID); err != nil {
		return nil, err
	}

	if req.ClusterId == 0 {
		logger.Info("cluster id is 0, just support org/project feature check")
		features := featureSwitcher.GetEnabledFeatures(req.OrgId, req.ProjectId, 0, "", "")
		return &dataflow.EnabledDPFeaturesForClusterResp{
			Features: features,
		}, nil
	}

	cluster, err := s.mgmtCli.GetClusterBasicByID(ctx, req.ClusterId)
	if err != nil {
		logger.Warn("failed to get cluster", zap.Error(err))
		return nil, errcode.NewInvalidInputErr(errors.New("Failed to get cluster"))
	}
	err = dbutil.CheckResourceOrgIDAndProjectID(orgID, entity.ID(cluster.OrganizationId), projectID, entity.ID(cluster.ProjectId))
	if err != nil {
		return nil, err
	}

	features := featureSwitcher.GetEnabledFeaturesByCluster(cluster)

	// biz filter
	features, err = s.bizFeaturesFilter(ctx, features, req)
	if err != nil {
		return nil, err
	}

	resp := &dataflow.EnabledDPFeaturesForClusterResp{
		Features: features,
	}
	return resp, nil
}

func (s *dpFeatureService) bizFeaturesFilter(ctx context.Context, features []string, req *dataflow.EnabledDPFeaturesForClusterReq) ([]string, error) {
	var ans []string
	for _, feature := range features {
		if feature == featureSwitcher.PiTR {
			bgContext, err := s.backupModel.BgContext(entity.ID(req.OrgId))
			if err != nil {
				return nil, err
			}
			// check cluster can enable log backup
			ok, err := bgContext.CanClusterEnableLogBackup(ctx, req.OrgId, req.ProjectId, req.ClusterId)
			if err != nil {
				return nil, err
			}
			if ok {
				ans = append(ans, feature)
			}
			continue
		}
		ans = append(ans, feature)
	}
	return ans, nil
}
