package invoices

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/lightningnetwork/lnd/feature"
	"github.com/lightningnetwork/lnd/lntypes"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/lightningnetwork/lnd/record"
)

const (
	// MaxMemoSize is maximum size of the memo field within invoices stored
	// in the database.
	MaxMemoSize = 1024

	// MaxPaymentRequestSize is the max size of a payment request for
	// this invoice.
	// TODO(halseth): determine the max length payment request when field
	// lengths are final.
	MaxPaymentRequestSize = 4096
)

var (
	// unknownPreimage is an all-zeroes preimage that indicates that the
	// preimage for this invoice is not yet known.
	UnknownPreimage lntypes.Preimage

	// BlankPayAddr is a sentinel payment address for legacy invoices.
	// Invoices with this payment address are special-cased in the insertion
	// logic to prevent being indexed in the payment address index,
	// otherwise they would cause collisions after the first insertion.
	BlankPayAddr [32]byte
)

// RefModifier is a modification on top of a base invoice ref. It allows the
// caller to opt to skip out on HTLCs for a given payAddr, or only return the
// set of specified HTLCs for a given setID.
type RefModifier uint8

const (
	// DefaultModifier is the base modifier that doesn't change any
	// behavior.
	DefaultModifier RefModifier = iota

	// HtlcSetOnlyModifier can only be used with a setID based invoice ref,
	// and specifies that only the set of HTLCs related to that setID are
	// to be returned.
	HtlcSetOnlyModifier

	// HtlcSetOnlyModifier can only be used with a payAddr based invoice
	// ref, and specifies that the returned invoice shouldn't include any
	// HTLCs at all.
	HtlcSetBlankModifier
)

// InvoiceRef is a composite identifier for invoices. Invoices can be referenced
// by various combinations of payment hash and payment addr, in certain contexts
// only some of these are known. An InvoiceRef and its constructors thus
// encapsulate the valid combinations of query parameters that can be supplied
// to LookupInvoice and UpdateInvoice.
type InvoiceRef struct {
	// payHash is the payment hash of the target invoice. All invoices are
	// currently indexed by payment hash. This value will be used as a
	// fallback when no payment address is known.
	payHash *lntypes.Hash

	// payAddr is the payment addr of the target invoice. Newer invoices
	// (0.11 and up) are indexed by payment address in addition to payment
	// hash, but pre 0.8 invoices do not have one at all. When this value is
	// known it will be used as the primary identifier, falling back to
	// payHash if no value is known.
	payAddr *[32]byte

	// setID is the optional set id for an AMP payment. This can be used to
	// lookup or update the invoice knowing only this value. Queries by set
	// id are only used to facilitate user-facing requests, e.g. lookup,
	// settle or cancel an AMP invoice. The regular update flow from the
	// invoice registry will always query for the invoice by
	// payHash+payAddr.
	setID *[32]byte

	// refModifier allows an invoice ref to include or exclude specific
	// HTLC sets based on the payAddr or setId.
	refModifier RefModifier
}

// InvoiceRefByHash creates an InvoiceRef that queries for an invoice only by
// its payment hash.
func InvoiceRefByHash(payHash lntypes.Hash) InvoiceRef {
	return InvoiceRef{
		payHash: &payHash,
	}
}

// InvoiceRefByHashAndAddr creates an InvoiceRef that first queries for an
// invoice by the provided payment address, falling back to the payment hash if
// the payment address is unknown.
func InvoiceRefByHashAndAddr(payHash lntypes.Hash,
	payAddr [32]byte) InvoiceRef {

	return InvoiceRef{
		payHash: &payHash,
		payAddr: &payAddr,
	}
}

// InvoiceRefByAddr creates an InvoiceRef that queries the payment addr index
// for an invoice with the provided payment address.
func InvoiceRefByAddr(addr [32]byte) InvoiceRef {
	return InvoiceRef{
		payAddr: &addr,
	}
}

// InvoiceRefByAddrBlankHtlc creates an InvoiceRef that queries the payment addr
// index for an invoice with the provided payment address, but excludes any of
// the core HTLC information.
func InvoiceRefByAddrBlankHtlc(addr [32]byte) InvoiceRef {
	return InvoiceRef{
		payAddr:     &addr,
		refModifier: HtlcSetBlankModifier,
	}
}

