package errorh

import (
	"errors"
	"github.com/labstack/echo/v4"
	"net/http"
	"fmt"
)

type (
	httpErrorHandler struct {
		statusCodes map[error]int
	}
)


func NewHttpErrorHandler() *httpErrorHandler {
	return &httpErrorHandler{}
}

func (self *httpErrorHandler) getStatusCode(err error) int {
	for key, value := range self.statusCodes {
		if errors.Is(err, key) {
			return value
		}
	}

	return http.StatusInternalServerError
}

func unwrapRecursive(err error) error {
	var originalErr = err

	for originalErr != nil {
		var internalErr = errors.Unwrap(originalErr)

		if internalErr == nil {
			break
		}

		originalErr = internalErr
	}

	return originalErr
}

func (self *httpErrorHandler) Handler(err error, c echo.Context) {

	technical := ""
	he, ok := err.(*echo.HTTPError)
	if ok {
		if he.Internal != nil {
			if herr, ok := he.Internal.(*echo.HTTPError); ok {
				he = herr
			}
			technical = fmt.Sprintf("%v", he.Internal)
		}
	} else {
		he = &echo.HTTPError{
			Code:    self.getStatusCode(err),
			Message: unwrapRecursive(err).Error(),
		}
	}

	code := he.Code
	stdError := ErrorModel{
		Message: fmt.Sprintf("%v", he.Message),
		Resolution: "",
		Status: fmt.Sprintf("DM00%d", code),
		Technical: technical,
	}

	if (code == 401) {
		stdError.Resolution = "Please provide authentication details.  These can be API key, Basic Auth, Bearer Auth."
	}
	if (code == 400) {
		if (stdError.Message == "no matching operation was found") {
			stdError.Resolution = "Please check the Operation name and resource and correct to a valid name.  Reference the Open API specification for valid names"
		}
	}

	// Send response
	if !c.Response().Committed {
		if c.Request().Method == http.MethodHead {
			err = c.NoContent(he.Code)
		} else {
			err = c.JSON(code, stdError)
		}
		if err != nil {
			c.Echo().Logger.Error(err)
		}
	}
}
