package clustermaintenance

import (
	"context"
	"fmt"

	maintent "github.com/tidbcloud/devops/entity/maintenance"

	"github.com/tidbcloud/devops/entity/event"
	mgmtproto "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

func (s *svc) StartMaintenanceExecution(ctx context.Context, id uint64, execID uint64) error {
	// query from repo
	taskInRepo, err := s.repo.Maintenance.Get(ctx, id)
	if err != nil {
		return err
	}

	// update status in mgmt
	req := mgmtproto.MaintenanceTaskExecution{
		MaintenanceTaskExecutionId: execID,
		State:                      mgmtproto.MaintenanceTaskExecution_RUNNING,
	}
	_, err = s.coreSvcCli.Mgmt.Maintenance.UpdateMaintenanceTaskExecution(ctx, &mgmtproto.UpdateMaintenanceTaskExecutionRequest{MaintenanceTaskExecution: &req})
	if err != nil {
		return err
	}

	// publish event
	ev := assembleEvent(taskInRepo.ID, taskInRepo.InstanceID, actionOperator(ctx), event.ClusterMaintenanceEventTypeExecutionStart)
	ev.Details.StartExecution = &event.ClusterMaintenanceEventExecution{
		ExecutionID: execID,
	}
	err = s.eventBus.ClusterMaintenanceEvent.Publish(ev)
	if err != nil {
		return fmt.Errorf("publish event failed: %w", err)
	}

	return nil
}

func (s *svc) FinishMaintenanceExecution(ctx context.Context, id uint64, execID uint64) error {
	// query from repo
	taskInRepo, err := s.repo.Maintenance.Get(ctx, id)
	if err != nil {
		return err
	}

	// update status in mgmt
	req := &mgmtproto.UpdateMaintenanceTaskExecutionRequest{
		MaintenanceTaskExecution: &mgmtproto.MaintenanceTaskExecution{
			MaintenanceTaskExecutionId: execID,
			State:                      mgmtproto.MaintenanceTaskExecution_DONE,
		},
	}
	_, err = s.coreSvcCli.Mgmt.Maintenance.UpdateMaintenanceTaskExecution(ctx, req)
	if err != nil {
		return err
	}

	// publish event
	ev := assembleEvent(taskInRepo.ID, taskInRepo.InstanceID, actionOperator(ctx), event.ClusterMaintenanceEventTypeExecutionFinish)
	ev.Details.FinishExecution = &event.ClusterMaintenanceEventExecution{
		ExecutionID: execID,
	}
	err = s.eventBus.ClusterMaintenanceEvent.Publish(ev)
	if err != nil {
		return fmt.Errorf("publish event failed: %w", err)
	}

	return nil
}

func (s *svc) CancelMaintenanceExecution(ctx context.Context, id uint64, execID uint64) error {
	// query from repo
	taskInRepo, err := s.repo.Maintenance.Get(ctx, id)
	if err != nil {
		return err
	}

	// update status in mgmt
	req := &mgmtproto.UpdateMaintenanceTaskExecutionRequest{
		MaintenanceTaskExecution: &mgmtproto.MaintenanceTaskExecution{
			MaintenanceTaskExecutionId: execID,
			State:                      mgmtproto.MaintenanceTaskExecution_CANCELED,
		},
	}
	_, err = s.coreSvcCli.Mgmt.Maintenance.UpdateMaintenanceTaskExecution(ctx, req)
	if err != nil {
		return err
	}

	// publish event
	ev := assembleEvent(taskInRepo.ID, taskInRepo.InstanceID, actionOperator(ctx), event.ClusterMaintenanceEventTypeExecutionCancel)
	ev.Details.CancelExecution = &event.ClusterMaintenanceEventExecution{
		ExecutionID: execID,
	}
	err = s.eventBus.ClusterMaintenanceEvent.Publish(ev)
	if err != nil {
		return fmt.Errorf("publish event failed: %w", err)
	}

	return nil
}

func cvtMaintenanceExecutionToEntity(exec *mgmtproto.MaintenanceTaskExecution) maintent.ClusterMaintenanceExecution {
	e := maintent.ClusterMaintenanceExecution{
		ID:           exec.MaintenanceTaskExecutionId,
		TaskID:       exec.MaintenanceTaskId,
		ClusterID:    exec.ClusterId,
		Status:       ExecutionStatusToEntity(exec.State),
		CancelReason: exec.CancellationReason.String(),
	}

	return e
}

func ExecutionStatusToCS(status maintent.ClusterMaintenanceExecutionStatus) mgmtproto.MaintenanceTaskExecution_State {
	if s, exist := ExecutionStatusMap[status]; exist {
		return s
	}
	return -1
}

func ExecutionStatusToEntity(s mgmtproto.MaintenanceTaskExecution_State) maintent.ClusterMaintenanceExecutionStatus {
	for k, v := range ExecutionStatusMap {
		if v == s {
			return k
		}
	}

	return "Unknown"
}