// InvoiceRefBySetID creates an InvoiceRef that queries the set id index for an
// invoice with the provided setID. If the invoice is not found, the query will
// not fallback to payHash or payAddr.
func InvoiceRefBySetID(setID [32]byte) InvoiceRef {
	return InvoiceRef{
		setID: &setID,
	}
}

// InvoiceRefBySetIDFiltered is similar to the InvoiceRefBySetID identifier,
// but it specifies that the returned set of HTLCs should be filtered to only
// include HTLCs that are part of that set.
func InvoiceRefBySetIDFiltered(setID [32]byte) InvoiceRef {
	return InvoiceRef{
		setID:       &setID,
		refModifier: HtlcSetOnlyModifier,
	}
}

// PayHash returns the optional payment hash of the target invoice.
//
// NOTE: This value may be nil.
func (r InvoiceRef) PayHash() *lntypes.Hash {
	if r.payHash != nil {
		hash := *r.payHash
		return &hash
	}

	return nil
}

// PayAddr returns the optional payment address of the target invoice.
//
// NOTE: This value may be nil.
func (r InvoiceRef) PayAddr() *[32]byte {
	if r.payAddr != nil {
		addr := *r.payAddr
		return &addr
	}

	return nil
}

// SetID returns the optional set id of the target invoice.
//
// NOTE: This value may be nil.
func (r InvoiceRef) SetID() *[32]byte {
	if r.setID != nil {
		id := *r.setID
		return &id
	}

	return nil
}

// Modifier defines the set of available modifications to the base invoice ref
// look up that are available.
func (r InvoiceRef) Modifier() RefModifier {
	return r.refModifier
}

// String returns a human-readable representation of an InvoiceRef.
func (r InvoiceRef) String() string {
	var ids []string
	if r.payHash != nil {
		ids = append(ids, fmt.Sprintf("pay_hash=%v", *r.payHash))
	}
	if r.payAddr != nil {
		ids = append(ids, fmt.Sprintf("pay_addr=%x", *r.payAddr))
	}
	if r.setID != nil {
		ids = append(ids, fmt.Sprintf("set_id=%x", *r.setID))
	}

	return fmt.Sprintf("(%s)", strings.Join(ids, ", "))
}

// ContractState describes the state the invoice is in.
type ContractState uint8

const (
	// ContractOpen means the invoice has only been created.
	ContractOpen ContractState = 0

	// ContractSettled means the htlc is settled and the invoice has been
	// paid.
	ContractSettled ContractState = 1

	// ContractCanceled means the invoice has been canceled.
	ContractCanceled ContractState = 2

	// ContractAccepted means the HTLC has been accepted but not settled
	// yet.
	ContractAccepted ContractState = 3
)

// String returns a human readable identifier for the ContractState type.
func (c ContractState) String() string {
	switch c {
	case ContractOpen:
		return "Open"

	case ContractSettled:
		return "Settled"

	case ContractCanceled:
		return "Canceled"

	case ContractAccepted:
		return "Accepted"
	}

	return "Unknown"
}

// IsFinal returns a boolean indicating whether an invoice state is final.
func (c ContractState) IsFinal() bool {
	return c == ContractSettled || c == ContractCanceled
}

// ContractTerm is a companion struct to the Invoice struct. This struct houses
// the necessary conditions required before the invoice can be considered fully
// settled by the payee.
type ContractTerm struct {
	// FinalCltvDelta is the minimum required number of blocks before htlc
	// expiry when the invoice is accepted.
	FinalCltvDelta int32

	// Expiry defines how long after creation this invoice should expire.
	Expiry time.Duration

	// PaymentPreimage is the preimage which is to be revealed in the
	// occasion that an HTLC paying to the hash of this preimage is
	// extended. Set to nil if the preimage isn't known yet.
	PaymentPreimage *lntypes.Preimage

	// Value is the expected amount of milli-satoshis to be paid to an HTLC
	// which can be satisfied by the above preimage.
	Value lnwire.MilliSatoshi

	// PaymentAddr is a randomly generated value include in the MPP record
	// by the sender to prevent probing of the receiver.
	PaymentAddr [32]byte

	// Features is the feature vectors advertised on the payment request.
	Features *lnwire.FeatureVector
}

