package pointers_errors

import (
	"errors"
	"fmt"
)

// The var keyword allows us to define values global to the package.
var ErrInsufficientFunds = errors.New("cannot withdraw, insufficient funds")

type Stringer interface {
	String() string
}

// creating a new type from an existing on
// The idea being it could be more descriptive
// but also you can declare methods on them
// which can be used to add domain specific functionality
type Bitcoin int

// this let's us use fmt strings with %s on the Bitcoin type
func (b Bitcoin) String() string {
	return fmt.Sprintf("%d BTC", b)
}

type Wallet struct {
	balance Bitcoin
}

func (w *Wallet) Deposit(amount Bitcoin) {
	// Need a pointer because the func args are copied
	// so w Wallet is a copy of wallet. Not the actually refernce to the
	// wallet that Deposit was called on.

	// Notice we don't have to derefence the pointer like in Balance because
	// in go https://go.dev/ref/spec#Method_values struct pointers are automatically
	// derefenced
	w.balance += amount
}

// Technically you do not need to change Balance to use a pointer receiver as taking a copy of the balance is fine. However, by convention you should keep your method receiver types the same for consistency.

func (w *Wallet) Balance() Bitcoin {
	// dereferences the pointer
	// which as stated above didn't need to happen because go automatically
	// dereferences struct pointers.
	return (*w).balance
}

func (w *Wallet) Withdraw(amount Bitcoin) error {
	if amount > w.balance {
		return ErrInsufficientFunds
	}
	w.balance -= amount
	return nil
}
