package actors

import (
	"fmt"
	"strconv"

	entity "github.com/tidbcloud/devops/entity"
	apprent "github.com/tidbcloud/devops/entity/approval"
	apprsvcapi "github.com/tidbcloud/devops/pkg/service/approval/api"
	rmapi "github.com/tidbcloud/devops/pkg/service/remediationticket/api"
	rmhelpers "github.com/tidbcloud/devops/pkg/service/remediationticket/helpers"
	"github.com/tidbcloud/devops/pkg/service/remediationticket/phase"
)

type actorRejected struct {
	*baseActor
}

func (a *actorRejected) GetContext() *Context {
	return a.Context
}

func (a *actorRejected) SendApproval(patch rmapi.TransferTicketParam) error {
	ctx := a.Context

	// validate and update the fields that belong to current phase. a.RemediationTicket will be changed
	err := a.currentPhase.ValidateAndUpdateTicket(a.RemediationTicket, patch)
	if err != nil {
		return fmt.Errorf("validate and update failed: %w", err)
	}

	// get next phase
	nextPhase, err := a.currentPhase.Next(phase.Context{
		Context:        ctx.Context,
		TicketInstance: *a.RemediationTicket,
	})
	if err != nil {
		return fmt.Errorf("get next phase failed: %w", err)
	}

	if nextPhase.IsFinalized() {
		// Transfer to final phase
		err = a.PatchRemediationTicket(ctx,
			func(current *entity.RemediationTicket) error {
				nextPhaseName := nextPhase.PhaseName()
				current.ApprID = 0
				current.Phase = &nextPhaseName
				current.Status = entity.RemediationTicketStatusClosed
				return nil
			},
		)
		if err != nil {
			return fmt.Errorf("approval sent but transition approving status with PatchRemediationTicket() failed: %w", err)
		}
		return nil
	}

	nextReponsiblePerson, err := rmhelpers.GetResponsiblePersonEmail(ctx, &a.RTConfig.ResponsiblePersons, a.RemediationTicket, nextPhase.PhaseName())
	if err != nil {
		return fmt.Errorf("get responsible person for next phase failed: %w", err)
	}

	// create an operation
	apprID, err := a.sendApproval(a.RemediationTicket.CurrentResponsible, nextReponsiblePerson)
	if err != nil {
		return fmt.Errorf("send approval failed: %w", err)
	} else if apprID == nil {
		return fmt.Errorf("send approval failed: nil approval ID")
	}

	// TODO: encapsulate this function
	_, err = ctx.Components.RemediationTicketRepo.SaveTicketPhaseApproval(ctx, &entity.RemediationTicketPhaseApproval{
		TicketId:   a.Origin.ID,
		FromPhase:  a.currentPhase.PhaseName(),
		ToPhase:    nextPhase.PhaseName(),
		ApprovalID: *apprID,
	})

	if err != nil {
		return fmt.Errorf("create ticket phase approval failed: %w", err)
	}

	// transition status to 'Approving'
	err = a.PatchRemediationTicket(ctx,
		func(current *entity.RemediationTicket) error {
			*current = *a.RemediationTicket
			nextPhaseName := nextPhase.PhaseName()
			current.Phase = &nextPhaseName
			current.ApprID = *apprID
			current.Status = entity.RemediationTicketStatusApproving
			return nil
		},
	)
	if err != nil {
		return fmt.Errorf("approval sent but transition approving status with PatchRemediationTicket() failed: %w", err)
	}

	return nil
}

func (a *actorRejected) ContinueIfApproved() error {
	return a.defaultErr
}

func (a *actorRejected) Cancel() error {
	// transition status to 'Closed'
	err := a.PatchRemediationTicket(a.Context,
		func(current *entity.RemediationTicket) error {
			newPhase := entity.RemediationTicketPhaseClosed
			current.Phase = &newPhase
			current.Status = entity.RemediationTicketStatusClosed
			return nil
		},
	)
	if err != nil {
		return fmt.Errorf("approval sent but transition approving status with PatchRemediationTicket() failed: %w", err)
	}

	return nil
}

func (a *actorRejected) sendApproval(fromPerson string, toPerson string) (*uint64, error) {
	ctx := a.Context
	ticket := a.RemediationTicket

	approver := apprent.Approver{
		Email: toPerson,
	}

	param := apprsvcapi.CreateApprovalParam{
		CreateApprovalParamBase: apprsvcapi.CreateApprovalParamBase{
			Name:       "Remediation Ticket",
			Applicant:  fromPerson,
			DetailLink: rmhelpers.RemediationTicketDetailLink(ticket.ID),
			Parameters: map[string]string{
				"Incident ID":     strconv.FormatUint(ticket.AssociatedIncidentId, 10),
				"Applicant Email": ticket.ApplicantEmail,
			},
		},
		Nodes: []*apprent.ApproverNode{{Approver: &approver, Method: apprent.ApproveMethodAll}},
	}

	apprID, err := a.ApprovalService.CreateApproval(ctx, param)
	if err != nil {
		return nil, fmt.Errorf("create approval for remediation ticket id: %d failed: %w", ticket.ID, err)
	}

	return apprID, nil
}