// String returns a human-readable description of the prominent contract terms.
func (c ContractTerm) String() string {
	return fmt.Sprintf("amt=%v, expiry=%v, final_cltv_delta=%v", c.Value,
		c.Expiry, c.FinalCltvDelta)
}

// SetID is the extra unique tuple item for AMP invoices. In addition to
// setting a payment address, each repeated payment to an AMP invoice will also
// contain a set ID as well.
type SetID [32]byte

// InvoiceStateAMP is a struct that associates the current state of an AMP
// invoice identified by its set ID along with the set of invoices identified
// by the circuit key. This allows callers to easily look up the latest state
// of an AMP "sub-invoice" and also look up the invoice HLTCs themselves in the
// greater HTLC map index.
type InvoiceStateAMP struct {
	// State is the state of this sub-AMP invoice.
	State HtlcState

	// SettleIndex indicates the location in the settle index that
	// references this instance of InvoiceStateAMP, but only if
	// this value is set (non-zero), and State is HtlcStateSettled.
	SettleIndex uint64

	// SettleDate is the date that the setID was settled.
	SettleDate time.Time

	// InvoiceKeys is the set of circuit keys that can be used to locate
	// the invoices for a given set ID.
	InvoiceKeys map[CircuitKey]struct{}

	// AmtPaid is the total amount that was paid in the AMP sub-invoice.
	// Fetching the full HTLC/invoice state allows one to extract the
	// custom records as well as the break down of the payment splits used
	// when paying.
	AmtPaid lnwire.MilliSatoshi
}

// copy makes a deep copy of the underlying InvoiceStateAMP.
func (i *InvoiceStateAMP) copy() (InvoiceStateAMP, error) {
	result := *i

	// Make a copy of the InvoiceKeys map.
	result.InvoiceKeys = make(map[CircuitKey]struct{})
	for k := range i.InvoiceKeys {
		result.InvoiceKeys[k] = struct{}{}
	}

	// As a safety measure, copy SettleDate. time.Time is concurrency safe
	// except when using any of the (un)marshalling methods.
	settleDateBytes, err := i.SettleDate.MarshalBinary()
	if err != nil {
		return InvoiceStateAMP{}, err
	}

	err = result.SettleDate.UnmarshalBinary(settleDateBytes)
	if err != nil {
		return InvoiceStateAMP{}, err
	}

	return result, nil
}

// AMPInvoiceState represents a type that stores metadata related to the set of
// settled AMP "sub-invoices".
type AMPInvoiceState map[SetID]InvoiceStateAMP

// Invoice is a payment invoice generated by a payee in order to request
// payment for some good or service. The inclusion of invoices within Lightning
// creates a payment work flow for merchants very similar to that of the
// existing financial system within PayPal, etc.  Invoices are added to the
// database when a payment is requested, then can be settled manually once the
// payment is received at the upper layer. For record keeping purposes,
// invoices are never deleted from the database, instead a bit is toggled
// denoting the invoice has been fully settled. Within the database, all
// invoices must have a unique payment hash which is generated by taking the
// sha256 of the payment preimage.
type Invoice struct {
	// Memo is an optional memo to be stored along side an invoice.  The
	// memo may contain further details pertaining to the invoice itself,
	// or any other message which fits within the size constraints.
	Memo []byte

	// PaymentRequest is the encoded payment request for this invoice. For
	// spontaneous (keysend) payments, this field will be empty.
	PaymentRequest []byte

	// CreationDate is the exact time the invoice was created.
	CreationDate time.Time

	// SettleDate is the exact time the invoice was settled.
	SettleDate time.Time

	// Terms are the contractual payment terms of the invoice. Once all the
	// terms have been satisfied by the payer, then the invoice can be
	// considered fully fulfilled.
	//
	// TODO(roasbeef): later allow for multiple terms to fulfill the final
	// invoice: payment fragmentation, etc.
	Terms ContractTerm

	// AddIndex is an auto-incrementing integer that acts as a
	// monotonically increasing sequence number for all invoices created.
	// Clients can then use this field as a "checkpoint" of sorts when
	// implementing a streaming RPC to notify consumers of instances where
	// an invoice has been added before they re-connected.
	//
	// NOTE: This index starts at 1.
	AddIndex uint64

	// SettleIndex is an auto-incrementing integer that acts as a
	// monotonically increasing sequence number for all settled invoices.
	// Clients can then use this field as a "checkpoint" of sorts when
	// implementing a streaming RPC to notify consumers of instances where
	// an invoice has been settled before they re-connected.
	//
	// NOTE: This index starts at 1.
	SettleIndex uint64

	// State describes the state the invoice is in. This is the global
	// state of the invoice which may remain open even when a series of
	// sub-invoices for this invoice has been settled.
	State ContractState

	// AmtPaid is the final amount that we ultimately accepted for pay for
	// this invoice. We specify this value independently as it's possible
	// that the invoice originally didn't specify an amount, or the sender
	// overpaid.
	AmtPaid lnwire.MilliSatoshi

	// Htlcs records all htlcs that paid to this invoice. Some of these
	// htlcs may have been marked as canceled.
	Htlcs map[CircuitKey]*InvoiceHTLC

	// AMPState describes the state of any related sub-invoices AMP to this
	// greater invoice. A sub-invoice is defined by a set of HTLCs with the
	// same set ID that attempt to make one time or recurring payments to
	// this greater invoice. It's possible for a sub-invoice to be canceled
	// or settled, but the greater invoice still open.
	AMPState AMPInvoiceState

	// HodlInvoice indicates whether the invoice should be held in the
	// Accepted state or be settled right away.
	HodlInvoice bool
}

