package resolver

import (
	"context"
	"encoding/json"
	"log/slog"

	"github.com/tidbcloud/jiraport/pkg/clients/lambda"
	"github.com/tidbcloud/jiraport/pkg/config"
	"github.com/tidbcloud/jiraport/pkg/db"
	"github.com/tidbcloud/jiraport/pkg/entity"
	"github.com/tidbcloud/jiraport/pkg/errors"
)

// notifications' data manager
type Notifications struct {
	logger       *slog.Logger
	dbClient     db.NotificationTransition
	lambdaClient lambda.Interface
}

// create Notifications and migrate types to database
func NewNotificationService(
	logger *slog.Logger,
	dbClient db.NotificationTransition,
	lambdaClient lambda.Interface,
) (*Notifications, error) {
	return &Notifications{
		logger:       logger,
		dbClient:     dbClient,
		lambdaClient: lambdaClient,
	}, nil
}

func (ns *Notifications) doAction(actionExecInfo *entity.NotificationActionExecInfo) error {
	if actionExecInfo == nil {
		return nil
	}
	payload, err := json.Marshal(actionExecInfo)
	if err != nil {
		return err
	}
	// action will be executed by resolver
	ev := lambda.Event{
		Name:    "NotificationAction",
		Payload: payload,
	}
	return ns.lambdaClient.InvokeAsync(context.Background(), ev)
}

func (ns *Notifications) GlobalTransitionWithUpdate(issue *entity.Issue, transEvent config.NotificationTransitionEvent) error {
	ns.logger.Info("transition with update",
		"issue", issue.Key,
		"transitionEvent", transEvent)
	actionExecInfos, err := ns.dbClient.GlobalTransitionWithUpdate(issue, transEvent)
	if err != nil {
		return err
	}

	errList := make(errors.ErrorList, 0)
	for _, actionExecInfo := range actionExecInfos {
		err = ns.doAction(actionExecInfo)
		if err != nil {
			errList = append(errList, err)
		}
	}
	if len(errList) != 0 {
		return errList
	}
	return nil
}

func (ns *Notifications) Transition(
	notificationID uint,
	transEvent config.NotificationTransitionEvent,
) error {
	actionExecInfo, err := ns.dbClient.Transition(notificationID, transEvent)
	if err != nil {
		return err
	}
	if actionExecInfo != nil {
		err := ns.doAction(actionExecInfo)
		if err != nil {
			return err
		}
	}
	return nil
}

func (ns *Notifications) UpdateAssignee(issueKey string, assigneeEmail string) error {
	issue := entity.Issue{Key: issueKey, AssigneeEmail: assigneeEmail}
	err := ns.GlobalTransitionWithUpdate(&issue, config.InnerUpdatedAssigneeEvent)
	err = HandleTransitionError(err, func(te *db.TransitionError) error {
		ns.logger.Warn("do global transition error part",
			"err", te, "issue", issueKey)
		return nil
	})
	return err
}

func HandleTransitionError(err error, f func(*db.TransitionError) error) error {
	switch err := err.(type) {
	case *db.TransitionError:
		return f(err)
	case *db.TransitionErrorList:
		errList := make([]error, 0)
		for _, innerErr := range err.ErrorList {
			outErr := HandleTransitionError(innerErr, f)
			if outErr != nil {
				errList = append(errList, outErr)
			}
		}
		if len(errList) != 0 {
			return &db.TransitionErrorList{ErrorList: errList}
		}
		return nil
	}
	return err
}
