package errors_test

import (
	"encoding/json"
	"testing"

	"github.com/uafrica/go-utils/errors"
)

func TestErrorFormatting(t *testing.T) {
	//pretend this is your error stack
	//the original error is created by 3rd party library e.g. pg or mysql and may not have stack info
	//but from that point on, we use our errors package which includes stack info...
	e1 := errors.Errorf("you have problem in your SQL near xxx")
	e2 := errors.Wrapf(e1, "query failed")
	e3 := errors.Wrapf(e2, "failed to find account")
	e4 := errors.Wrapf(e3, "login failed").(*errors.CustomError)

	//we can log the error in different ways:

	//use %s for a simple message
	//-> "login failed"
	t.Logf("%%s: %s", e4)

	//use either Error() or "%v" for concatenated message:
	//-> "login failed, because failed to find account, because query failed, because you have problem in your SQL near xxx"
	t.Logf("Error(): %s", e4.Error())
	t.Logf("%%v: %v", e4)

	//use %+v for complete error with stack:
	//-> "github.com/uafrica/go-utils/errors_test/TestErrorFormatting():errors_test.go(17): login failed, because github.com/uafrica/go-utils/errors_test/TestErrorFormatting():errors_test.go(16): failed to find account, because github.com/uafrica/go-utils/errors_test/TestErrorFormatting():errors_test.go(15): query failed, because you have problem in your SQL near xxx
	t.Logf("%%+v: %+v", e4)

	//you may also JSON marshal the error
	//->
	jsonError, _ := json.Marshal(e4.Description())
	t.Logf("json: %s", string(jsonError))

	//using the Description(), one can also verify that the correct
	//source references are reported
	desc := e4.Description()
	if desc.Source == nil || desc.Source.Line != 18 {
		t.Fatalf("failed not on line 18")
	}
	desc = *desc.Cause
	if desc.Source == nil || desc.Source.Line != 17 {
		t.Fatalf("failed not on line 17")
	}
	desc = *desc.Cause
	if desc.Source == nil || desc.Source.Line != 16 {
		t.Fatalf("failed not on line 16")
	}
}

func TestErrorStack(t *testing.T) {
	//if some function (e.g. 3rd party) fail without stack, then store the calling stack and append as it is wrapped from there...
	err := a(1)
	t.Logf("err: %+v", err)
}

func a(i int) error {
	return errors.Wrapf(b(i), "b failed")
}

func b(i int) error {
	return errors.Wrapf(c(i), "c failed")
}

func c(i int) error {
	return errors.Wrapf(d(i), "d failed")
}

func d(i int) error {
	if err := e(i); err != nil {
		return errors.Wrapf(err, "e(%d) failed", i)
	}
	return nil
}

func e(i int) error {
	if err := f(i); err != nil {
		return errors.Wrapf(err, "f(%d) failed", i)
	}
	return nil
}

func f(i int) error {
	if i%2 == 0 {
		return nil
	}
	return errors.Errorf("i=%d is odd", i)
}