package mid

import (
	"net/http"

	"github.com/go-chi/chi/v5/middleware"
	"github.com/hay-kot/homebox/backend/internal/data/ent"
	"github.com/hay-kot/homebox/backend/internal/sys/validate"
	"github.com/hay-kot/httpkit/errchain"
	"github.com/hay-kot/httpkit/server"
	"github.com/rs/zerolog"
)

type ErrorResponse struct {
	Error  string            `json:"error"`
	Fields map[string]string `json:"fields,omitempty"`
}

func Errors(svr *server.Server, log zerolog.Logger) errchain.ErrorHandler {
	return func(h errchain.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			err := h.ServeHTTP(w, r)
			if err != nil {
				var resp ErrorResponse
				var code int

				traceID := r.Context().Value(middleware.RequestIDKey).(string)
				log.Err(err).
					Stack().
					Str("req_id", traceID).
					Msg("ERROR occurred")

				switch {
				case validate.IsUnauthorizedError(err):
					code = http.StatusUnauthorized
					resp = ErrorResponse{
						Error: "unauthorized",
					}
				case validate.IsInvalidRouteKeyError(err):
					code = http.StatusBadRequest
					resp = ErrorResponse{
						Error: err.Error(),
					}
				case validate.IsFieldError(err):
					code = http.StatusUnprocessableEntity

					fieldErrors := err.(validate.FieldErrors)
					resp.Error = "Validation Error"
					resp.Fields = map[string]string{}

					for _, fieldError := range fieldErrors {
						resp.Fields[fieldError.Field] = fieldError.Error
					}
				case validate.IsRequestError(err):
					requestError := err.(*validate.RequestError)
					resp.Error = requestError.Error()

					if requestError.Status == 0 {
						code = http.StatusBadRequest
					} else {
						code = requestError.Status
					}
				case ent.IsNotFound(err):
					resp.Error = "Not Found"
					code = http.StatusNotFound
				default:
					resp.Error = "Unknown Error"
					code = http.StatusInternalServerError
				}

				if err := server.JSON(w, code, resp); err != nil {
					log.Err(err).Msg("failed to write response")
				}

				// If Showdown error, return error
				if server.IsShutdownError(err) {
					err := svr.Shutdown(err.Error())
					if err != nil {
						log.Err(err).Msg("failed to shutdown server")
					}
				}
			}
		})
	}
}
