package langext

import "runtime/debug"

type PanicWrappedErr struct {
	panic any
	Stack string
}

func (p PanicWrappedErr) Error() string {
	return "A panic occured"
}

func (p PanicWrappedErr) RecoveredObj() any {
	return p.panic
}

func RunPanicSafe(fn func()) (err error) {
	defer func() {
		if rec := recover(); rec != nil {
			err = PanicWrappedErr{panic: rec, Stack: string(debug.Stack())}
		}
	}()

	fn()

	return nil
}

func RunPanicSafeR1(fn func() error) (err error) {
	defer func() {
		if rec := recover(); rec != nil {
			err = PanicWrappedErr{panic: rec, Stack: string(debug.Stack())}
		}
	}()

	return fn()
}

func RunPanicSafeR2[T1 any](fn func() (T1, error)) (r1 T1, err error) {
	defer func() {
		if rec := recover(); rec != nil {
			r1 = *new(T1)
			err = PanicWrappedErr{panic: rec, Stack: string(debug.Stack())}
		}
	}()

	return fn()
}

func RunPanicSafeR3[T1 any, T2 any](fn func() (T1, T2, error)) (r1 T1, r2 T2, err error) {
	defer func() {
		if rec := recover(); rec != nil {
			r1 = *new(T1)
			r2 = *new(T2)
			err = PanicWrappedErr{panic: rec, Stack: string(debug.Stack())}
		}
	}()

	return fn()
}

func RunPanicSafeR4[T1 any, T2 any, T3 any](fn func() (T1, T2, T3, error)) (r1 T1, r2 T2, r3 T3, err error) {
	defer func() {
		if rec := recover(); rec != nil {
			r1 = *new(T1)
			r2 = *new(T2)
			r3 = *new(T3)
			err = PanicWrappedErr{panic: rec, Stack: string(debug.Stack())}
		}
	}()

	return fn()
}