package merrors

import (
	"errors"
	"fmt"
	"strings"

	"go.mongodb.org/mongo-driver/v2/bson"
)

var ErrNotImplemented = errors.New("notImplemented")

func NotImplemented(msg string) error {
	return fmt.Errorf("%w: %s", ErrNotImplemented, msg)
}

var ErrNoData = errors.New("noData")

func NoData(msg string) error {
	return fmt.Errorf("%w: %s", ErrNoData, msg)
}

var ErrInternal = errors.New("internal")

func Internal(msg string) error {
	return fmt.Errorf("%w: %s", ErrInternal, msg)
}

var ErrInvalidArg = errors.New("invalidArgError")

func InvalidArgument(msg string, argumentNames ...string) error {
	return fmt.Errorf("%w: %s", ErrInvalidArg, invalidArgumentMessage(msg, argumentNames...))
}

var ErrDataConflict = errors.New("dataConflict")

func DataConflict(msg string) error {
	return fmt.Errorf("%w: %s", ErrDataConflict, msg)
}

var ErrAccessDenied = errors.New("accessDenied")

func AccessDenied(object, action string, objectRef bson.ObjectID) error {
	if objectRef != bson.NilObjectID {
		return fmt.Errorf("%w: cannot %s object of type '%s' with ID '%s'", ErrAccessDenied, action, object, objectRef.Hex())
	}
	return fmt.Errorf("%w: cannot %s object of type '%s'", ErrAccessDenied, action, object)
}

var ErrInvalidDataType = errors.New("invalidDataType")

func InvalidDataType(msg string) error {
	return fmt.Errorf("%w: %s", ErrInvalidDataType, msg)
}

var ErrUnauthorized = errors.New("unathorized")

func Unauthorized(msg string) error {
	return fmt.Errorf("%w: %s", ErrUnauthorized, msg)
}

var ErrNoMessagingTopic = errors.New("messagingTopicError")

func NoMessagingTopic(topic string) error {
	return fmt.Errorf("%w: messaging topic '%s' not found", ErrNoMessagingTopic, topic)
}

func InvalidArgumentWrap(err error, msg string, argumentNames ...string) error {
	return wrapError(ErrInvalidArg, invalidArgumentMessage(msg, argumentNames...), err)
}

func InternalWrap(err error, msg string) error {
	return wrapError(ErrInternal, msg, err)
}

func wrapError(base error, msg string, err error) error {
	baseErr := fmt.Errorf("%w: %s", base, msg)
	if err == nil {
		return baseErr
	}
	return errors.Join(baseErr, err)
}

func invalidArgumentMessage(msg string, argumentNames ...string) string {
	names := make([]string, 0, len(argumentNames))
	for _, name := range argumentNames {
		name = strings.TrimSpace(name)
		if name == "" {
			continue
		}
		names = append(names, fmt.Sprintf("%q", name))
	}
	if len(names) == 0 {
		return msg
	}

	prefix := "broken argument"
	if len(names) > 1 {
		prefix = "broken arguments"
	}
	return fmt.Sprintf("%s %s: %s", prefix, strings.Join(names, ", "), msg)
}
