package zpay32

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcutil/bech32"
	"github.com/lightningnetwork/lnd/lnwire"
)

const (
	// mSatPerBtc is the number of millisatoshis in 1 BTC.
	mSatPerBtc = 100000000000

	// signatureBase32Len is the number of 5-bit groups needed to encode
	// the 512 bit signature + 8 bit recovery ID.
	signatureBase32Len = 104

	// timestampBase32Len is the number of 5-bit groups needed to encode
	// the 35-bit timestamp.
	timestampBase32Len = 7

	// hashBase32Len is the number of 5-bit groups needed to encode a
	// 256-bit hash. Note that the last group will be padded with zeroes.
	hashBase32Len = 52

	// pubKeyBase32Len is the number of 5-bit groups needed to encode a
	// 33-byte compressed pubkey. Note that the last group will be padded
	// with zeroes.
	pubKeyBase32Len = 53

	// hopHintLen is the number of bytes needed to encode the hop hint of a
	// single private route.
	hopHintLen = 51

	// The following byte values correspond to the supported field types.
	// The field name is the character representing that 5-bit value in the
	// bech32 string.

	// fieldTypeP is the field containing the payment hash.
	fieldTypeP = 1

	// fieldTypeD contains a short description of the payment.
	fieldTypeD = 13

	// fieldTypeN contains the pubkey of the target node.
	fieldTypeN = 19

	// fieldTypeH contains the hash of a description of the payment.
	fieldTypeH = 23

	// fieldTypeX contains the expiry in seconds of the invoice.
	fieldTypeX = 6

	// fieldTypeF contains a fallback on-chain address.
	fieldTypeF = 9

	// fieldTypeR contains extra routing information.
	fieldTypeR = 3

	// fieldTypeC contains an optional requested final CLTV delta.
	fieldTypeC = 24

	// fieldType9 contains one or more bytes for signaling features
	// supported or required by the receiver.
	fieldType9 = 5

	// fieldTypeS contains a 32-byte payment address, which is a nonce
	// included in the final hop's payload to prevent intermediaries from
	// probing the recipient.
	fieldTypeS = 16

	// maxInvoiceLength is the maximum total length an invoice can have.
	// This is chosen to be the maximum number of bytes that can fit into a
	// single QR code: https://en.wikipedia.org/wiki/QR_code#Storage
	maxInvoiceLength = 7089

	// DefaultInvoiceExpiry is the default expiry duration from the creation
	// timestamp if expiry is set to zero.
	DefaultInvoiceExpiry = time.Hour
)

var (
	// ErrInvoiceTooLarge is returned when an invoice exceeds
	// maxInvoiceLength.
	ErrInvoiceTooLarge = errors.New("invoice is too large")

	// ErrInvalidFieldLength is returned when a tagged field was specified
	// with a length larger than the left over bytes of the data field.
	ErrInvalidFieldLength = errors.New("invalid field length")

	// ErrBrokenTaggedField is returned when the last tagged field is
	// incorrectly formatted and doesn't have enough bytes to be read.
	ErrBrokenTaggedField = errors.New("last tagged field is broken")
)

// MessageSigner is passed to the Encode method to provide a signature
// corresponding to the node's pubkey.
type MessageSigner struct {
	// SignCompact signs the passed hash with the node's privkey. The
	// returned signature should be 65 bytes, where the last 64 are the
	// compact signature, and the first one is a header byte. This is the
	// format returned by btcec.SignCompact.
	SignCompact func(hash []byte) ([]byte, error)
}

