// Package service add by blacknc <blacknc@163.com> at 2023/04/06 14:24:49
package service

import (
	"context"
	"dataservices/commons/config"
	"dataservices/commons/eventbus"
	"dataservices/commons/metrics"
	"dataservices/commons/utils"
	dto "dataservices/internal/dto/dataapi"
	"dataservices/internal/repository"
	"time"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

func publishDataappDeployEvent(ctx context.Context, event dto.DataappDeployEvent, operator string) error {
	// notify eventbus
	event.BaseEvent = dto.BaseEvent{
		Operator: operator,
		Optime:   time.Now().UnixMilli(),
	}
	eventInfo := &eventbus.Event{
		ID:   utils.RandNumStr(32),
		Type: dto.BusEventDataappDeploy,
		Data: event,
	}
	return publishEvent(ctx, eventInfo, "")
}

func publishDataappEvent(ctx context.Context, appID uint64, operator string, actionType string) error {
	// notify eventbus
	eventInfo := &eventbus.Event{
		ID:   utils.RandNumStr(32),
		Type: dto.BusEventDataapp,
		Data: dto.DataappEvent{
			BaseEvent: dto.BaseEvent{
				Operator:   operator,
				Optime:     time.Now().UnixMilli(),
				ActionType: actionType,
			},
			DataappID: appID,
		},
	}
	return publishEvent(ctx, eventInfo, actionType)
}

func PublishEndpointActionEvent(ctx context.Context, endpointID uint64, operator string, actionType string) error {
	return publishEndpointEvent(ctx, endpointID, operator, actionType)
}

func publishMultiEndpointEvent(ctx context.Context, endpointIDs []uint64, operator string, actionType string) error {
	// notify eventbus
	eventInfo := &eventbus.Event{
		ID:   utils.RandNumStr(32),
		Type: dto.BusEventEndpoint,
		Data: dto.EndpointEvent{
			BaseEvent: dto.BaseEvent{
				Operator:   operator,
				Optime:     time.Now().UnixMilli(),
				ActionType: actionType,
			},
			EndpointIDs: endpointIDs,
		},
	}
	return publishEvent(ctx, eventInfo, actionType)
}

func publishEndpointEvent(ctx context.Context, endpointID uint64, operator string, actionType string) error {
	// notify eventbus
	eventInfo := &eventbus.Event{
		ID:   utils.RandNumStr(32),
		Type: dto.BusEventEndpoint,
		Data: dto.EndpointEvent{
			BaseEvent: dto.BaseEvent{
				Operator:   operator,
				Optime:     time.Now().UnixMilli(),
				ActionType: actionType,
			},
			EndpointID: endpointID,
		},
	}
	return publishEvent(ctx, eventInfo, actionType)
}

func publishAppClusterEvent(ctx context.Context, appID uint64, clusterIDs []uint64, createdBy string, eventType dto.DataAppBindClusterAction) (err error) {

	// notify eventbus
	eventInfo := &eventbus.Event{
		ID:   utils.RandNumStr(32),
		Type: dto.BusEventLinkCluster,
		Data: dto.DataAppBindClusterEvent{
			EventType:  eventType,
			DataAppID:  appID,
			ClusterIDs: clusterIDs,
			Operator:   createdBy,
			Optime:     time.Now().UnixMilli(),
		},
	}
	return publishEvent(ctx, eventInfo, string(eventType))
}

func publishApiKeyEvent(ctx context.Context, appID, apikeyID uint64, createdBy string, eventType dto.DataAppBindClusterAction) (err error) {

	var eventInfo *eventbus.Event
	defer func() {
		if eventInfo == nil {
			recordFailedCounterMetric(metrics.EventFailedCounter, err, dto.BusEventLinkCluster, string(eventType))
		}
		if err != nil {
			tilog.Error(ctx, "publichApiKeyEvent failed",
				zap.Uint64("app_id", appID),
				zap.Uint64("apikey_id", apikeyID),
				zap.String("user", createdBy),
				zap.String("event_type", string(eventType)),
				zap.Error(err),
			)
		} else {
			tilog.Info(ctx, "publichApiKeyEvent succeed",
				zap.Uint64("app_id", appID),
				zap.Uint64("apikey_id", apikeyID),
				zap.String("user", createdBy),
				zap.String("event_type", string(eventType)),
			)
		}
	}()

	var clusterIDs []uint64

	// list link cluster
	clusterList, err := repository.ListAppCluster(ctx, dto.ListAppClusterReq{DataappID: appID})
	if err != nil {
		return err
	}
	if len(clusterList) == 0 {
		return nil
	}
	for _, cluster := range clusterList {
		clusterIDs = append(clusterIDs, cluster.ClusterID)
	}

	// notify eventbus
	eventInfo = &eventbus.Event{
		ID:   utils.RandNumStr(32),
		Type: dto.BusEventLinkCluster,
		Data: dto.DataAppBindClusterEvent{
			EventType:  eventType,
			DataAppID:  appID,
			ApiKeyID:   apikeyID,
			ClusterIDs: clusterIDs,
			Operator:   createdBy,
			Optime:     time.Now().UnixMilli(),
		},
	}
	return publishEvent(ctx, eventInfo, string(eventType))
}

func publishEvent(ctx context.Context, eventInfo *eventbus.Event, actionType string) (err error) {
	defer func() {
		recordFailedCounterMetric(metrics.EventFailedCounter, err, eventInfo.Type, actionType)
		if err != nil {
			tilog.Error(ctx, "publichEvent failed",
				zap.String("action_type", actionType),
				zap.Any("event_info", eventInfo),
				zap.Error(err),
			)
		} else {
			tilog.Info(ctx, "publichEvent succeed",
				zap.String("action_type", actionType),
				zap.Any("event_info", eventInfo),
			)
		}
	}()

	if config.IsLocalMode() {
		return nil
	}

	if db := repository.FromContext(ctx); db != nil {
		return eventbus.Get().PublishEventWithTX(ctx, eventInfo, db)
	} else {
		return eventbus.Get().PublishEvent(ctx, eventInfo)
	}
}
