package premium_dm

import (
	"context"
	"fmt"
	"strconv"
	"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"
)

// PremiumDMOperation identifies the user operation represented by an event.
// It is intentionally not persisted: asynchronous workers publish only when
// an existing state transition provides a reliable outcome.
type PremiumDMOperation string

const (
	PremiumDMOperationCreate PremiumDMOperation = "create"
	PremiumDMOperationDelete PremiumDMOperation = "delete"
	PremiumDMOperationPause  PremiumDMOperation = "pause"
	PremiumDMOperationResume PremiumDMOperation = "resume"
)

// PublishPremiumDMEvent publishes a DisplayEvent for an explicit operation
// result. Internal task stages must not be passed as display statuses.
// Failures are logged and never returned to callers (best-effort delivery).
func PublishPremiumDMEvent(
	ctx context.Context,
	publisher center.EventCenterPublisher,
	task *entity.PremiumDMTask,
	operation PremiumDMOperation,
	displayStatus event.EventStatus,
) {
	if publisher == nil || task == nil {
		return
	}
	ev, err := convertPremiumDMToDisplayEvent(ctx, task, operation, displayStatus)
	if err != nil {
		log.GetLogger(ctx).Error("failed to build premium dm display event",
			zap.String("taskID", task.ID), zap.Error(err))
		return
	}
	publisher.PublishAsync(ev)
}

func convertPremiumDMToDisplayEvent(
	ctx context.Context,
	task *entity.PremiumDMTask,
	operation PremiumDMOperation,
	displayStatus event.EventStatus,
) (*event.DisplayEvent, error) {
	clusterID, err := strconv.ParseUint(task.TiDBID, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("parse tidb_id %q: %w", task.TiDBID, err)
	}
	tenantID, err := strconv.ParseUint(task.TenantID, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("parse tenant_id %q: %w", task.TenantID, err)
	}
	projectID, err := strconv.ParseUint(task.ProjectID, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("parse project_id %q: %w", task.ProjectID, err)
	}

	displayEventType, err := premiumDMOperationEventType(operation)
	if err != nil {
		return nil, err
	}
	msgStatus, summary := premiumDMOperationPresentation(task, operation, displayStatus)
	msg := &center.PremiumDMMsg{
		BaseMsg: center.BaseMsg{
			Name:         task.Name,
			Status:       msgStatus,
			HappenedAtMs: uint64(time.Now().UnixMilli()),
		},
		IDMsg: center.IDMsg{
			AuthorID:  task.CreatorID,
			TenantID:  tenantID,
			ProjectID: projectID,
			ClusterID: clusterID,
		},
		MigrationId:   task.ID,
		Mode:          string(task.Mode),
		ImportMode:    string(task.ImportMode),
		Stage:         string(task.Stage),
		OriginalError: task.ErrMsg,
	}

	data := center.BuildEventBasicInfosWithContextID(task.ID, msg.HappenedAtMs, msg.IDMsg)
	data.Author = task.Creator
	data.DisplayEventType = string(displayEventType)
	data.DisplayStatus = displayStatus
	data.Detail = msg
	data.Summary = summary
	return data, nil
}

func premiumDMOperationEventType(operation PremiumDMOperation) (entity.DataflowEventType, error) {
	switch operation {
	case PremiumDMOperationCreate:
		return entity.CreateDataMigration, nil
	case PremiumDMOperationDelete:
		return entity.DeleteDataMigration, nil
	case PremiumDMOperationPause:
		return entity.PauseDataMigration, nil
	case PremiumDMOperationResume:
		return entity.ResumeDataMigration, nil
	default:
		return "", fmt.Errorf("unsupported premium dm operation %q", operation)
	}
}

func premiumDMOperationPresentation(
	task *entity.PremiumDMTask,
	operation PremiumDMOperation,
	displayStatus event.EventStatus,
) (center.MsgStatusType, string) {
	msgStatus := center.FailedStatus
	switch displayStatus {
	case event.EventStatusStarted:
		msgStatus = center.PendingStatus
	case event.EventStatusRunning:
		msgStatus = center.RunningStatus
	case event.EventStatusSucceeded:
		msgStatus = center.SuccessStatus
	case event.EventStatusCanceled:
		msgStatus = center.TerminatedStatus
	}

	switch operation {
	case PremiumDMOperationCreate:
		if displayStatus == event.EventStatusStarted {
			return msgStatus, fmt.Sprintf(
				"The dm task creation started. Task ID: %s, Mode: %s, Import Mode: %s.",
				task.ID, task.Mode, task.ImportMode)
		}
		if displayStatus == event.EventStatusRunning {
			return msgStatus, fmt.Sprintf(
				"The dm task is running. Task ID: %s, Mode: %s, Import Mode: %s.",
				task.ID, task.Mode, task.ImportMode)
		}
		return msgStatus, fmt.Sprintf("The dm task failed. Task ID: %s, Mode: %s, Import Mode: %s.",
			task.ID, task.Mode, task.ImportMode)
	case PremiumDMOperationDelete:
		if displayStatus == event.EventStatusStarted {
			return msgStatus, fmt.Sprintf("The dm task deletion started. Task ID: %s.", task.ID)
		}
		if displayStatus == event.EventStatusSucceeded {
			return msgStatus, fmt.Sprintf("The dm task was deleted. Task ID: %s.", task.ID)
		}
		return msgStatus, fmt.Sprintf("The dm task deletion failed. Task ID: %s.", task.ID)
	case PremiumDMOperationPause:
		if displayStatus == event.EventStatusStarted {
			return msgStatus, fmt.Sprintf("The dm task pause started. Task ID: %s.", task.ID)
		}
		if displayStatus == event.EventStatusSucceeded {
			return msgStatus, fmt.Sprintf("The dm task paused. Task ID: %s.", task.ID)
		}
		if displayStatus == event.EventStatusCanceled {
			return msgStatus, fmt.Sprintf("The dm task pause was canceled. Task ID: %s.", task.ID)
		}
		return msgStatus, fmt.Sprintf("The dm task pause failed. Task ID: %s.", task.ID)
	case PremiumDMOperationResume:
		if displayStatus == event.EventStatusStarted {
			return msgStatus, fmt.Sprintf("The dm task resume started. Task ID: %s.", task.ID)
		}
		if displayStatus == event.EventStatusSucceeded {
			return msgStatus, fmt.Sprintf("The dm task resumed. Task ID: %s.", task.ID)
		}
		if displayStatus == event.EventStatusCanceled {
			return msgStatus, fmt.Sprintf("The dm task resume was canceled. Task ID: %s.", task.ID)
		}
		return msgStatus, fmt.Sprintf("The dm task resume failed. Task ID: %s.", task.ID)
	default:
		return msgStatus, fmt.Sprintf("The dm task status was updated. Task ID: %s.", task.ID)
	}
}
