package exerr

import (
	"github.com/gin-gonic/gin"
	json "gogs.mikescher.com/BlackForestBytes/goext/gojson"
	"gogs.mikescher.com/BlackForestBytes/goext/langext"
	"net/http"
	"time"
)

func (ee *ExErr) toJson(depth int, applyExtendListener bool, outputMeta bool) langext.H {
	ginJson := langext.H{}

	if ee.UniqueID != "" {
		ginJson["id"] = ee.UniqueID
	}
	if ee.Category != CatWrap {
		ginJson["category"] = ee.Category.Category
	}
	if ee.Type != TypeWrap {
		ginJson["type"] = ee.Type.Key
	}
	if ee.StatusCode != nil {
		ginJson["statuscode"] = ee.StatusCode
	}
	if ee.Message != "" {
		ginJson["message"] = ee.Message
	}
	if ee.Caller != "" {
		ginJson["caller"] = ee.Caller
	}
	if ee.Severity != SevErr {
		ginJson["severity"] = ee.Severity.Severity
	}
	if ee.Timestamp != (time.Time{}) {
		ginJson["time"] = ee.Timestamp.Format(time.RFC3339)
	}
	if ee.WrappedErrType != "" {
		ginJson["wrappedErrType"] = ee.WrappedErrType
	}
	if ee.OriginalError != nil {
		ginJson["original"] = ee.OriginalError.toJson(depth+1, applyExtendListener, outputMeta)
	}

	if outputMeta {
		metaJson := langext.H{}
		for metaKey, metaVal := range ee.Meta {
			metaJson[metaKey] = metaVal.rawValueForJson()
		}
		ginJson["meta"] = metaJson
	}

	if applyExtendListener {
		pkgconfig.ExtendGinDataOutput(ee, depth, ginJson)
	}

	return ginJson
}

func (ee *ExErr) ToDefaultAPIJson() (string, error) {

	gjr := json.GoJsonRender{Data: ee.ToAPIJson(true, pkgconfig.ExtendedGinOutput, pkgconfig.IncludeMetaInGinOutput), NilSafeSlices: true, NilSafeMaps: true}

	r, err := gjr.RenderString()

	if err != nil {
		return "", err
	}

	return r, nil
}

// ToAPIJson converts the ExError to a json object
// (the same object as used in the Output(gin) method)
//
// Parameters:
//   - [applyExtendListener]:  if false the pkgconfig.ExtendGinOutput / pkgconfig.ExtendGinDataOutput will not be applied
//   - [includeWrappedErrors]: if false we do not include the recursive/wrapped errors in `__data`
//   - [includeMetaFields]:    if true  we also include meta-values (aka from `.Str(key, value).Build()`), needs includeWrappedErrors=true
func (ee *ExErr) ToAPIJson(applyExtendListener bool, includeWrappedErrors bool, includeMetaFields bool) langext.H {

	apiOutput := langext.H{
		"errorid":   ee.UniqueID,
		"message":   ee.RecursiveMessage(),
		"errorcode": ee.RecursiveType().Key,
		"category":  ee.RecursiveCategory().Category,
	}

	if includeWrappedErrors {
		apiOutput["__data"] = ee.toJson(0, applyExtendListener, includeMetaFields)
	}

	for exkey, exval := range ee.Extra {

		// ensure we do not override existing values
		for {
			if _, ok := apiOutput[exkey]; ok {
				exkey = "_" + exkey
			} else {
				break
			}
		}

		apiOutput[exkey] = exval
	}

	if applyExtendListener {
		pkgconfig.ExtendGinOutput(ee, apiOutput)
	}

	return apiOutput
}

func (ee *ExErr) Output(g *gin.Context) {

	warnOnPkgConfigNotInitialized()

	var statuscode = http.StatusInternalServerError

	var baseCat = ee.RecursiveCategory()
	var baseType = ee.RecursiveType()
	var baseStatuscode = ee.RecursiveStatuscode()

	if baseCat == CatUser {
		statuscode = http.StatusBadRequest
	} else if baseCat == CatSystem {
		statuscode = http.StatusInternalServerError
	}

	if baseStatuscode != nil {
		statuscode = *ee.StatusCode
	} else if baseType.DefaultStatusCode != nil {
		statuscode = *baseType.DefaultStatusCode
	}

	ginOutput := ee.ToAPIJson(true, pkgconfig.ExtendedGinOutput, pkgconfig.IncludeMetaInGinOutput)

	g.Render(statuscode, json.GoJsonRender{Data: ginOutput, NilSafeSlices: true, NilSafeMaps: true})
}