// Package service add by blacknc <blacknc@163.com> at 2023/04/06 15:38:19
package service

import (
	"context"
	comerrs "dataservices/commons/errs"
	"dataservices/commons/eventbus"
	"dataservices/commons/grpc"
	"dataservices/commons/utils"
	"dataservices/ddb"
	dto "dataservices/internal/dto/dataapi"
	"dataservices/internal/entity"
	"dataservices/internal/repository_ddb"

	"github.com/tidbcloud/core-svc/idl/pbgen/proto/core"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

func init() {
	registerHandler(
		eventbus.HandlerConfig{
			EventType:  dto.BusEventLinkCluster,
			DataStruct: dto.DataAppBindClusterEvent{},
			HandleFunc: handleLinkClusterEvent,
		},
	)
}

func handleLinkClusterEvent(ctx context.Context, logger *zap.Logger, event *eventbus.Event) (err error) {
	logger.Info("handleLinkClusterEvent begin...")
	defer func() {
		logger.Info("handleLinkClusterEvent end", zap.Error(err))
	}()

	clusterEvent, ok := event.Data.(*dto.DataAppBindClusterEvent)
	if !ok || clusterEvent == nil {
		logger.Error("got invalid event data", zap.Any("event", event))
		return comerrs.ErrInvalidEvent
	} else if len(clusterEvent.ClusterIDs) == 0 {
		logger.Info("cluster_ids is empty, nothing to do")
		return nil
	}

	switch clusterEvent.EventType {
	case dto.DataAppBindClusterActionAddCluster:
		err = handleLinkClusterCreateEvent(ctx, logger, clusterEvent)
	case dto.DataAppBindClusterActionDelCluster:
		err = handleLinkClusterDeleteEvent(ctx, logger, clusterEvent)
	default:
		logger.Error("link cluster event got unsupported action", zap.String("event_type", string(clusterEvent.EventType)))
	}
	return err
}

func handleLinkClusterCreateEvent(ctx context.Context, logger *zap.Logger, clusterEvent *dto.DataAppBindClusterEvent) error {

	dataappID := clusterEvent.DataAppID
	clusterIDs := clusterEvent.ClusterIDs
	logger.Info("begin deal link cluster", zap.Int("count", len(clusterIDs)))

	// list cluster info from cm
	clusterList, berr := grpc.ListClusters(ctx, &core.ListClustersReq{
		Filter: &core.ClusterFilter{
			ClusterIds: clusterIDs,
		},
	})
	if berr != nil {
		return berr
	} else if len(clusterList.Clusters) == 0 {
		logger.Error("ListClusters got empty result, nothing to do")
		return nil
	}

	var (
		ddbClient        = ddb.Client()
		linkClusterCount int
	)
	for _, clusterID := range clusterIDs {

		// write link cluster to ddb
		if err := repository_ddb.PutAppCluster(ctx, ddbClient, &entity.AppCluster{
			DataappID: dataappID,
			ClusterID: clusterID,
			CreatedAt: utils.Now(),
		}); err != nil {
			logger.Error("PutAppCluster failed", zap.Uint64("cluster_id", clusterID), zap.Error(err))
			continue
		}
		linkClusterCount++
	}
	logger.Info("end deal link cluster",
		zap.Int("total_count", len(clusterIDs)),
		zap.Int("link_cluster_succ_count", linkClusterCount))
	return nil
}

func handleLinkClusterDeleteEvent(ctx context.Context, logger *zap.Logger, clusterEvent *dto.DataAppBindClusterEvent) error {

	dataappID := clusterEvent.DataAppID
	clusterIDs := clusterEvent.ClusterIDs
	logger.Info("begin deal delete link cluster", zap.Int("count", len(clusterIDs)))

	ddbClient := ddb.Client()

	written, err := repository_ddb.BatchDeleteAppCluster(ctx, ddbClient, dataappID, clusterIDs)
	if err != nil {
		return err
	}
	if written != len(clusterIDs) {
		tilog.Warn(ctx, "BatchDeleteAppCluster partial success", zap.Int("total", len(clusterIDs)), zap.Int("written", written))
	}
	return nil
}