// Invoice represents a decoded invoice, or to-be-encoded invoice. Some of the
// fields are optional, and will only be non-nil if the invoice this was parsed
// from contains that field. When encoding, only the non-nil fields will be
// added to the encoded invoice.
type Invoice struct {
	// Net specifies what network this Lightning invoice is meant for.
	Net *chaincfg.Params

	// MilliSat specifies the amount of this invoice in millisatoshi.
	// Optional.
	MilliSat *lnwire.MilliSatoshi

	// Timestamp specifies the time this invoice was created.
	// Mandatory
	Timestamp time.Time

	// PaymentHash is the payment hash to be used for a payment to this
	// invoice.
	PaymentHash *[32]byte

	// PaymentAddr is the payment address to be used by payments to prevent
	// probing of the destination.
	PaymentAddr *[32]byte

	// Destination is the public key of the target node. This will always
	// be set after decoding, and can optionally be set before encoding to
	// include the pubkey as an 'n' field. If this is not set before
	// encoding then the destination pubkey won't be added as an 'n' field,
	// and the pubkey will be extracted from the signature during decoding.
	Destination *btcec.PublicKey

	// minFinalCLTVExpiry is the value that the creator of the invoice
	// expects to be used for the CLTV expiry of the HTLC extended to it in
	// the last hop.
	//
	// NOTE: This value is optional, and should be set to nil if the
	// invoice creator doesn't have a strong requirement on the CLTV expiry
	// of the final HTLC extended to it.
	//
	// This field is un-exported and can only be read by the
	// MinFinalCLTVExpiry() method. By forcing callers to read via this
	// method, we can easily enforce the default if not specified.
	minFinalCLTVExpiry *uint64

	// Description is a short description of the purpose of this invoice.
	// Optional. Non-nil iff DescriptionHash is nil.
	Description *string

	// DescriptionHash is the SHA256 hash of a description of the purpose of
	// this invoice.
	// Optional. Non-nil iff Description is nil.
	DescriptionHash *[32]byte

	// expiry specifies the timespan this invoice will be valid.
	// Optional. If not set, a default expiry of 60 min will be implied.
	//
	// This field is unexported and can be read by the Expiry() method. This
	// method makes sure the default expiry time is returned in case the
	// field is not set.
	expiry *time.Duration

	// FallbackAddr is an on-chain address that can be used for payment in
	// case the Lightning payment fails.
	// Optional.
	FallbackAddr btcutil.Address

	// RouteHints represents one or more different route hints. Each route
	// hint can be individually used to reach the destination. These usually
	// represent private routes.
	//
	// NOTE: This is optional.
	RouteHints [][]HopHint

	// Features represents an optional field used to signal optional or
	// required support for features by the receiver.
	Features *lnwire.FeatureVector
}

// Amount is a functional option that allows callers of NewInvoice to set the
// amount in millisatoshis that the Invoice should encode.
func Amount(milliSat lnwire.MilliSatoshi) func(*Invoice) {
	return func(i *Invoice) {
		i.MilliSat = &milliSat
	}
}

// Destination is a functional option that allows callers of NewInvoice to
// explicitly set the pubkey of the Invoice's destination node.
func Destination(destination *btcec.PublicKey) func(*Invoice) {
	return func(i *Invoice) {
		i.Destination = destination
	}
}

// Description is a functional option that allows callers of NewInvoice to set
// the payment description of the created Invoice.
//
// NOTE: Must be used if and only if DescriptionHash is not used.
func Description(description string) func(*Invoice) {
	return func(i *Invoice) {
		i.Description = &description
	}
}

// CLTVExpiry is an optional value which allows the receiver of the payment to
// specify the delta between the current height and the HTLC extended to the
// receiver.
func CLTVExpiry(delta uint64) func(*Invoice) {
	return func(i *Invoice) {
		i.minFinalCLTVExpiry = &delta
	}
}

// DescriptionHash is a functional option that allows callers of NewInvoice to
// set the payment description hash of the created Invoice.
//
// NOTE: Must be used if and only if Description is not used.
func DescriptionHash(descriptionHash [32]byte) func(*Invoice) {
	return func(i *Invoice) {
		i.DescriptionHash = &descriptionHash
	}
}

// Expiry is a functional option that allows callers of NewInvoice to set the
// expiry of the created Invoice. If not set, a default expiry of 60 min will
// be implied.
func Expiry(expiry time.Duration) func(*Invoice) {
	return func(i *Invoice) {
		i.expiry = &expiry
	}
}

// FallbackAddr is a functional option that allows callers of NewInvoice to set
// the Invoice's fallback on-chain address that can be used for payment in case
// the Lightning payment fails
func FallbackAddr(fallbackAddr btcutil.Address) func(*Invoice) {
	return func(i *Invoice) {
		i.FallbackAddr = fallbackAddr
	}
}

// RouteHint is a functional option that allows callers of NewInvoice to add
// one or more hop hints that represent a private route to the destination.
func RouteHint(routeHint []HopHint) func(*Invoice) {
	return func(i *Invoice) {
		i.RouteHints = append(i.RouteHints, routeHint)
	}
}

// Features is a functional option that allows callers of NewInvoice to set the
// desired feature bits that are advertised on the invoice. If this option is
// not used, an empty feature vector will automatically be populated.
func Features(features *lnwire.FeatureVector) func(*Invoice) {
	return func(i *Invoice) {
		i.Features = features
	}
}

// PaymentAddr is a functional option that allows callers of NewInvoice to set
// the desired payment address tht is advertised on the invoice.
func PaymentAddr(addr [32]byte) func(*Invoice) {
	return func(i *Invoice) {
		i.PaymentAddr = &addr
	}
}