// HTLCSet returns the set of HTLCs belonging to setID and in the provided
// state. Passing a nil setID will return all HTLCs in the provided state in the
// case of legacy or MPP, and no HTLCs in the case of AMP.  Otherwise, the
// returned set will be filtered by the populated setID which is used to
// retrieve AMP HTLC sets.
func (i *Invoice) HTLCSet(setID *[32]byte,
	state HtlcState) map[CircuitKey]*InvoiceHTLC {

	htlcSet := make(map[CircuitKey]*InvoiceHTLC)
	for key, htlc := range i.Htlcs {
		// Only add HTLCs that are in the requested HtlcState.
		if htlc.State != state {
			continue
		}

		if !htlc.IsInHTLCSet(setID) {
			continue
		}

		htlcSet[key] = htlc
	}

	return htlcSet
}

// HTLCSetCompliment returns the set of all HTLCs not belonging to setID that
// are in the target state. Passing a nil setID will return no invoices, since
// all MPP HTLCs are part of the same HTLC set.
func (i *Invoice) HTLCSetCompliment(setID *[32]byte,
	state HtlcState) map[CircuitKey]*InvoiceHTLC {

	htlcSet := make(map[CircuitKey]*InvoiceHTLC)
	for key, htlc := range i.Htlcs {
		// Only add HTLCs that are in the requested HtlcState.
		if htlc.State != state {
			continue
		}

		// We are constructing the compliment, so filter anything that
		// matches this set id.
		if htlc.IsInHTLCSet(setID) {
			continue
		}

		htlcSet[key] = htlc
	}

	return htlcSet
}

// IsKeysend returns true if the invoice is a Keysend invoice.
func (i *Invoice) IsKeysend() bool {
	// TODO(positiveblue): look for a more reliable way to tests if
	// an invoice is keysend.
	return len(i.PaymentRequest) == 0 && !i.IsAMP()
}

// IsAMP returns true if the invoice is an AMP invoice.
func (i *Invoice) IsAMP() bool {
	if i.Terms.Features == nil {
		return false
	}

	return i.Terms.Features.HasFeature(
		lnwire.AMPRequired,
	)
}

// IsBlinded returns true if the invoice contains blinded paths.
func (i *Invoice) IsBlinded() bool {
	if i.Terms.Features == nil {
		return false
	}

	return i.Terms.Features.IsSet(lnwire.Bolt11BlindedPathsRequired)
}

// HtlcState defines the states an htlc paying to an invoice can be in.
type HtlcState uint8

const (
	// HtlcStateAccepted indicates the htlc is locked-in, but not resolved.
	HtlcStateAccepted HtlcState = iota

	// HtlcStateCanceled indicates the htlc is canceled back to the
	// sender.
	HtlcStateCanceled

	// HtlcStateSettled indicates the htlc is settled.
	HtlcStateSettled
)

