package errors

import (
	"fmt"
	"net/http"
)

const (
	// Status code 400
	StatusReasonBadRequest StatusReason = "BadRequest"
	// Status code 404
	StatusReasonNotFound StatusReason = "NotFound"
	// Status code 401
	StatusReasonUnauthorized StatusReason = "Unauthorized"
	// Status code 500
	StatusReasonInternalError StatusReason = "InternalServerError"
	// Status code 429
	StatusTooManyRequests StatusReason = "TooManyRequests"
	// Status code 403
	StatusForbidden StatusReason = "Forbidden"
)

var (
	_ error = &StatusError{}
)

type StatusReason string

// StatusError is an error used by REST API server.
// It's inspired by: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#response-status-kind
type StatusError struct {
	// A human-readable description of the status of this operation.
	Message string `json:"message,omitempty"`
	// A machine-readable description of why this operation is failed
	Reason StatusReason `json:"reason,omitempty"`
	// Suggested HTTP return code for this status
	Code int32 `json:"code,omitempty"`
}

func (s StatusError) Error() string {
	return s.Message
}

func NewNotFound(name string) *StatusError {
	return &StatusError{
		Message: fmt.Sprintf("%s not found", name),
		Reason:  StatusReasonNotFound,
		Code:    http.StatusNotFound,
	}
}

func NewUnauthorized() *StatusError {
	return &StatusError{
		Message: "Unauthorized",
		Reason:  StatusReasonUnauthorized,
		Code:    http.StatusUnauthorized,
	}
}

func NewInternalError(err error) *StatusError {
	if e, ok := err.(*StatusError); ok {
		return e
	}
	return &StatusError{
		Message: fmt.Sprintf("internalerror: %v", err),
		Reason:  StatusReasonInternalError,
		Code:    http.StatusInternalServerError,
	}
}

func NewBadRequest(reason string) *StatusError {
	return &StatusError{
		Message: fmt.Sprintf("badrequest: %s", reason),
		Reason:  StatusReasonBadRequest,
		Code:    http.StatusBadRequest,
	}
}

func NewTooManyRequest(reason string) *StatusError {
	return &StatusError{
		Message: fmt.Sprintf("toomanyrequests: %s", reason),
		Reason:  StatusTooManyRequests,
		Code:    http.StatusTooManyRequests,
	}
}

func NewForbidden(reason string) *StatusError {
	return &StatusError{
		Message: fmt.Sprintf("forbidden: %s", reason),
		Reason:  StatusForbidden,
		Code:    http.StatusForbidden,
	}
}
