package instance

import (
	"fmt"
	"time"

	apprent "github.com/tidbcloud/devops/entity/approval"
	"github.com/tidbcloud/devops/entity/event"
	opent "github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/pkg/infra/errs"
)

type actorWhenApproving struct {
	*baseActor
}

func (a *actorWhenApproving) ContinueIfApproved() error {
	ctx := a.Context
	ins := a.Instance

	if ins.ApprovalID == nil {
		return fmt.Errorf("phase is approving but approval id is nil")
	}

	approval, err := a.ApprovalService.GetApproval(ctx, *ins.ApprovalID)
	if err != nil {
		return fmt.Errorf("get approval '%d' failed: %s", *ins.ApprovalID, err)
	}

	// record event detail — build approval action timeline
	finishDetail := &event.OperationEventFinishApproval{
		Result: approval.Status,
	}
	for _, level := range approval.Levels {
		for _, node := range level.Nodes {
			for _, task := range node.Tasks {
				if task.EndAt != nil && (task.Status == apprent.ApprovalTaskStatusApproved || task.Status == apprent.ApprovalTaskStatusRejected) {
					finishDetail.Tasks = append(finishDetail.Tasks, event.OperationEventApprovalTask{
						User: task.User, Status: task.Status, At: time.Unix(*task.EndAt, 0).UTC().Format(time.RFC3339),
					})
				}
			}
			for _, task := range node.BackupTasks {
				if task.EndAt != nil && (task.Status == apprent.ApprovalTaskStatusApproved || task.Status == apprent.ApprovalTaskStatusRejected) {
					finishDetail.Tasks = append(finishDetail.Tasks, event.OperationEventApprovalTask{
						User: task.User, Status: task.Status, At: time.Unix(*task.EndAt, 0).UTC().Format(time.RFC3339),
					})
				}
			}
		}
	}
	for _, task := range approval.AdminTasks {
		if task.EndAt != nil && (task.Status == apprent.ApprovalTaskStatusApproved || task.Status == apprent.ApprovalTaskStatusRejected) {
			finishDetail.Tasks = append(finishDetail.Tasks, event.OperationEventApprovalTask{
				User: task.User, Status: task.Status, At: time.Unix(*task.EndAt, 0).UTC().Format(time.RFC3339),
			})
		}
	}
	a.EventDetail.FinshApproval = finishDetail

	if approval.Status == apprent.ApprovalStatusRejected {
		err := a.PatchInstance(ctx, func(current *opent.Instance) error {
			current.Phase = opent.InstancePhaseWithdrawn
			return nil
		})
		return err
	}

	if approval.Status == apprent.ApprovalStatusApproved {
		// transition status to 'Pending'
		err := a.PatchInstance(ctx, func(current *opent.Instance) error {
			current.Phase = opent.InstancePhasePending
			return nil
		})

		return err
	}

	return errs.RetriableErrorf("approval '%d' is %s", *ins.ApprovalID, approval.Status)
}

func (a *actorWhenApproving) Cancel() error {
	err := a.cancel()
	if err != nil {
		return err
	}

	if a.Instance.ApprovalID != nil {
		appr, err := a.ApprovalService.GetApproval(a.Context, *a.Instance.ApprovalID)
		if err != nil {
			return fmt.Errorf("get approval %d failed: %s", *a.Instance.ApprovalID, err)
		}

		err = a.cancelApproval(appr)
		if err != nil {
			a.Logger.Info("cancel a approval failed", "error", err.Error())
		}
	}

	return nil
}
