package operations

import (
	"context"
	"fmt"

	"github.com/alcionai/clues"

	"github.com/alcionai/corso/src/pkg/control"
	"github.com/alcionai/corso/src/pkg/fault"
	"github.com/alcionai/corso/src/pkg/logger"
)

// finalizeErrorHandling ensures the operation follow the options
// failure behavior requirements.
func finalizeErrorHandling(
	ctx context.Context,
	opts control.Options,
	errs *fault.Bus,
	prefix string,
) {
	rcvd := errs.Recovered()

	// under certain conditions, there's nothing else left to do
	if opts.FailureHandling == control.BestEffort ||
		errs.Failure() != nil ||
		len(rcvd) == 0 {
		return
	}

	if opts.FailureHandling == control.FailAfterRecovery {
		msg := fmt.Sprintf("%s: partial success: %d errors occurred", prefix, len(rcvd))
		logger.Ctx(ctx).Error(msg)

		if len(rcvd) == 1 {
			errs.Fail(rcvd[0])
			return
		}

		errs.Fail(clues.New(msg))
	}
}

// LogFaultErrors is a helper function that logs all entries in the Errors struct.
func LogFaultErrors(ctx context.Context, fe *fault.Errors, prefix string) {
	if fe == nil {
		return
	}

	var (
		log        = logger.Ctx(ctx)
		pfxMsg     = prefix + ":"
		li, ls, lr = len(fe.Items), len(fe.Skipped), len(fe.Recovered)
	)

	if fe.Failure == nil && li+ls+lr == 0 {
		log.Info(pfxMsg + " no issues")
		return
	}

	if fe.Failure != nil {
		log.With("error", fe.Failure).Errorf("%s primary failure: %s", pfxMsg, fe.Failure.Msg)
	}

	for i, item := range fe.Items {
		log.With("failed_item", item).Errorf("%s item failure %d of %d: %s", pfxMsg, i+1, li, item.Cause)
	}

	for i, item := range fe.Skipped {
		log.With("skipped_item", item).Errorf("%s skipped item %d of %d: %s", pfxMsg, i+1, ls, item.Item.Cause)
	}

	for i, err := range fe.Recovered {
		log.With("recovered_error", err).Errorf("%s recoverable error %d of %d: %s", pfxMsg, i+1, lr, err.Msg)
	}
}