// NewInvoice creates a new Invoice object. The last parameter is a set of
// variadic arguments for setting optional fields of the invoice.
//
// NOTE: Either Description  or DescriptionHash must be provided for the Invoice
// to be considered valid.
func NewInvoice(net *chaincfg.Params, paymentHash [32]byte,
	timestamp time.Time, options ...func(*Invoice)) (*Invoice, error) {

	invoice := &Invoice{
		Net:         net,
		PaymentHash: &paymentHash,
		Timestamp:   timestamp,
	}

	for _, option := range options {
		option(invoice)
	}

	// If no features were set, we'll populate an empty feature vector.
	if invoice.Features == nil {
		invoice.Features = lnwire.NewFeatureVector(
			nil, lnwire.Features,
		)
	}

	if err := validateInvoice(invoice); err != nil {
		return nil, err
	}

	return invoice, nil
}

// Decode parses the provided encoded invoice and returns a decoded Invoice if
// it is valid by BOLT-0011 and matches the provided active network.
func Decode(invoice string, net *chaincfg.Params) (*Invoice, error) {
	decodedInvoice := Invoice{}

	// Before bech32 decoding the invoice, make sure that it is not too large.
	// This is done as an anti-DoS measure since bech32 decoding is expensive.
	if len(invoice) > maxInvoiceLength {
		return nil, ErrInvoiceTooLarge
	}

	// Decode the invoice using the modified bech32 decoder.
	hrp, data, err := decodeBech32(invoice)
	if err != nil {
		return nil, err
	}

	// We expect the human-readable part to at least have ln + one char
	// encoding the network.
	if len(hrp) < 3 {
		return nil, fmt.Errorf("hrp too short")
	}

	// First two characters of HRP should be "ln".
	if hrp[:2] != "ln" {
		return nil, fmt.Errorf("prefix should be \"ln\"")
	}

	// The next characters should be a valid prefix for a segwit BIP173
	// address that match the active network.
	if !strings.HasPrefix(hrp[2:], net.Bech32HRPSegwit) {
		return nil, fmt.Errorf(
			"invoice not for current active network '%s'", net.Name)
	}
	decodedInvoice.Net = net

	// Optionally, if there's anything left of the HRP after ln + the segwit
	// prefix, we try to decode this as the payment amount.
	var netPrefixLength = len(net.Bech32HRPSegwit) + 2
	if len(hrp) > netPrefixLength {
		amount, err := decodeAmount(hrp[netPrefixLength:])
		if err != nil {
			return nil, err
		}
		decodedInvoice.MilliSat = &amount
	}

	// Everything except the last 520 bits of the data encodes the invoice's
	// timestamp and tagged fields.
	invoiceData := data[:len(data)-signatureBase32Len]

	// Parse the timestamp and tagged fields, and fill the Invoice struct.
	if err := parseData(&decodedInvoice, invoiceData, net); err != nil {
		return nil, err
	}

	// The last 520 bits (104 groups) make up the signature.
	sigBase32 := data[len(data)-signatureBase32Len:]
	sigBase256, err := bech32.ConvertBits(sigBase32, 5, 8, true)
	if err != nil {
		return nil, err
	}
	var sig lnwire.Sig
	copy(sig[:], sigBase256[:64])
	recoveryID := sigBase256[64]

	// The signature is over the hrp + the data the invoice, encoded in
	// base 256.
	taggedDataBytes, err := bech32.ConvertBits(invoiceData, 5, 8, true)
	if err != nil {
		return nil, err
	}

	toSign := append([]byte(hrp), taggedDataBytes...)

	// We expect the signature to be over the single SHA-256 hash of that
	// data.
	hash := chainhash.HashB(toSign)

	// If the destination pubkey was provided as a tagged field, use that
	// to verify the signature, if not do public key recovery.
	if decodedInvoice.Destination != nil {
		signature, err := sig.ToSignature()
		if err != nil {
			return nil, fmt.Errorf("unable to deserialize "+
				"signature: %v", err)
		}
		if !signature.Verify(hash, decodedInvoice.Destination) {
			return nil, fmt.Errorf("invalid invoice signature")
		}
	} else {
		headerByte := recoveryID + 27 + 4
		compactSign := append([]byte{headerByte}, sig[:]...)
		pubkey, _, err := btcec.RecoverCompact(btcec.S256(),
			compactSign, hash)
		if err != nil {
			return nil, err
		}
		decodedInvoice.Destination = pubkey
	}

	// If no feature vector was decoded, populate an empty one.
	if decodedInvoice.Features == nil {
		decodedInvoice.Features = lnwire.NewFeatureVector(
			nil, lnwire.Features,
		)
	}

	// Now that we have created the invoice, make sure it has the required
	// fields set.
	if err := validateInvoice(&decodedInvoice); err != nil {
		return nil, err
	}

	return &decodedInvoice, nil
}

