package intents

import (
	"context"
	"fmt"

	"github.com/authgear/authgear-server/pkg/lib/authn"
	"github.com/authgear/authgear-server/pkg/lib/interaction"
	"github.com/authgear/authgear-server/pkg/lib/interaction/nodes"
)

func init() {
	interaction.RegisterIntent(&IntentUpdateIdentity{})
}

type IntentUpdateIdentity struct {
	UserID     string `json:"user_id"`
	IdentityID string `json:"identity_id"`
}

func NewIntentUpdateIdentity(userID string, identityID string) *IntentUpdateIdentity {
	return &IntentUpdateIdentity{
		UserID:     userID,
		IdentityID: identityID,
	}
}

func (i *IntentUpdateIdentity) InstantiateRootNode(goCtx context.Context, ctx *interaction.Context, graph *interaction.Graph) (interaction.Node, error) {
	edge := nodes.EdgeDoUseUser{UseUserID: i.UserID}
	return edge.Instantiate(goCtx, ctx, graph, i)
}

func (i *IntentUpdateIdentity) DeriveEdgesForNode(goCtx context.Context, graph *interaction.Graph, node interaction.Node) ([]interaction.Edge, error) {
	switch node := node.(type) {
	case *nodes.NodeDoUseUser:
		return []interaction.Edge{
			&nodes.EdgeUpdateIdentityBegin{
				IdentityID: i.IdentityID,
			},
		}, nil

	case *nodes.NodeUpdateIdentityEnd:
		return []interaction.Edge{
			&nodes.EdgeDoUpdateIdentity{
				IdentityBeforeUpdate: node.IdentityBeforeUpdate,
				IdentityAfterUpdate:  node.IdentityAfterUpdate,
			},
		}, nil
	case *nodes.NodeDoUpdateIdentity:
		return []interaction.Edge{
			&nodes.EdgeEnsureVerificationBegin{
				Identity:        node.IdentityAfterUpdate,
				RequestedByUser: false,
			},
		}, nil

	case *nodes.NodeEnsureVerificationEnd:
		return []interaction.Edge{
			&nodes.EdgeDoVerifyIdentity{
				Identity:         node.Identity,
				NewVerifiedClaim: node.NewVerifiedClaim,
			},
		}, nil

	case *nodes.NodeDoVerifyIdentity:
		return []interaction.Edge{
			&nodes.EdgeDoUseIdentity{Identity: node.Identity},
		}, nil

	case *nodes.NodeDoUseIdentity:
		return []interaction.Edge{
			&nodes.EdgeCreateAuthenticatorBegin{
				Stage: authn.AuthenticationStagePrimary,
			},
		}, nil

	case *nodes.NodeCreateAuthenticatorEnd:
		return []interaction.Edge{
			&nodes.EdgeDoCreateAuthenticator{
				Stage:          node.Stage,
				Authenticators: node.Authenticators,
			},
		}, nil
	case *nodes.NodeDoCreateAuthenticator:
		switch node.Stage {
		case authn.AuthenticationStagePrimary:
			return nil, nil
		default:
			panic("interaction: unexpected authenticator stage: " + node.Stage)
		}

	default:
		panic(fmt.Errorf("interaction: unexpected node: %T", node))
	}
}
