package error

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/joomcode/errorx"
)

var (
	ErrUnauthorized       = errorx.CommonErrors.NewType("unauthorized")
	ErrForbidden          = errorx.CommonErrors.NewType("forbidden")
	ErrBadRequest         = errorx.CommonErrors.NewType("bad_request")
	ErrNotFound           = errorx.CommonErrors.NewType("not_found")
	ErrPreConditionFailed = errorx.CommonErrors.NewType("precondition_failed")
	ErrServiceUnavailable = errorx.CommonErrors.NewType("service_unavailable")
	ErrTooManyRequest     = errorx.CommonErrors.NewType("Too many requests, please retry later.")
	ErrInternal           = errorx.CommonErrors.NewType("internal")

	propHTTPCode = errorx.RegisterProperty("http_code")
)

func extractHTTPCodeFromError(err error) int {
	if err == nil {
		return http.StatusOK
	}

	ex := errorx.Cast(err)
	if ex == nil {
		return http.StatusInternalServerError
	}

	v, ok := ex.Property(propHTTPCode)
	if ok {
		return v.(int)
	}

	if ex.IsOfType(ErrUnauthorized) {
		return http.StatusUnauthorized
	}
	if ex.IsOfType(ErrForbidden) {
		return http.StatusForbidden
	}
	if ex.IsOfType(ErrBadRequest) {
		return http.StatusBadRequest
	}
	if ex.IsOfType(ErrNotFound) {
		return http.StatusNotFound
	}
	if ex.IsOfType(ErrInternal) {
		return http.StatusInternalServerError
	}
	if ex.IsOfType(ErrPreConditionFailed) {
		return http.StatusPreconditionFailed
	}
	if ex.IsOfType(ErrServiceUnavailable) {
		return http.StatusServiceUnavailable
	}

	if ex.IsOfType(ErrTooManyRequest) {
		return http.StatusTooManyRequests
	}

	return http.StatusInternalServerError
}

func ErrorHandlerFn(c *gin.Context) {
	c.Next()

	err := c.Errors.Last()
	if err == nil {
		return
	}

	if c.Writer.Size() > 0 {
		return
	}

	statusCode := c.Writer.Status()
	if statusCode == http.StatusOK {
		// Change the status code if it is not specified.
		statusCode = extractHTTPCodeFromError(err.Err)
	}

	c.AbortWithStatusJSON(statusCode, NewErrorResponse(err.Err))
}
