package actors

import (
	"fmt"
	"strconv"

	"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 actorPending struct {
	*baseActor
}

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

func (a *actorPending) 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() {
		nextPhaseName := nextPhase.PhaseName()
		if nextPhaseName == entity.RemediationTicketPhaseSucceed {
			apprID, err := a.sendSuccessApprovalCC()
			if err != nil {
				return fmt.Errorf("send success approval cc failed: %w", err)
			}
			if apprID != nil {
				_, err = ctx.Components.RemediationTicketRepo.SaveTicketPhaseApproval(ctx, &entity.RemediationTicketPhaseApproval{
					TicketId:   a.Origin.ID,
					FromPhase:  a.currentPhase.PhaseName(),
					ToPhase:    nextPhaseName,
					ApprovalID: *apprID,
				})
				if err != nil {
					return fmt.Errorf("create success ticket phase approval failed: %w", err)
				}
			}
		}

		// Transfer to final phase
		err = a.PatchRemediationTicket(ctx,
			func(current *entity.RemediationTicket) error {
				current.ApprID = 0
				current.Phase = &nextPhaseName
				if nextPhaseName == entity.RemediationTicketPhaseClosed {
					current.Status = entity.RemediationTicketStatusClosed
				} else {
					current.Status = entity.RemediationTicketStatusApproved
				}
				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.ApplicantEmail, 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
			current.CurrentResponsible = nextReponsiblePerson
			return nil
		},
	)
	if err != nil {
		return fmt.Errorf("approval sent but transition approving status with PatchRemediationTicket() failed: %w", err)
	}

	return nil
}

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

func (a *actorPending) 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 *actorPending) 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,
			},
			Reason: "Please view remediation ticket details by clicking **detail link**",
		},
		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
}

func (a *actorPending) sendSuccessApprovalCC() (*uint64, error) {
	ctx := a.Context
	ticket := a.RemediationTicket

	ccEmails := rmhelpers.GetSuccessApprovalCCEmails(a.RTConfig.SuccessApprovalCCByBizRegion, ticket)
	if len(ccEmails) == 0 {
		return nil, nil
	}

	param := apprsvcapi.CreateApprovalParam{
		CreateApprovalParamBase: apprsvcapi.CreateApprovalParamBase{
			Name:       "Remediation Ticket Completed",
			Applicant:  ticket.ApplicantEmail,
			DetailLink: rmhelpers.RemediationTicketDetailLink(ticket.ID),
			Parameters: map[string]string{
				"Incident ID":     strconv.FormatUint(ticket.AssociatedIncidentId, 10),
				"Applicant Email": ticket.ApplicantEmail,
			},
			Reason: "The remediation ticket approval flow has completed.",
		},
		CCTasks: &apprent.Approver{
			Emails: ccEmails,
		},
	}

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

	return apprID, nil
}
