package errors

import (
	"net/http"

	"code.justin.tv/common/twitchhttp"
	collaberrors "code.justin.tv/creator-collab/log/errors"
	"github.com/twitchtv/twirp"
)

// HTTPError is an error
type HTTPError struct {
	Err     string `json:"Error"`
	Code    int
	Message string
	Cause   string `json:"-"`
}

// Error returns an error
func (h HTTPError) Error() string {
	return h.Err
}

// InternalServerError is an internal server error
func InternalServerError(err error) HTTPError {
	return HTTPError{
		Code:    http.StatusInternalServerError,
		Err:     "InternalServerError",
		Message: "There was an error processing the request",
		Cause:   err.Error(),
	}
}

// Forbidden is a forbidden error
var Forbidden = HTTPError{
	Code:    http.StatusForbidden,
	Err:     "Forbidden",
	Message: "You do not have permission to complete the request",
}

// Unauthorized is an unauthorized error
var Unauthorized = HTTPError{
	Code:    http.StatusUnauthorized,
	Err:     "Unauthorized",
	Message: "The request could not be authorized",
}

// TooManyRequests indicates that the request has been rate limited.
func TooManyRequests(msg string) HTTPError {
	return HTTPError{
		Err:     "TooManyRequests",
		Code:    http.StatusTooManyRequests,
		Message: msg,
	}
}

// BadRequest is a bad request error
func BadRequest(msg string) HTTPError {
	return HTTPError{
		Code:    http.StatusBadRequest,
		Err:     "BadRequest",
		Message: msg,
	}
}

// GetTwirpErr is a util function to convert HTTP errors to twirp errors
func GetTwirpErr(err error) twirp.Error {
	if err == Forbidden {
		return twirp.NewError(twirp.PermissionDenied, "You do not have permission to complete the request")
	}
	twitchHTTPErr := GetTwitchHTTPErr(err)
	if twitchHTTPErr == nil {
		return twirp.InternalErrorWith(err)
	}
	switch twitchHTTPErr.StatusCode {
	case http.StatusForbidden:
		return twirp.NewError(twirp.PermissionDenied, twitchHTTPErr.Message)
	case http.StatusNotFound:
		return twirp.NotFoundError(twitchHTTPErr.Message)
	case http.StatusUnauthorized:
		return twirp.NewError(twirp.Unauthenticated, twitchHTTPErr.Message)
	case http.StatusUnprocessableEntity:
		return twirp.NewError(twirp.InvalidArgument, twitchHTTPErr.Message)
	default:
		return twirp.InternalError(twitchHTTPErr.Message)
	}
}

// GetTwitchHTTPErr traces back an error to get an HTTP error
func GetTwitchHTTPErr(err error) *twitchhttp.Error {
	for ; err != nil; err = collaberrors.Cause(err) {
		if twitchHTTPErr, ok := err.(*twitchhttp.Error); ok {
			return twitchHTTPErr
		}
	}

	return nil
}