// Encode takes the given MessageSigner and returns a string encoding this
// invoice signed by the node key of the signer.
func (invoice *Invoice) Encode(signer MessageSigner) (string, error) {
	// First check that this invoice is valid before starting the encoding.
	if err := validateInvoice(invoice); err != nil {
		return "", err
	}

	// The buffer will encoded the invoice data using 5-bit groups (base32).
	var bufferBase32 bytes.Buffer

	// The timestamp will be encoded using 35 bits, in base32.
	timestampBase32 := uint64ToBase32(uint64(invoice.Timestamp.Unix()))

	// The timestamp must be exactly 35 bits, which means 7 groups. If it
	// can fit into fewer groups we add leading zero groups, if it is too
	// big we fail early, as there is not possible to encode it.
	if len(timestampBase32) > timestampBase32Len {
		return "", fmt.Errorf("timestamp too big: %d",
			invoice.Timestamp.Unix())
	}

	// Add zero bytes to the first timestampBase32Len-len(timestampBase32)
	// groups, then add the non-zero groups.
	zeroes := make([]byte, timestampBase32Len-len(timestampBase32),
		timestampBase32Len-len(timestampBase32))
	_, err := bufferBase32.Write(zeroes)
	if err != nil {
		return "", fmt.Errorf("unable to write to buffer: %v", err)
	}
	_, err = bufferBase32.Write(timestampBase32)
	if err != nil {
		return "", fmt.Errorf("unable to write to buffer: %v", err)
	}

	// We now write the tagged fields to the buffer, which will fill the
	// rest of the data part before the signature.
	if err := writeTaggedFields(&bufferBase32, invoice); err != nil {
		return "", err
	}

	// The human-readable part (hrp) is "ln" + net hrp + optional amount.
	hrp := "ln" + invoice.Net.Bech32HRPSegwit
	if invoice.MilliSat != nil {
		// Encode the amount using the fewest possible characters.
		am, err := encodeAmount(*invoice.MilliSat)
		if err != nil {
			return "", err
		}
		hrp += am
	}

	// The signature is over the single SHA-256 hash of the hrp + the
	// tagged fields encoded in base256.
	taggedFieldsBytes, err := bech32.ConvertBits(bufferBase32.Bytes(), 5, 8, true)
	if err != nil {
		return "", err
	}

	toSign := append([]byte(hrp), taggedFieldsBytes...)
	hash := chainhash.HashB(toSign)

	// We use compact signature format, and also encoded the recovery ID
	// such that a reader of the invoice can recover our pubkey from the
	// signature.
	sign, err := signer.SignCompact(hash)
	if err != nil {
		return "", err
	}

	// From the header byte we can extract the recovery ID, and the last 64
	// bytes encode the signature.
	recoveryID := sign[0] - 27 - 4
	var sig lnwire.Sig
	copy(sig[:], sign[1:])

	// If the pubkey field was explicitly set, it must be set to the pubkey
	// used to create the signature.
	if invoice.Destination != nil {
		signature, err := sig.ToSignature()
		if err != nil {
			return "", fmt.Errorf("unable to deserialize "+
				"signature: %v", err)
		}

		valid := signature.Verify(hash, invoice.Destination)
		if !valid {
			return "", fmt.Errorf("signature does not match " +
				"provided pubkey")
		}
	}

	// Convert the signature to base32 before writing it to the buffer.
	signBase32, err := bech32.ConvertBits(append(sig[:], recoveryID), 8, 5, true)
	if err != nil {
		return "", err
	}
	bufferBase32.Write(signBase32)

	// Now we can create the bech32 encoded string from the base32 buffer.
	b32, err := bech32.Encode(hrp, bufferBase32.Bytes())
	if err != nil {
		return "", err
	}

	// Before returning, check that the bech32 encoded string is not greater
	// than our largest supported invoice size.
	if len(b32) > maxInvoiceLength {
		return "", ErrInvoiceTooLarge
	}

	return b32, nil
}

// Expiry returns the expiry time for this invoice. If expiry time is not set
// explicitly, the default 3600 second expiry will be returned.
func (invoice *Invoice) Expiry() time.Duration {
	if invoice.expiry != nil {
		return *invoice.expiry
	}

	// If no expiry is set for this invoice, default is 3600 seconds.
	return DefaultInvoiceExpiry
}

// MinFinalCLTVExpiry returns the minimum final CLTV expiry delta as specified
// by the creator of the invoice. This value specifies the delta between the
// current height and the expiry height of the HTLC extended in the last hop.
func (invoice *Invoice) MinFinalCLTVExpiry() uint64 {
	if invoice.minFinalCLTVExpiry != nil {
		return *invoice.minFinalCLTVExpiry
	}

	return DefaultFinalCLTVDelta
}

