package errors

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws/awserr"
	pkg_errors "github.com/pkg/errors"
)

// extends default golang error interface
type ErrorWithCause interface {
	error
	Cause() error
	Code() int
}

type ErrorWithIs interface {
	error
	Is(specificError error) bool
}

func New(message string) error {
	err := &CustomError{
		message: message,
		caller:  GetCaller(2),
		cause:   nil,
	}
	return err
}

func Error(message string) error {
	err := &CustomError{
		message: message,
		caller:  GetCaller(2),
		cause:   nil,
	}
	return err
}

func Errorf(format string, args ...interface{}) error {
	err := &CustomError{
		message: fmt.Sprintf(format, args...),
		caller:  GetCaller(2),
		cause:   nil,
	}
	return err
}

func Wrapf(err error, format string, args ...interface{}) error {
	if err == nil {
		return nil
	}

	wrappedErr := &CustomError{
		message: fmt.Sprintf(format, args...),
		caller:  GetCaller(2),
		cause:   err,
	}

	return wrappedErr
}

func Wrap(err error, msg string) error {
	if err == nil {
		return nil
	}

	wrappedErr := &CustomError{
		message: msg,
		caller:  GetCaller(2),
		cause:   err,
	}

	return wrappedErr
}

func HTTP(code int, err error, format string, args ...interface{}) error {
	wrappedErr := &CustomError{
		code:    code,
		message: fmt.Sprintf(format, args...),
		caller:  GetCaller(2),
		cause:   err,
	}

	return wrappedErr
}

func HTTPWithMsg(code int, format string, args ...interface{}) error {
	errorString := fmt.Sprintf(format, args...)

	wrappedErr := &CustomError{
		code:    code,
		message: errorString,
		caller:  GetCaller(2),
		cause:   Error(errorString),
	}

	return wrappedErr
}

func HTTPCodeOnly(code int) error {
	return HTTP(code, nil, "")
}

func HTTPWithError(code int, err error) error {
	var errorMessage string
	// This check is here just as a failsafe to seg faults, if err is nil then just return assign an empty string as message.
	if err != nil {
		errorMessage = err.Error()
	}

	wrappedErr := &CustomError{
		code:    code,
		message: errorMessage,
		caller:  GetCaller(2),
		cause:   err,
	}

	return wrappedErr
}

func AWSErrorExceptionCode(err error) string {
	if err == nil {
		return ""
	}

	if awsError, ok := err.(awserr.Error); ok {
		return awsError.Code()
	}
	return ""
}

func AWSErrorWithoutExceptionCode(err error) error {
	if err == nil {
		return nil
	}

	if awsError, ok := err.(awserr.Error); ok {
		return Error(awsError.Message())
	}
	return err
}

type Description struct {
	Message string       `json:"error"`
	Source  *CallerInfo  `json:"source,omitempty"`
	Cause   *Description `json:"cause,omitempty"`
}

// from github.com/pkg/errors:
type stackTracer interface {
	StackTrace() pkg_errors.StackTrace
}