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 {
	if base.MsgPrefix == "" {
		return fmt.Sprintf("%v", base.error.Error())
	}
	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 WrapErrorNoStack(err error) error {
	if err == nil {
		err = errors.New("")
	}
	return 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 NewDuplicateRequestError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusNotFound,
		MsgPrefix: "Duplicate Request",
		Code:      "DuplicateRequest",
	}
}

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

func NewForbiddenError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusForbidden,
		MsgPrefix: "Forbidden",
		Code:      "ForbiddenError",
	}
}

func NewForbiddenForPartnerManagedCustomersError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusForbidden,
		MsgPrefix: "This api is not available for partner managed customers",
		Code:      "PartnerManagedCustomerPermissionDeny",
	}
}

func NewCreditCardForbiddenError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusForbidden,
		MsgPrefix: "Forbidden",
		Code:      "ForbiddenError",
	}
}

func NewTooManyRequestError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusTooManyRequests,
		MsgPrefix: "Too Many Requests",
		Code:      "TooManyRequests",
	}
}

func NewMeasureDelayError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "Delay Measure Error",
		Code:      "MeasureDelayError",
	}
}

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

func NewSalesforceError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusInternalServerError,
		MsgPrefix: "Salesforce Error",
		Code:      "SalesforceError",
	}
}

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

func NewInvalidStatusError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "Invalid Status",
		Code:      "InvalidStatus",
	}
}

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 NewInvoiceSplit(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusMovedPermanently,
		MsgPrefix: "InvoiceSplit",
		Code:      "InvoiceSplit",
	}
}

func NewCreditsPackageAlreadyInactive(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "Credits package already inactive",
		Code:      "CreditsPackageAlreadyInactive",
	}
}

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",
	}
}

func NewContractActionForbiddenError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusForbidden,
		MsgPrefix: "Action is forbidden",
		Code:      "ContractActionForbidden",
	}
}

func NewCardVerificationError(cause error, code string) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "",
		Code:      code,
	}
}

func NewConfirmPaymentIntentError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "confirm paymentIntent error",
		Code:      "PaymentIntentConfirmError",
	}
}

func NewConfirmPaymentIntentFail(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "confirm paymentIntent fail",
		Code:      "PaymentIntentConfirmFail",
	}
}

func NewCreditCardValidateForbidden(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "not allowed to card validate",
		Code:      "CreditCardValidateForbidden",
	}
}

func NewFrequentSendMeteringError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "can not frequent send invoice",
		Code:      "FrequentSendMeteringError",
	}
}

func NewUninvoiceableError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "UninvoiceableError",
		Code:      "UninvoiceableError",
	}
}

func NewRemeasureError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusConflict,
		MsgPrefix: "measure need re-process",
		Code:      "RemeasureError",
	}
}

func NewAPIOfflineError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusNotFound,
		MsgPrefix: "The API is no longer in service.",
		Code:      "APIOfflineError",
	}
}

func NewSystemBusyError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusRequestTimeout,
		MsgPrefix: "Sorry, the system is busy now",
		Code:      "SystemBusyError",
	}
}

func NewInvalidBillingProfileInputError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusBadRequest,
		MsgPrefix: "Invalid Billing Profile",
		Code:      "InvalidBillingProfileInputError",
	}
}
func NewActionForbiddenError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusForbidden,
		MsgPrefix: "Action is forbidden",
		Code:      "ActionForbidden",
	}
}

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

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

func NewTimeRangeConflictedError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusConflict,
		MsgPrefix: "time range conflicted",
		Code:      "TimeRangeConfilictedError",
	}
}

func NewUnitFilterCopyArgsConflictedError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusConflict,
		MsgPrefix: "copy args conflicted",
		Code:      "UnitFilterCopyArgsConflicted",
	}
}

var ObjectNotExistsError = NewObjectNotExistsError(nil)
var DuplicateRequestError = NewDuplicateRequestError(nil)
var InvalidInputError = NewInvalidInputError(nil)
var ForbiddenError = NewForbiddenError(nil)
var SystemError = NewSystemError(nil)
var NotImplementedError = NewNotImplementedError(nil)
var InvoiceNoCostsError = NewInvoiceNoCosts(nil)
var NotCostableError = NewNotCostableError(nil)
var InvoiceNotConfirmedError = NewInvoiceNotConfirmedError(nil)
var InvoiceMonthlyNoCosts = NewInvoiceNoCosts(nil)
var DeletePackageAlreadyBinded = NewDeletePackageAlreadyBinded(nil)
var ObjectAlreadyExists = NewObjectAlreadyExistsError(nil)
var CreditsPackageAlreadyInactive = NewCreditsPackageAlreadyInactive(nil)
var ContractActionForbidden = NewContractActionForbiddenError(nil)
var ConfirmPaymentIntentFail = NewConfirmPaymentIntentFail(nil)
var InvalidStatusError = NewInvalidStatusError(nil)
var SalesforceError = NewSalesforceError(nil)
var OutOfMaxBindedTimesError = NewInvalidInputError(errors.New("package binded more than tenant_max_binded_times"))
var FrequentSendMeteringError = NewFrequentSendMeteringError(nil)
var UninvoiceableError = NewUninvoiceableError(nil)
var InvoiceSplit = NewInvoiceSplit(nil)
var RemeasureError = NewRemeasureError(nil)
var APIOfflineError = NewAPIOfflineError(nil)
var InvalidBillingProfileInputError = NewInvalidBillingProfileInputError(nil)
var MeasureDelayError = NewMeasureDelayError(nil)
var MergedTargetMissingError = NewMergedTargetMissingError(nil)
var CostTargetExecutedError = NewCostTargetExecutedError(nil)
var ActionForbidden = NewActionForbiddenError(nil)
var TimeTimeRangeConflicted = NewTimeRangeConflictedError(nil)
var ForbiddenForPartnerManagedCustomersError = NewForbiddenForPartnerManagedCustomersError(errors.New("contact your sales or our support for more information"))

// NewPayingAlreadyActiveError is returned when a Finance click tries to create
// a paying task for a month that already has an active (pending/running) one.
func NewPayingAlreadyActiveError(cause error) error {
	return BaseError{
		error:     WrapError(cause),
		Status:    http.StatusConflict,
		MsgPrefix: "an active paying already exists for this month",
		Code:      "PayingAlreadyActive",
	}
}

var PayingAlreadyActiveError = NewPayingAlreadyActiveError(nil)
