package errs

import (
	"errors"

	"backend/pkg/library/errors/code"

	"google.golang.org/grpc/status"
)

func newBuiltinGRPCErr(scope, detail uint32, msg string) *Error {
	return &Error{
		category: uint32(code.CatGRPC),
		detail:   detail,
		scope:    scope,
		msg:      msg,
	}
}

// FromError tries to let error as Err
// it supports to unwrap error that has Error
// return nil if failed to transfer
func FromError(err error) *Error {
	if err == nil {
		return nil
	}

	var e *Error
	if errors.As(err, &e) {
		return e
	}

	return nil
}

// FromCode parses code as following 8 碼
// Decimal: 10201000
// 10 represents Scope
// 201 represents Category
// 000 represents Detail error code
func FromCode(code uint32) *Error {
	const CodeMultiplier = 1000000
	const SubMultiplier = 1000
	// 獲取 scope，前兩位數
	scope := code / CodeMultiplier

	// 獲取 detail，最後三位數
	detail := code % SubMultiplier

	// 獲取 category，中間三位數
	category := (code / SubMultiplier) % SubMultiplier

	return &Error{
		category: category,
		detail:   detail,
		scope:    scope,
		msg:      "",
	}
}

// FromGRPCError transfer error to Err
// useful for gRPC client
func FromGRPCError(err error) *Error {
	s, _ := status.FromError(err)
	e := FromCode(uint32(s.Code()))
	e.msg = s.Message()

	// For GRPC built-in code
	if e.Scope() == uint32(code.Unset) && e.Category() == 0 && e.Code() != code.OK {
		e = newBuiltinGRPCErr(uint32(Scope), e.detail, s.Message()) // Note: detail is now 3-digit, but built-in codes are small
	}

	return e
}
