package exerr

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"gogs.mikescher.com/BlackForestBytes/goext/langext"
	"net/http"
)

type ErrorPackageConfig struct {
	ZeroLogErrTraces       bool                                                                     // autom print zerolog logs on .Build()  (for SevErr and SevFatal)
	ZeroLogAllTraces       bool                                                                     // autom print zerolog logs on .Build()  (for all Severities)
	RecursiveErrors        bool                                                                     // errors contains their Origin-Error
	ExtendedGinOutput      bool                                                                     // Log extended data (trace, meta, ...) to gin in err.Output()
	IncludeMetaInGinOutput bool                                                                     // Log meta fields ( from e.g. `.Str(key, val).Build()` ) to gin in err.Output()
	ExtendGinOutput        func(err *ExErr, json map[string]any)                                    // (Optionally) extend the gin output with more fields
	ExtendGinDataOutput    func(err *ExErr, depth int, json map[string]any)                         // (Optionally) extend the gin `__data` output with more fields
	DisableErrorWrapping   bool                                                                     // Disables the exerr.Wrap()...Build() function - will always return the original error
	ZeroLogErrGinOutput    bool                                                                     // autom print zerolog logs on ginext.Error() / .Output(gin)  (for SevErr and SevFatal)
	ZeroLogAllGinOutput    bool                                                                     // autom print zerolog logs on ginext.Error() / .Output(gin)  (for all Severities)
	ExtendGinMeta          func(ctx context.Context, b *Builder, g *gin.Context, req *http.Request) // (Optionally) extend the final error meta values with additional data from the gin context (a few are automatically added, here more can be included)
	ExtendContextMeta      func(b *Builder, method Method, dctx context.Context)                    // (Optionally) extend the final error meta values with additional data from the context (a few are automatically added, here more can be included)
}

type ErrorPackageConfigInit struct {
	ZeroLogErrTraces       *bool
	ZeroLogAllTraces       *bool
	RecursiveErrors        *bool
	ExtendedGinOutput      *bool
	IncludeMetaInGinOutput *bool
	ExtendGinOutput        func(err *ExErr, json map[string]any)
	ExtendGinDataOutput    func(err *ExErr, depth int, json map[string]any)
	DisableErrorWrapping   *bool
	ZeroLogErrGinOutput    *bool
	ZeroLogAllGinOutput    *bool
	ExtendGinMeta          func(ctx context.Context, b *Builder, g *gin.Context, req *http.Request)
	ExtendContextMeta      func(b *Builder, method Method, dctx context.Context)
}

var initialized = false

var pkgconfig = ErrorPackageConfig{
	ZeroLogErrTraces:       true,
	ZeroLogAllTraces:       false,
	RecursiveErrors:        true,
	ExtendedGinOutput:      false,
	IncludeMetaInGinOutput: true,
	ExtendGinOutput:        func(err *ExErr, json map[string]any) {},
	ExtendGinDataOutput:    func(err *ExErr, depth int, json map[string]any) {},
	DisableErrorWrapping:   false,
	ZeroLogErrGinOutput:    true,
	ZeroLogAllGinOutput:    false,
	ExtendGinMeta:          func(ctx context.Context, b *Builder, g *gin.Context, req *http.Request) {},
	ExtendContextMeta:      func(b *Builder, method Method, dctx context.Context) {},
}

// Init initializes the exerr packages
// Must be called at the program start, before (!) any errors
// Is not thread-safe
func Init(cfg ErrorPackageConfigInit) {
	if initialized {
		panic("Cannot re-init error package")
	}

	ego := func(err *ExErr, json map[string]any) {}
	egdo := func(err *ExErr, depth int, json map[string]any) {}
	egm := func(ctx context.Context, b *Builder, g *gin.Context, req *http.Request) {}
	egcm := func(b *Builder, method Method, dctx context.Context) {}

	if cfg.ExtendGinOutput != nil {
		ego = cfg.ExtendGinOutput
	}
	if cfg.ExtendGinDataOutput != nil {
		egdo = cfg.ExtendGinDataOutput
	}
	if cfg.ExtendGinMeta != nil {
		egm = cfg.ExtendGinMeta
	}
	if cfg.ExtendContextMeta != nil {
		egcm = cfg.ExtendContextMeta
	}

	pkgconfig = ErrorPackageConfig{
		ZeroLogErrTraces:       langext.Coalesce(cfg.ZeroLogErrTraces, pkgconfig.ZeroLogErrTraces),
		ZeroLogAllTraces:       langext.Coalesce(cfg.ZeroLogAllTraces, pkgconfig.ZeroLogAllTraces),
		RecursiveErrors:        langext.Coalesce(cfg.RecursiveErrors, pkgconfig.RecursiveErrors),
		ExtendedGinOutput:      langext.Coalesce(cfg.ExtendedGinOutput, pkgconfig.ExtendedGinOutput),
		IncludeMetaInGinOutput: langext.Coalesce(cfg.IncludeMetaInGinOutput, pkgconfig.IncludeMetaInGinOutput),
		ExtendGinOutput:        ego,
		ExtendGinDataOutput:    egdo,
		DisableErrorWrapping:   langext.Coalesce(cfg.DisableErrorWrapping, pkgconfig.DisableErrorWrapping),
		ZeroLogAllGinOutput:    langext.Coalesce(cfg.ZeroLogAllGinOutput, pkgconfig.ZeroLogAllGinOutput),
		ZeroLogErrGinOutput:    langext.Coalesce(cfg.ZeroLogErrGinOutput, pkgconfig.ZeroLogErrGinOutput),
		ExtendGinMeta:          egm,
		ExtendContextMeta:      egcm,
	}

	initialized = true
}

func Initialized() bool {
	return initialized
}

func warnOnPkgConfigNotInitialized() {
	if !initialized {
		fmt.Printf("\n")
		fmt.Printf("%s\n", langext.StrRepeat("=", 80))
		fmt.Printf("%s\n", "[WARNING] exerr package used without initializiation")
		fmt.Printf("%s\n", "          call exerr.Init() in your main() function")
		fmt.Printf("%s\n", langext.StrRepeat("=", 80))
		fmt.Printf("\n")
	}
}