// validateInvoice does a sanity check of the provided Invoice, making sure it
// has all the necessary fields set for it to be considered valid by BOLT-0011.
func validateInvoice(invoice *Invoice) error {
	// The net must be set.
	if invoice.Net == nil {
		return fmt.Errorf("net params not set")
	}

	// Ensure that if there is an amount set, it is not negative.
	if invoice.MilliSat != nil && *invoice.MilliSat < 0 {
		return fmt.Errorf("negative amount: %v", *invoice.MilliSat)
	}

	// The invoice must contain a payment hash.
	if invoice.PaymentHash == nil {
		return fmt.Errorf("no payment hash found")
	}

	// Either Description or DescriptionHash must be set, not both.
	if invoice.Description != nil && invoice.DescriptionHash != nil {
		return fmt.Errorf("both description and description hash set")
	}
	if invoice.Description == nil && invoice.DescriptionHash == nil {
		return fmt.Errorf("neither description nor description hash set")
	}

	// Check that we support the field lengths.
	if len(invoice.PaymentHash) != 32 {
		return fmt.Errorf("unsupported payment hash length: %d",
			len(invoice.PaymentHash))
	}

	if invoice.DescriptionHash != nil && len(invoice.DescriptionHash) != 32 {
		return fmt.Errorf("unsupported description hash length: %d",
			len(invoice.DescriptionHash))
	}

	if invoice.Destination != nil &&
		len(invoice.Destination.SerializeCompressed()) != 33 {
		return fmt.Errorf("unsupported pubkey length: %d",
			len(invoice.Destination.SerializeCompressed()))
	}

	// Ensure that all invoices have feature vectors.
	if invoice.Features == nil {
		return fmt.Errorf("missing feature vector")
	}

	return nil
}

// parseData parses the data part of the invoice. It expects base32 data
// returned from the bech32.Decode method, except signature.
func parseData(invoice *Invoice, data []byte, net *chaincfg.Params) error {
	// It must contain the timestamp, encoded using 35 bits (7 groups).
	if len(data) < timestampBase32Len {
		return fmt.Errorf("data too short: %d", len(data))
	}

	t, err := parseTimestamp(data[:timestampBase32Len])
	if err != nil {
		return err
	}
	invoice.Timestamp = time.Unix(int64(t), 0)

	// The rest are tagged parts.
	tagData := data[7:]
	return parseTaggedFields(invoice, tagData, net)
}

// parseTimestamp converts a 35-bit timestamp (encoded in base32) to uint64.
func parseTimestamp(data []byte) (uint64, error) {
	if len(data) != timestampBase32Len {
		return 0, fmt.Errorf("timestamp must be 35 bits, was %d",
			len(data)*5)
	}

	return base32ToUint64(data)
}

// parseTaggedFields takes the base32 encoded tagged fields of the invoice, and
// fills the Invoice struct accordingly.
func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) error {
	index := 0
	for len(fields)-index > 0 {
		// If there are less than 3 groups to read, there cannot be more
		// interesting information, as we need the type (1 group) and
		// length (2 groups).
		//
		// This means the last tagged field is broken.
		if len(fields)-index < 3 {
			return ErrBrokenTaggedField
		}

		typ := fields[index]
		dataLength, err := parseFieldDataLength(fields[index+1 : index+3])
		if err != nil {
			return err
		}

		// If we don't have enough field data left to read this length,
		// return error.
		if len(fields) < index+3+int(dataLength) {
			return ErrInvalidFieldLength
		}
		base32Data := fields[index+3 : index+3+int(dataLength)]

		// Advance the index in preparation for the next iteration.
		index += 3 + int(dataLength)

		switch typ {
		case fieldTypeP:
			if invoice.PaymentHash != nil {
				// We skip the field if we have already seen a
				// supported one.
				continue
			}

			invoice.PaymentHash, err = parse32Bytes(base32Data)
		case fieldTypeS:
			if invoice.PaymentAddr != nil {
				// We skip the field if we have already seen a
				// supported one.
				continue
			}

			invoice.PaymentAddr, err = parse32Bytes(base32Data)
		case fieldTypeD:
			if invoice.Description != nil {
				// We skip the field if we have already seen a
				// supported one.
				continue
			}

			invoice.Description, err = parseDescription(base32Data)
		case fieldTypeN:
			if invoice.Destination != nil {
				// We skip the field if we have already seen a
				// supported one.
				continue
			}

			invoice.Destination, err = parseDestination(base32Data)
		case fieldTypeH:
			if invoice.DescriptionHash != nil {
				// We skip the field if we have already seen a
				// supported one.
				continue
			}

			invoice.DescriptionHash, err = parse32Bytes(base32Data)
		case fieldTypeX:
			if invoice.expiry != nil {
				// We skip the field if we have already seen a
				// supported one.
				continue
			}

			invoice.expiry, err = parseExpiry(base32Data)
		case fieldTypeC:
			if invoice.minFinalCLTVExpiry != nil {
				// We skip the field if we have already seen a
				// supported one.
				continue
			}

			invoice.minFinalCLTVExpiry, err = parseMinFinalCLTVExpiry(base32Data)
		case fieldTypeF:
			if invoice.FallbackAddr != nil {
				// We skip the field if we have already seen a
				// supported one.
				continue
			}

			invoice.FallbackAddr, err = parseFallbackAddr(base32Data, net)
		case fieldTypeR:
			// An `r` field can be included in an invoice multiple
			// times, so we won't skip it if we have already seen
			// one.
			routeHint, err := parseRouteHint(base32Data)
			if err != nil {
				return err
			}

			invoice.RouteHints = append(invoice.RouteHints, routeHint)
		case fieldType9:
			if invoice.Features != nil {
				// We skip the field if we have already seen a
				// supported one.
				continue
			}

			invoice.Features, err = parseFeatures(base32Data)
		default:
			// Ignore unknown type.
		}

		// Check if there was an error from parsing any of the tagged
		// fields and return it.
		if err != nil {
			return err
		}
	}

	return nil
}

