package impl

import (
	"context"
	"fmt"
	"time"

	"github.com/tidbcloud/dataflow-service-ng/app/event_bus/publisher/center"
	"github.com/tidbcloud/dataflow-service-ng/app/repo/entity"
	"github.com/tidbcloud/dataflow-service-ng/pkg/log"
	"github.com/tidbcloud/eventbus-datastruct/event"
	"go.uber.org/zap"
)

// PublishPrivateEndpointEvent publishes a DisplayEvent for Create/Delete Private Endpoint.
func PublishPrivateEndpointEvent(
	ctx context.Context,
	publisher center.EventCenterPublisher,
	endpoint *entity.BizSinkEndpoint,
	eventType entity.DataflowEventType,
) {
	if publisher == nil || endpoint == nil {
		return
	}
	ev := convertPrivateEndpointToDisplayEvent(endpoint, eventType)
	if ev == nil {
		return
	}
	publisher.PublishAsync(ev)
	log.GetLogger(ctx).Info("published private endpoint display event",
		zap.Uint64("endpointID", endpoint.ID.Uint64()),
		zap.String("eventType", string(eventType)),
		zap.String("status", string(endpoint.Status)),
		zap.String("displayStatus", string(ev.DisplayStatus)))
}

func convertPrivateEndpointToDisplayEvent(
	endpoint *entity.BizSinkEndpoint,
	eventType entity.DataflowEventType,
) *event.DisplayEvent {
	msgStatus, displayStatus, summary := privateEndpointStatusPresentation(endpoint, eventType)
	if summary == "" {
		return nil
	}
	msg := &center.PrivateEndpointMsg{
		BaseMsg: center.BaseMsg{
			Name:         endpoint.Name,
			Status:       msgStatus,
			HappenedAtMs: uint64(time.Now().UnixMilli()),
		},
		IDMsg: center.IDMsg{
			AuthorID:  endpoint.EventAuthorID,
			TenantID:  endpoint.TenantID.Uint64(),
			ProjectID: endpoint.EventProjectID.Uint64(),
			ClusterID: endpoint.EventClusterID.Uint64(),
		},
		EndpointId:    endpoint.ID.Uint64(),
		SharedPoolID:  endpoint.SharedPoolID,
		ServiceName:   endpoint.ServiceName,
		EndpointType:  string(endpoint.EndpointType),
		OriginalError: endpoint.ErrMsg,
	}
	data := center.BuildEventBasicInfos(msg.EndpointId, msg.HappenedAtMs, msg.IDMsg)
	data.Author = endpoint.EventAuthor
	data.DisplayEventType = string(eventType)
	data.DisplayStatus = displayStatus
	data.Detail = msg
	data.Summary = summary
	return data
}

func privateEndpointStatusPresentation(
	endpoint *entity.BizSinkEndpoint,
	eventType entity.DataflowEventType,
) (center.MsgStatusType, event.EventStatus, string) {
	switch eventType {
	case entity.CreatePrivateEndpointForExternalServices:
		switch endpoint.Status {
		case entity.BizPLEndpointStatusCreating, entity.BizPLEndpointStatusPendingAcceptance:
			return center.CreatingStatus, event.EventStatusStarted, fmt.Sprintf(
				"Start to create private endpoint for external services. Name: %s.", endpoint.Name)
		case entity.BizPLEndpointStatusAvailable:
			return center.SuccessStatus, event.EventStatusSucceeded, fmt.Sprintf(
				"Private endpoint for external services created successfully. Name: %s.", endpoint.Name)
		case entity.BizPLEndpointStatusFailed, entity.BizPLEndpointStatusRejected:
			return center.FailedStatus, event.EventStatusFailed, fmt.Sprintf(
				"Failed to create private endpoint for external services. Name: %s.", endpoint.Name)
		}
	case entity.DeletePrivateEndpointForExternalServices:
		switch endpoint.Status {
		case entity.BizPLEndpointStatusDeleting:
			return center.DeletingStatus, event.EventStatusStarted, fmt.Sprintf(
				"Start to delete private endpoint for external services. Name: %s.", endpoint.Name)
		case entity.BizPLEndpointStatusDeleted:
			return center.DeletedStatus, event.EventStatusSucceeded, fmt.Sprintf(
				"Private endpoint for external services deleted successfully. Name: %s.", endpoint.Name)
		}
	}
	return "", "", ""
}

func shouldEmitCreatePrivateEndpointStatus(status entity.BizSinkEndpointStatus) bool {
	switch status {
	case entity.BizPLEndpointStatusAvailable,
		entity.BizPLEndpointStatusFailed,
		entity.BizPLEndpointStatusRejected:
		return true
	default:
		return false
	}
}
