package billing_errors

import (
	"fmt"
	"net/http"

	"github.com/pingcap/errors"
)

type BaseError struct {
	error
	Status    int
	MsgPrefix string
	Code      string
}

func (base BaseError) Unwrap() error {
	return base.error
}

func (base BaseError) Error() string {
	return fmt.Sprintf("%v:%v", base.MsgPrefix, base.error.Error())
}

func (base BaseError) Format(s fmt.State, verb rune) {
	switch verb {
	case 'v':
		if s.Flag('+') {
			fmt.Fprint(s, base.MsgPrefix+":") // nolint: errcheck
			fmt.Fprintf(s, "%+v", base.error)
			return
		}
		fallthrough
	case 's':
		fmt.Fprint(s, base.Error())
	case 'q':
		fmt.Fprintf(s, "%q", base.Error())
	}
}

func WrapError(err error) error {
	if err == nil {
		err = errors.New("")
	}
	return errors.AddStack(err)
}

func (base BaseError) Is(target error) bool {
	if val, ok := target.(BaseError); ok {
		return base.Code == val.Code
	}
	return false
}

func NewObjectNotExistsError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusNotFound,
		MsgPrefix: "Object Not Exists",
		Code:      "ObjectNotExists",
	}
}

func NewInvalidInputError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "Invalid Input Error",
		Code:      "InvalidInputError",
	}
}

func NewSystemError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusInternalServerError,
		MsgPrefix: "System Internal Error",
		Code:      "SystemInternalError",
	}
}

func NewNotImplementedError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusInternalServerError,
		MsgPrefix: "System Not Implemented",
		Code:      "NotImplementedError",
	}
}

func NewNotCostableError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "Unit Could Not Cost",
		Code:      "NotCostableError",
	}
}

func NewInvoiceNotConfirmedError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "Invoice not confirmed",
		Code:      "InvoiceNotConfirmedError",
	}
}

func NewInvoiceNoCosts(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "Invoice generated no costs",
		Code:      "InvoiceNoCosts",
	}
}

func NewDeletePackageAlreadyBinded(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "delete package still have users binded",
		Code:      "DeletePackageAlreadyBinded",
	}
}

func NewObjectAlreadyExistsError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "Object Already Exists",
		Code:      "ObjectAlreadyExists",
	}
}

var ObjectNotExistsError = NewObjectNotExistsError(nil)
var InvalidInputError = NewInvalidInputError(nil)
var SystemError = NewSystemError(nil)
var NotImplementedError = NewNotImplementedError(nil)
var NotCostableError = NewNotCostableError(nil)
var InvoiceNotConfirmedError = NewInvoiceNotConfirmedError(nil)
var InvoiceMonthlyNoCosts = NewInvoiceNoCosts(nil)
var DeletePackageAlreadyBinded = NewDeletePackageAlreadyBinded(nil)
var ObjectAlreadyExists = NewObjectAlreadyExistsError(nil)