// parseFieldDataLength converts the two byte slice into a uint16.
func parseFieldDataLength(data []byte) (uint16, error) {
	if len(data) != 2 {
		return 0, fmt.Errorf("data length must be 2 bytes, was %d",
			len(data))
	}

	return uint16(data[0])<<5 | uint16(data[1]), nil
}

// parse32Bytes converts a 256-bit value (encoded in base32) to *[32]byte. This
// can be used for payment hashes, description hashes, payment addresses, etc.
func parse32Bytes(data []byte) (*[32]byte, error) {
	var paymentHash [32]byte

	// As BOLT-11 states, a reader must skip over the 32-byte fields if
	// it does not have a length of 52, so avoid returning an error.
	if len(data) != hashBase32Len {
		return nil, nil
	}

	hash, err := bech32.ConvertBits(data, 5, 8, false)
	if err != nil {
		return nil, err
	}

	copy(paymentHash[:], hash[:])

	return &paymentHash, nil
}

// parseDescription converts the data (encoded in base32) into a string to use
// as the description.
func parseDescription(data []byte) (*string, error) {
	base256Data, err := bech32.ConvertBits(data, 5, 8, false)
	if err != nil {
		return nil, err
	}

	description := string(base256Data)

	return &description, nil
}

// parseDestination converts the data (encoded in base32) into a 33-byte public
// key of the payee node.
func parseDestination(data []byte) (*btcec.PublicKey, error) {
	// As BOLT-11 states, a reader must skip over the destination field
	// if it does not have a length of 53, so avoid returning an error.
	if len(data) != pubKeyBase32Len {
		return nil, nil
	}

	base256Data, err := bech32.ConvertBits(data, 5, 8, false)
	if err != nil {
		return nil, err
	}

	return btcec.ParsePubKey(base256Data, btcec.S256())
}

// parseExpiry converts the data (encoded in base32) into the expiry time.
func parseExpiry(data []byte) (*time.Duration, error) {
	expiry, err := base32ToUint64(data)
	if err != nil {
		return nil, err
	}

	duration := time.Duration(expiry) * time.Second

	return &duration, nil
}

// parseMinFinalCLTVExpiry converts the data (encoded in base32) into a uint64
// to use as the minFinalCLTVExpiry.
func parseMinFinalCLTVExpiry(data []byte) (*uint64, error) {
	expiry, err := base32ToUint64(data)
	if err != nil {
		return nil, err
	}

	return &expiry, nil
}

// parseFallbackAddr converts the data (encoded in base32) into a fallback
// on-chain address.
func parseFallbackAddr(data []byte, net *chaincfg.Params) (btcutil.Address, error) {
	// Checks if the data is empty or contains a version without an address.
	if len(data) < 2 {
		return nil, fmt.Errorf("empty fallback address field")
	}

	var addr btcutil.Address

	version := data[0]
	switch version {
	case 0:
		witness, err := bech32.ConvertBits(data[1:], 5, 8, false)
		if err != nil {
			return nil, err
		}

		switch len(witness) {
		case 20:
			addr, err = btcutil.NewAddressWitnessPubKeyHash(witness, net)
		case 32:
			addr, err = btcutil.NewAddressWitnessScriptHash(witness, net)
		default:
			return nil, fmt.Errorf("unknown witness program length %d",
				len(witness))
		}

		if err != nil {
			return nil, err
		}
	case 17:
		pubKeyHash, err := bech32.ConvertBits(data[1:], 5, 8, false)
		if err != nil {
			return nil, err
		}

		addr, err = btcutil.NewAddressPubKeyHash(pubKeyHash, net)
		if err != nil {
			return nil, err
		}
	case 18:
		scriptHash, err := bech32.ConvertBits(data[1:], 5, 8, false)
		if err != nil {
			return nil, err
		}

		addr, err = btcutil.NewAddressScriptHashFromHash(scriptHash, net)
		if err != nil {
			return nil, err
		}
	default:
		// Ignore unknown version.
	}

	return addr, nil
}

