package actors

import (
	"context"
	"fmt"
	"slices"

	"github.com/go-logr/logr"
	"github.com/tidbcloud/devops/api"
	entity "github.com/tidbcloud/devops/entity"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/infra/errs"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
	apprsvcapi "github.com/tidbcloud/devops/pkg/service/approval/api"
	rmapi "github.com/tidbcloud/devops/pkg/service/remediationticket/api"
	phasesubsystem "github.com/tidbcloud/devops/pkg/service/remediationticket/phase"
	"github.com/tidbcloud/devops/pkg/service/remediationticket/repo"
)

type Objects struct {
	// Origin is the original instance object which is used to patch.
	//
	// NOTE: **DO NOT** modify it.
	Origin *entity.RemediationTicket
	// Instance is the copied instance object that is used in action.
	RemediationTicket *entity.RemediationTicket
	// TODO: support eventbus
}

type Components struct {
	Logger logr.Logger

	RTConfig              config.RemediationTicketConfig
	RemediationTicketRepo repo.RemediationTicketRepo
	ApprovalService       apprsvcapi.ApprovalServiceAPI
}

type Context struct {
	context.Context
	Components
	Objects
}

func (c *Context) PatchRemediationTicket(ctx context.Context, patch ...repo.PatchRemediationTicket) error {
	return c.RemediationTicketRepo.FunctionalPatchTicket(ctx, c.Origin, patch...)
}

type RemediationTicketActor interface {
	// GetContext returns the context of the actor.
	GetContext() *Context
	// SendApproval send a approval to approve this operation.
	SendApproval(patchFields rmapi.TransferTicketParam) error
	// ContinueIfApproved check the approval status and transition the instance phase.
	ContinueIfApproved() error
	// Cancel cancel a instance.
	Cancel() error
}

func NewRemediationTicketActor(ctx context.Context, comps Components, rtent *entity.RemediationTicket) RemediationTicketActor {
	var a RemediationTicketActor
	var err error

	actx := &Context{
		Context:    ctx,
		Components: comps,
		Objects: Objects{
			Origin: rtent,
		},
	}

	if actx.Origin == nil {
		return &actorPending{
			baseActor: &baseActor{
				Context:    actx,
				defaultErr: errs.APIErrorf(api.ErrCodeFailedPrecond, "only support to create a new remediation ticket"),
			},
		}
	}

	actx.RemediationTicket, err = pointer.DeepCopy(actx.Origin) // deep copy to avoid modify origin instance directly
	if err != nil {
		return &baseActor{defaultErr: fmt.Errorf("copy authorization failed: %s", err)}
	}

	status := actx.Origin.Status
	phase, err := phasesubsystem.GetPhaseByName(*actx.Origin.Phase)
	if err != nil {
		return &baseActor{defaultErr: fmt.Errorf("no assocaited phase found for actor")}
	}

	base := &baseActor{
		Context:      actx,
		currentPhase: phase,
		defaultErr: errs.APIErrorf(api.ErrCodeFailedPrecond,
			"unsupported action for instance when status is %s", status,
		),
	}
	switch {
	case phase.PhaseName() == entity.RemediationTicketPhaseSucceed:
		a = &actorApproved{baseActor: base}
	case slices.Contains(entity.RemediationTicketEndPhases, phase.PhaseName()):
		a = base
	case status == entity.RemediationTicketStatusPending:
		a = &actorPending{baseActor: base}
	case status == entity.RemediationTicketStatusApproving:
		a = &actorApproving{baseActor: base}
	case status == entity.RemediationTicketStatusRejected:
		a = &actorRejected{baseActor: base}
	default:
		base.defaultErr = fmt.Errorf("unsupported ticket status: %s in status %s", status, phase.PhaseName())
		a = base
	}

	return a
}

type baseActor struct {
	*Context

	currentPhase phasesubsystem.Phase
	defaultErr   error
}

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

func (a *baseActor) SendApproval(rmapi.TransferTicketParam) error {
	return a.defaultErr
}

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

func (a *baseActor) Cancel() error {
	return a.defaultErr
}
