package action

import (
	"errors"
	"fmt"

	relent "github.com/tidbcloud/devops/entity/release"
	"github.com/tidbcloud/devops/pkg/infra/errs"
	relsvcapi "github.com/tidbcloud/devops/pkg/service/release/api"
)

type actionWhenCreated struct {
	*actionBase
}

func (a *actionWhenCreated) SendApprovalIfReady() error {
	ctx := a.Context
	change := a.Change

	rparam := relsvcapi.GetReleaseParam{WithTickets: true}
	release, err := a.ReleaseService.GetRelease(a.Context, change.ReleaseID, rparam)
	if err != nil {
		return err
	}

	// wait for all tickets ready
	tickets, err := CheckChangeItems(ctx, release, change.ChangeItemNames)
	if err != nil {
		return err
	}

	unreadyTickets := make([]string, 0)
	for _, ticket := range tickets {
		switch ticket.Status {
		case relent.ReleaseTicketStatusReady,
			relent.ReleaseTicketStatusDone:
		default:
			unreadyTickets = append(unreadyTickets, ticket.Name)
		}
	}

	if len(unreadyTickets) > 0 {
		// record pending reason
		reason := fmt.Sprintf("Some related tickets %v are unready, please change them to 'Ready for release' to continue.", unreadyTickets)
		err = a.PatchChange(ctx,
			PatchChangeStatus(relent.ChangeStatusCreated, reason),
		)
		if err != nil {
			return err
		}
		return errs.RetriableErrorNew(errors.New(reason))
	}

	// send a approval
	apprID, err := a.sendApproval(tickets)
	if err != nil {
		return err
	}

	// transition status to 'Approving'
	var cancelApprID uint64
	err = a.PatchChange(ctx,
		PatchChangeStatus(relent.ChangeStatusApproving, ""),
		func(current *relent.ChangeInRepo) error {
			if current.ApprovalID != nil {
				cancelApprID = *current.ApprovalID
				current.PrevApprovalIDs = append(current.PrevApprovalIDs, *current.ApprovalID)
			}
			current.ApprovalID = apprID
			return nil
		},
	)
	if err != nil {
		return err
	}

	// cancel previous approval
	if cancelApprID != 0 {
		err := func() error {
			cancelAppr, err := a.ApprovalService.GetApproval(a.Context, cancelApprID)
			if err != nil {
				return err
			}
			err = a.cancelApproval(cancelAppr)
			if err != nil {
				return err
			}
			return nil
		}()
		if err != nil {
			a.Logger.Error(err, "cancel approval failed", "approval_id", cancelApprID)
		}
	}
	return nil
}

func (a *actionWhenCreated) Cancel() error {
	return a.actionBase.cancel()
}

func (a *actionWhenCreated) UpdateChangeBeforeDeploy(update relsvcapi.UpdateChangeParam) error {
	return a.updateBeforeApproved(update)
}