// parseRouteHint converts the data (encoded in base32) into an array containing
// one or more routing hop hints that represent a single route hint.
func parseRouteHint(data []byte) ([]HopHint, error) {
	base256Data, err := bech32.ConvertBits(data, 5, 8, false)
	if err != nil {
		return nil, err
	}

	// Check that base256Data is a multiple of hopHintLen.
	if len(base256Data)%hopHintLen != 0 {
		return nil, fmt.Errorf("expected length multiple of %d bytes, "+
			"got %d", hopHintLen, len(base256Data))
	}

	var routeHint []HopHint

	for len(base256Data) > 0 {
		hopHint := HopHint{}
		hopHint.NodeID, err = btcec.ParsePubKey(base256Data[:33], btcec.S256())
		if err != nil {
			return nil, err
		}
		hopHint.ChannelID = binary.BigEndian.Uint64(base256Data[33:41])
		hopHint.FeeBaseMSat = binary.BigEndian.Uint32(base256Data[41:45])
		hopHint.FeeProportionalMillionths = binary.BigEndian.Uint32(base256Data[45:49])
		hopHint.CLTVExpiryDelta = binary.BigEndian.Uint16(base256Data[49:51])

		routeHint = append(routeHint, hopHint)

		base256Data = base256Data[51:]
	}

	return routeHint, nil
}

// parseFeatures decodes any feature bits directly from the base32
// representation.
func parseFeatures(data []byte) (*lnwire.FeatureVector, error) {
	rawFeatures := lnwire.NewRawFeatureVector()
	err := rawFeatures.DecodeBase32(bytes.NewReader(data), len(data))
	if err != nil {
		return nil, err
	}

	return lnwire.NewFeatureVector(rawFeatures, lnwire.Features), nil
}

// writeTaggedFields writes the non-nil tagged fields of the Invoice to the
// base32 buffer.
func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error {
	if invoice.PaymentHash != nil {
		err := writeBytes32(bufferBase32, fieldTypeP, *invoice.PaymentHash)
		if err != nil {
			return err
		}
	}

	if invoice.Description != nil {
		base32, err := bech32.ConvertBits([]byte(*invoice.Description),
			8, 5, true)
		if err != nil {
			return err
		}
		err = writeTaggedField(bufferBase32, fieldTypeD, base32)
		if err != nil {
			return err
		}
	}

	if invoice.DescriptionHash != nil {
		err := writeBytes32(
			bufferBase32, fieldTypeH, *invoice.DescriptionHash,
		)
		if err != nil {
			return err
		}
	}

	if invoice.minFinalCLTVExpiry != nil {
		finalDelta := uint64ToBase32(uint64(*invoice.minFinalCLTVExpiry))
		err := writeTaggedField(bufferBase32, fieldTypeC, finalDelta)
		if err != nil {
			return err
		}
	}

	if invoice.expiry != nil {
		seconds := invoice.expiry.Seconds()
		expiry := uint64ToBase32(uint64(seconds))
		err := writeTaggedField(bufferBase32, fieldTypeX, expiry)
		if err != nil {
			return err
		}
	}

	if invoice.FallbackAddr != nil {
		var version byte
		switch addr := invoice.FallbackAddr.(type) {
		case *btcutil.AddressPubKeyHash:
			version = 17
		case *btcutil.AddressScriptHash:
			version = 18
		case *btcutil.AddressWitnessPubKeyHash:
			version = addr.WitnessVersion()
		case *btcutil.AddressWitnessScriptHash:
			version = addr.WitnessVersion()
		default:
			return fmt.Errorf("unknown fallback address type")
		}
		base32Addr, err := bech32.ConvertBits(
			invoice.FallbackAddr.ScriptAddress(), 8, 5, true)
		if err != nil {
			return err
		}

		err = writeTaggedField(bufferBase32, fieldTypeF,
			append([]byte{version}, base32Addr...))
		if err != nil {
			return err
		}
	}

	for _, routeHint := range invoice.RouteHints {
		// Each hop hint is encoded using 51 bytes, so we'll make to
		// sure to allocate enough space for the whole route hint.
		routeHintBase256 := make([]byte, 0, hopHintLen*len(routeHint))

		for _, hopHint := range routeHint {
			hopHintBase256 := make([]byte, hopHintLen)
			copy(hopHintBase256[:33], hopHint.NodeID.SerializeCompressed())
			binary.BigEndian.PutUint64(
				hopHintBase256[33:41], hopHint.ChannelID,
			)
			binary.BigEndian.PutUint32(
				hopHintBase256[41:45], hopHint.FeeBaseMSat,
			)
			binary.BigEndian.PutUint32(
				hopHintBase256[45:49], hopHint.FeeProportionalMillionths,
			)
			binary.BigEndian.PutUint16(
				hopHintBase256[49:51], hopHint.CLTVExpiryDelta,
			)
			routeHintBase256 = append(routeHintBase256, hopHintBase256...)
		}

		routeHintBase32, err := bech32.ConvertBits(
			routeHintBase256, 8, 5, true,
		)
		if err != nil {
			return err
		}

		err = writeTaggedField(bufferBase32, fieldTypeR, routeHintBase32)
		if err != nil {
			return err
		}
	}

	if invoice.Destination != nil {
		// Convert 33 byte pubkey to 53 5-bit groups.
		pubKeyBase32, err := bech32.ConvertBits(
			invoice.Destination.SerializeCompressed(), 8, 5, true)
		if err != nil {
			return err
		}

		if len(pubKeyBase32) != pubKeyBase32Len {
			return fmt.Errorf("invalid pubkey length: %d",
				len(invoice.Destination.SerializeCompressed()))
		}

		err = writeTaggedField(bufferBase32, fieldTypeN, pubKeyBase32)
		if err != nil {
			return err
		}
	}
	if invoice.PaymentAddr != nil {
		err := writeBytes32(
			bufferBase32, fieldTypeS, *invoice.PaymentAddr,
		)
		if err != nil {
			return err
		}
	}
	if invoice.Features.SerializeSize32() > 0 {
		var b bytes.Buffer
		err := invoice.Features.RawFeatureVector.EncodeBase32(&b)
		if err != nil {
			return err
		}

		err = writeTaggedField(bufferBase32, fieldType9, b.Bytes())
		if err != nil {
			return err
		}
	}

	return nil
}