// InvoiceHTLC contains details about an htlc paying to this invoice.
type InvoiceHTLC struct {
	// Amt is the amount that is carried by this htlc.
	Amt lnwire.MilliSatoshi

	// MppTotalAmt is a field for mpp that indicates the expected total
	// amount.
	MppTotalAmt lnwire.MilliSatoshi

	// AcceptHeight is the block height at which the invoice registry
	// decided to accept this htlc as a payment to the invoice. At this
	// height, the invoice cltv delay must have been met.
	AcceptHeight uint32

	// AcceptTime is the wall clock time at which the invoice registry
	// decided to accept the htlc.
	AcceptTime time.Time

	// ResolveTime is the wall clock time at which the invoice registry
	// decided to settle the htlc.
	ResolveTime time.Time

	// Expiry is the expiry height of this htlc.
	Expiry uint32

	// State indicates the state the invoice htlc is currently in. A
	// canceled htlc isn't just removed from the invoice htlcs map, because
	// we need AcceptHeight to properly cancel the htlc back.
	State HtlcState

	// CustomRecords contains the custom key/value pairs that accompanied
	// the htlc.
	CustomRecords record.CustomSet

	// WireCustomRecords contains the custom key/value pairs that were only
	// included in p2p wire message of the HTLC and not in the onion
	// payload.
	WireCustomRecords lnwire.CustomRecords

	// AMP encapsulates additional data relevant to AMP HTLCs. This includes
	// the AMP onion record, in addition to the HTLC's payment hash and
	// preimage since these are unique to each AMP HTLC, and not the invoice
	// as a whole.
	//
	// NOTE: This value will only be set for AMP HTLCs.
	AMP *InvoiceHtlcAMPData
}

// Copy makes a deep copy of the target InvoiceHTLC.
func (h *InvoiceHTLC) Copy() *InvoiceHTLC {
	result := *h

	// Make a copy of the CustomSet map.
	result.CustomRecords = make(record.CustomSet)
	for k, v := range h.CustomRecords {
		result.CustomRecords[k] = v
	}

	result.WireCustomRecords = h.WireCustomRecords.Copy()
	result.AMP = h.AMP.Copy()

	return &result
}

// IsInHTLCSet returns true if this HTLC is part an HTLC set. If nil is passed,
// this method returns true if this is an MPP HTLC. Otherwise, it only returns
// true if the AMP HTLC's set id matches the populated setID.
func (h *InvoiceHTLC) IsInHTLCSet(setID *[32]byte) bool {
	wantAMPSet := setID != nil
	isAMPHtlc := h.AMP != nil

	// Non-AMP HTLCs cannot be part of AMP HTLC sets, and vice versa.
	if wantAMPSet != isAMPHtlc {
		return false
	}

	// Skip AMP HTLCs that have differing set ids.
	if isAMPHtlc && *setID != h.AMP.Record.SetID() {
		return false
	}

	return true
}

// InvoiceHtlcAMPData is a struct hodling the additional metadata stored for
// each received AMP HTLC. This includes the AMP onion record, in addition to
// the HTLC's payment hash and preimage.
type InvoiceHtlcAMPData struct {
	// AMP is a copy of the AMP record presented in the onion payload
	// containing the information necessary to correlate and settle a
	// spontaneous HTLC set. Newly accepted legacy keysend payments will
	// also have this field set as we automatically promote them into an AMP
	// payment for internal processing.
	Record record.AMP

	// Hash is an HTLC-level payment hash that is stored only for AMP
	// payments. This is done because an AMP HTLC will carry a different
	// payment hash from the invoice it might be satisfying, so we track the
	// payment hashes individually to able to compute whether or not the
	// reconstructed preimage correctly matches the HTLC's hash.
	Hash lntypes.Hash

	// Preimage is an HTLC-level preimage that satisfies the AMP HTLC's
	// Hash. The preimage will be derived either from secret share
	// reconstruction of the shares in the AMP payload.
	//
	// NOTE: Preimage will only be present once the HTLC is in
	// HtlcStateSettled.
	Preimage *lntypes.Preimage
}

