package standardaction

import (
	"context"
	"errors"
	"fmt"

	"github.com/nexuscrm/automation"
	"github.com/nexuscrm/shared/pkg/contract"
)

type ErrorKind string

const (
	ErrorNotRegistered      ErrorKind = "not_registered"
	ErrorDisabled           ErrorKind = "disabled"
	ErrorUnauthorized       ErrorKind = "unauthorized"
	ErrorInvalidRequest     ErrorKind = "invalid_request"
	ErrorPreviewUnsupported ErrorKind = "preview_unsupported"
	ErrorNoPreviewProducer  ErrorKind = "no_preview_producer"
)

type Error struct {
	Kind     ErrorKind
	Code     string
	Message  string
	Cause    error
	Internal bool
}

func (e *Error) Error() string {
	if e == nil {
		return ""
	}
	if e.Cause != nil {
		return fmt.Sprintf("%s: %v", e.Message, e.Cause)
	}
	return e.Message
}

func (e *Error) Unwrap() error {
	if e == nil {
		return nil
	}
	return e.Cause
}

func outcomeFromError(actionName string, err error) *contract.StandardActionOutcome {
	if err == nil {
		return nil
	}
	var actionErr *Error
	if !errors.As(err, &actionErr) {
		return &contract.StandardActionOutcome{
			Kind:  contract.StandardActionOutcomeInternal,
			Name:  actionName,
			Cause: err,
		}
	}
	if actionErr.Internal {
		return &contract.StandardActionOutcome{
			Kind:    contract.StandardActionOutcomeInternal,
			Code:    actionErr.Code,
			Name:    actionName,
			Message: actionErr.Message,
			Cause:   err,
		}
	}
	out := &contract.StandardActionOutcome{
		Code:    actionErr.Code,
		Name:    actionName,
		Message: actionErr.Message,
		Cause:   actionErr.Cause,
	}
	switch actionErr.Kind {
	case ErrorNotRegistered:
		out.Kind = contract.StandardActionOutcomeNotRegistered
	case ErrorDisabled:
		out.Kind = contract.StandardActionOutcomeDisabled
	case ErrorUnauthorized:
		out.Kind = contract.StandardActionOutcomeUnauthorized
	case ErrorInvalidRequest:
		out.Kind = contract.StandardActionOutcomeInvalidRequest
	case ErrorPreviewUnsupported:
		out.Kind = contract.StandardActionOutcomePreviewUnsupported
	case ErrorNoPreviewProducer:
		out.Kind = contract.StandardActionOutcomeNoPreviewProducer
	default:
		out.Kind = contract.StandardActionOutcomeInternal
		out.Cause = err
	}
	return out
}

func outcomeFromActionError(_ context.Context, action *Action, err error) *contract.StandardActionOutcome {
	if err == nil {
		return nil
	}
	actionName := ""
	if action != nil {
		actionName = action.Name
	}
	if errors.Is(err, automation.ErrAtomicFlowDeadlineExceeded) {
		return &contract.StandardActionOutcome{
			Kind:  contract.StandardActionOutcomeTimeout,
			Name:  actionName,
			Cause: err,
		}
	}
	return outcomeFromError(actionName, err)
}

func actionRefName(ref *contract.StandardActionRef) string {
	if ref == nil {
		return ""
	}
	return ref.Name
}