// writeBytes32 encodes a 32-byte array as base32 and writes it to bufferBase32
// under the passed fieldType.
func writeBytes32(bufferBase32 *bytes.Buffer, fieldType byte, b [32]byte) error {
	// Convert 32 byte hash to 52 5-bit groups.
	base32, err := bech32.ConvertBits(b[:], 8, 5, true)
	if err != nil {
		return err
	}

	return writeTaggedField(bufferBase32, fieldType, base32)
}

// writeTaggedField takes the type of a tagged data field, and the data of
// the tagged field (encoded in base32), and writes the type, length and data
// to the buffer.
func writeTaggedField(bufferBase32 *bytes.Buffer, dataType byte, data []byte) error {
	// Length must be exactly 10 bits, so add leading zero groups if
	// needed.
	lenBase32 := uint64ToBase32(uint64(len(data)))
	for len(lenBase32) < 2 {
		lenBase32 = append([]byte{0}, lenBase32...)
	}

	if len(lenBase32) != 2 {
		return fmt.Errorf("data length too big to fit within 10 bits: %d",
			len(data))
	}

	err := bufferBase32.WriteByte(dataType)
	if err != nil {
		return fmt.Errorf("unable to write to buffer: %v", err)
	}
	_, err = bufferBase32.Write(lenBase32)
	if err != nil {
		return fmt.Errorf("unable to write to buffer: %v", err)
	}
	_, err = bufferBase32.Write(data)
	if err != nil {
		return fmt.Errorf("unable to write to buffer: %v", err)
	}

	return nil
}

// base32ToUint64 converts a base32 encoded number to uint64.
func base32ToUint64(data []byte) (uint64, error) {
	// Maximum that fits in uint64 is ceil(64 / 5) = 12 groups.
	if len(data) > 13 {
		return 0, fmt.Errorf("cannot parse data of length %d as uint64",
			len(data))
	}

	val := uint64(0)
	for i := 0; i < len(data); i++ {
		val = val<<5 | uint64(data[i])
	}
	return val, nil
}

// uint64ToBase32 converts a uint64 to a base32 encoded integer encoded using
// as few 5-bit groups as possible.
func uint64ToBase32(num uint64) []byte {
	// Return at least one group.
	if num == 0 {
		return []byte{0}
	}

	// To fit an uint64, we need at most is ceil(64 / 5) = 13 groups.
	arr := make([]byte, 13)
	i := 13
	for num > 0 {
		i--
		arr[i] = byte(num & uint64(31)) // 0b11111 in binary
		num = num >> 5
	}

	// We only return non-zero leading groups.
	return arr[i:]
}