// Copy returns a deep copy of the InvoiceHtlcAMPData.
func (d *InvoiceHtlcAMPData) Copy() *InvoiceHtlcAMPData {
	if d == nil {
		return nil
	}

	var preimage *lntypes.Preimage
	if d.Preimage != nil {
		pimg := *d.Preimage
		preimage = &pimg
	}

	return &InvoiceHtlcAMPData{
		Record:   d.Record,
		Hash:     d.Hash,
		Preimage: preimage,
	}
}

// HtlcAcceptDesc describes the details of a newly accepted htlc.
type HtlcAcceptDesc struct {
	// AcceptHeight is the block height at which this htlc was accepted.
	AcceptHeight int32

	// Amt is the amount that is carried by this htlc.
	Amt lnwire.MilliSatoshi

	// MppTotalAmt is a field for mpp that indicates the expected total
	// amount.
	MppTotalAmt lnwire.MilliSatoshi

	// Expiry is the expiry height of this htlc.
	Expiry uint32

	// CustomRecords contains the custom key/value pairs that accompanied
	// the htlc.
	CustomRecords record.CustomSet

	// AMP encapsulates additional data relevant to AMP HTLCs. This includes
	// the AMP onion record, in addition to the HTLC's payment hash and
	// preimage since these are unique to each AMP HTLC, and not the invoice
	// as a whole.
	//
	// NOTE: This value will only be set for AMP HTLCs.
	AMP *InvoiceHtlcAMPData
}

// UpdateType is an enum that describes the type of update that was applied to
// an invoice.
type UpdateType uint8

const (
	// UnknownUpdate indicates that the UpdateType has not been set for a
	// given udpate. This kind of updates are not allowed.
	UnknownUpdate UpdateType = iota

	// CancelHTLCsUpdate indicates that this update cancels one or more
	// HTLCs.
	CancelHTLCsUpdate

	// AddHTLCsUpdate indicates that this update adds one or more HTLCs.
	AddHTLCsUpdate

	// SettleHodlInvoiceUpdate indicates that this update settles one or
	// more HTLCs from a hodl invoice.
	SettleHodlInvoiceUpdate

	// CancelInvoiceUpdate indicates that this update is trying to cancel
	// an invoice.
	CancelInvoiceUpdate
)

// String returns a human readable string for the UpdateType.
func (u UpdateType) String() string {
	switch u {
	case CancelHTLCsUpdate:
		return "CancelHTLCsUpdate"

	case AddHTLCsUpdate:
		return "AddHTLCsUpdate"

	case SettleHodlInvoiceUpdate:
		return "SettleHodlInvoiceUpdate"

	case CancelInvoiceUpdate:
		return "CancelInvoiceUpdate"

	default:
		return fmt.Sprintf("unknown invoice update type: %d", u)
	}
}

// InvoiceUpdateDesc describes the changes that should be applied to the
// invoice.
type InvoiceUpdateDesc struct {
	// State is the new state that this invoice should progress to. If nil,
	// the state is left unchanged.
	State *InvoiceStateUpdateDesc

	// CancelHtlcs describes the htlcs that need to be canceled.
	CancelHtlcs map[CircuitKey]struct{}

	// AddHtlcs describes the newly accepted htlcs that need to be added to
	// the invoice.
	AddHtlcs map[CircuitKey]*HtlcAcceptDesc

	// SetID is an optional set ID for AMP invoices that allows operations
	// to be more efficient by ensuring we don't need to read out the
	// entire HTLC set each timee an HTLC is to be cancelled.
	SetID *SetID

	// UpdateType indicates what type of update is being applied.
	UpdateType UpdateType
}

// InvoiceStateUpdateDesc describes an invoice-level state transition.
type InvoiceStateUpdateDesc struct {
	// NewState is the new state that this invoice should progress to.
	NewState ContractState

	// Preimage must be set to the preimage when NewState is settled.
	Preimage *lntypes.Preimage

	// HTLCPreimages set the HTLC-level preimages stored for AMP HTLCs.
	// These are only learned when settling the invoice as a whole. Must be
	// set when settling an invoice with non-nil SetID.
	HTLCPreimages map[CircuitKey]lntypes.Preimage

	// SetID identifies a specific set of HTLCs destined for the same
	// invoice as part of a larger AMP payment. This value will be nil for
	// legacy or MPP payments.
	SetID *[32]byte
}

