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

import (
	"context"
	"dataservices/commons/errs"
	comerrs "dataservices/commons/errs"
	"dataservices/commons/eventbus"
	"dataservices/ddb"
	dto "dataservices/internal/dto/dataapi"
	"dataservices/internal/repository"
	"dataservices/internal/repository_ddb"

	"go.uber.org/zap"
)

func init() {
	registerHandler(
		eventbus.HandlerConfig{
			EventType:  dto.BusEventEndpoint,
			DataStruct: dto.EndpointEvent{},
			HandleFunc: handleEndpointEvent,
		},
	)
}

func handleEndpointEvent(ctx context.Context, logger *zap.Logger, event *eventbus.Event) error {
	logger.Info("begin deal endpoint event")

	endpointEvent, ok := event.Data.(*dto.EndpointEvent)
	if !ok || endpointEvent == nil || (endpointEvent.EndpointID == 0 && len(endpointEvent.EndpointIDs) == 0) {
		logger.Error("got invalid endpoint event", zap.Any("event", event))
		return comerrs.ErrInvalidEvent
	}

	switch endpointEvent.ActionType {
	case dto.EndpointActionSave:
		return handleEndpointSaveEvent(ctx, logger, endpointEvent)
	case dto.EndpointActionDelete:
		return handleEndpointDeleteEvent(ctx, logger, endpointEvent)
	case dto.EndpointActionDeploy:
		return handleEndpointDeployEvent(ctx, logger, endpointEvent)
	case dto.EndpointActionUndeploy:
		return handleEndpointUndeployEvent(ctx, logger, endpointEvent)
	default:
		logger.Error("dataapp event got unsupported action", zap.String("action_type", endpointEvent.ActionType))
		return comerrs.ErrInvalidEvent
	}
}

func handleEndpointSaveEvent(ctx context.Context, logger *zap.Logger, endpointEvent *dto.EndpointEvent) error {

	endpointIDs := endpointEvent.EndpointIDs
	if endpointEvent.EndpointID > 0 {
		endpointIDs = append(endpointIDs, endpointEvent.EndpointID)
	}

	ddbClient := ddb.Client()
	var failedEndpointIDs []uint64
	for _, endpointID := range endpointIDs {
		endpointInfo, err := repository.GetEndpoint(endpointID)
		if err != nil {
			logger.Error("GetEndpoint failed", zap.Uint64("endpoint_id", endpointID), zap.Error(err))
			failedEndpointIDs = append(failedEndpointIDs, endpointID)
			continue
			// return err
		}
		if endpointInfo.Endpoint == "" {
			logger.Warn("endpoint path is empty, nothing to do")
			continue
			// return nil
		}

		if err := repository_ddb.PutEndpoint(ctx, ddbClient, endpointInfo); err != nil {
			logger.Error("PutEndpoint failed", zap.Uint64("endpoint_id", endpointID), zap.Error(err))
			failedEndpointIDs = append(failedEndpointIDs, endpointID)
			continue
			// return err
		}

		if endpointInfo.IsChat2QueryEndpoint() {
			endpointPublishInfo, err := repository.GetPublishedEndpoint(endpointID)
			if err != nil {
				logger.Error("GetPublishedEndpoint failed", zap.Uint64("endpoint_id", endpointID), zap.Error(err))
				failedEndpointIDs = append(failedEndpointIDs, endpointID)
				continue
				// return err
			}

			if err := repository_ddb.PutPublishedEndpoint(ctx, ddbClient, endpointPublishInfo); err != nil {
				logger.Error("PutPublishedEndpoint failed", zap.Uint64("endpoint_id", endpointID), zap.Error(err))
				failedEndpointIDs = append(failedEndpointIDs, endpointID)
				continue
				// return err
			}
		}
	}
	if len(failedEndpointIDs) > 0 {
		if len(failedEndpointIDs) == len(endpointIDs) {
			logger.Error("all endpoint handle failed", zap.Uint64s("failed_endpoint_ids", failedEndpointIDs))
			return errs.NewInternalGeneralBusiness("all endpoint handle failed", nil)
		}
		logger.Error("some endpoint handle failed", zap.Uint64s("failed_endpoint_ids", failedEndpointIDs), zap.Uint64s("all_endpoint_ids", endpointIDs))
	}
	return nil
}

func handleEndpointDeleteEvent(ctx context.Context, logger *zap.Logger, endpointEvent *dto.EndpointEvent) error {
	endpointIDs := endpointEvent.EndpointIDs
	if endpointEvent.EndpointID > 0 {
		endpointIDs = append(endpointIDs, endpointEvent.EndpointID)
	}
	ddbClient := ddb.Client()
	n, err := repository_ddb.BatchDeleteEndpoint(ctx, ddbClient, endpointIDs)
	if err != nil {
		return err
	} else if len(endpointIDs) != n {
		logger.Warn("batch delete endpoint count not match input count", zap.Uint64s("input_endpoints", endpointIDs), zap.Int("delete_count", n))
	}
	return nil
}

func handleEndpointDeployEvent(ctx context.Context, logger *zap.Logger, endpointEvent *dto.EndpointEvent) error {
	var (
		err        error
		endpointID = endpointEvent.EndpointID
	)

	endpointInfo, err := repository.GetPublishedEndpoint(endpointID)
	if err != nil {
		return err
	}

	ddbClient := ddb.Client()
	return repository_ddb.PutPublishedEndpoint(ctx, ddbClient, endpointInfo)
}

func handleEndpointUndeployEvent(ctx context.Context, logger *zap.Logger, endpointEvent *dto.EndpointEvent) error {
	endpointID := endpointEvent.EndpointID
	ddbClient := ddb.Client()
	return repository_ddb.DeletePublishedEndpoint(ctx, ddbClient, endpointID)
}