// InvoiceUpdateCallback is a callback used in the db transaction to update the
// invoice.
type InvoiceUpdateCallback = func(invoice *Invoice) (*InvoiceUpdateDesc, error)

// ValidateInvoice assures the invoice passes the checks for all the relevant
// constraints.
func ValidateInvoice(i *Invoice, paymentHash lntypes.Hash) error {
	// Avoid conflicts with all-zeroes magic value in the database.
	if paymentHash == UnknownPreimage.Hash() {
		return fmt.Errorf("cannot use hash of all-zeroes preimage")
	}

	if len(i.Memo) > MaxMemoSize {
		return fmt.Errorf("max length a memo is %v, and invoice "+
			"of length %v was provided", MaxMemoSize, len(i.Memo))
	}
	if len(i.PaymentRequest) > MaxPaymentRequestSize {
		return fmt.Errorf("max length of payment request is %v, "+
			"length provided was %v", MaxPaymentRequestSize,
			len(i.PaymentRequest))
	}
	if i.Terms.Features == nil {
		return errors.New("invoice must have a feature vector")
	}

	err := feature.ValidateDeps(i.Terms.Features)
	if err != nil {
		return err
	}

	if i.requiresPreimage() && i.Terms.PaymentPreimage == nil {
		return errors.New("this invoice must have a preimage")
	}

	if len(i.Htlcs) > 0 {
		return ErrInvoiceHasHtlcs
	}

	return nil
}

// requiresPreimage returns true if the invoice requires a preimage to be valid.
func (i *Invoice) requiresPreimage() bool {
	// AMP invoices and hodl invoices are allowed to have no preimage
	// specified.
	if i.HodlInvoice || i.IsAMP() {
		return false
	}

	return true
}

// IsPending returns true if the invoice is in ContractOpen state.
func (i *Invoice) IsPending() bool {
	return i.State == ContractOpen || i.State == ContractAccepted
}

// copySlice allocates a new slice and copies the source into it.
func copySlice(src []byte) []byte {
	dest := make([]byte, len(src))
	copy(dest, src)
	return dest
}

// CopyInvoice makes a deep copy of the supplied invoice.
func CopyInvoice(src *Invoice) (*Invoice, error) {
	dest := Invoice{
		Memo:           copySlice(src.Memo),
		PaymentRequest: copySlice(src.PaymentRequest),
		CreationDate:   src.CreationDate,
		SettleDate:     src.SettleDate,
		Terms:          src.Terms,
		AddIndex:       src.AddIndex,
		SettleIndex:    src.SettleIndex,
		State:          src.State,
		AmtPaid:        src.AmtPaid,
		Htlcs: make(
			map[CircuitKey]*InvoiceHTLC, len(src.Htlcs),
		),
		AMPState:    make(map[SetID]InvoiceStateAMP),
		HodlInvoice: src.HodlInvoice,
	}

	dest.Terms.Features = src.Terms.Features.Clone()

	if src.Terms.PaymentPreimage != nil {
		preimage := *src.Terms.PaymentPreimage
		dest.Terms.PaymentPreimage = &preimage
	}

	for k, v := range src.Htlcs {
		dest.Htlcs[k] = v.Copy()
	}

	// Lastly, copy the amp invoice state.
	for k, v := range src.AMPState {
		ampInvState, err := v.copy()
		if err != nil {
			return nil, err
		}

		dest.AMPState[k] = ampInvState
	}

	return &dest, nil
}

// InvoiceDeleteRef holds a reference to an invoice to be deleted.
type InvoiceDeleteRef struct {
	// PayHash is the payment hash of the target invoice. All invoices are
	// currently indexed by payment hash.
	PayHash lntypes.Hash

	// PayAddr is the payment addr of the target invoice. Newer invoices
	// (0.11 and up) are indexed by payment address in addition to payment
	// hash, but pre 0.8 invoices do not have one at all.
	PayAddr *[32]byte

	// AddIndex is the add index of the invoice.
	AddIndex uint64

	// SettleIndex is the settle index of the invoice.
	SettleIndex uint64
}
