package chanstate

import (
	"net"

	"github.com/btcsuite/btcd/btcec/v2"
	"github.com/btcsuite/btcd/wire/v2"
	"github.com/lightningnetwork/lnd/fn/v2"
	"github.com/lightningnetwork/lnd/graph/db/models"
	"github.com/lightningnetwork/lnd/lntypes"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/lightningnetwork/lnd/shachain"
)

// Store is the full persistence contract for the channel-state subsystem.
// Consumers depend on this interface rather than the concrete
// channeldb.ChannelStateDB so the underlying storage can be swapped without
// touching call sites.
//
// NOTE: This is named Store instead of DB to avoid confusion with the existing
// concrete channeldb.ChannelStateDB type during the migration. Once the channel
// state implementation moves into this package and the old concrete type is no
// longer part of consumer-facing code, this name can be revisited.
type Store interface { //nolint:interfacebloat
	// OpenChannelStore owns open-channel records.
	OpenChannelStore

	// HistoricalChannelStore owns the post-close historical channel view.
	HistoricalChannelStore

	// OpenChannelLifecycleStore owns persisted lifecycle state for open
	// channel records.
	OpenChannelLifecycleStore

	// OpenChannelStatusStore owns persisted status flags for open channel
	// records.
	OpenChannelStatusStore

	// OpenChannelShutdownStore owns persisted shutdown state.
	OpenChannelShutdownStore

	// OpenChannelCloseTxStore owns persisted closing transaction state.
	OpenChannelCloseTxStore

	// OpenChannelCommitmentStore owns persisted commitment state for open
	// channel records.
	OpenChannelCommitmentStore

	// OpenChannelFwdPkgStore owns forwarding packages tied to open
	// channel records.
	OpenChannelFwdPkgStore

	// ClosedChannelStore owns closed-channel summaries and lifecycle
	// mutations.
	ClosedChannelStore

	// FinalHTLCStore owns final HTLC outcome data.
	FinalHTLCStore

	// ChannelSetupStore owns temporary state used while setting up a
	// channel.
	ChannelSetupStore

	// LinkNodeMaintainer owns link-node maintenance derived from channel
	// state.
	LinkNodeMaintainer
}

// OpenChannelStore owns open-channel records.
type OpenChannelStore interface {
	// FetchOpenChannels starts a new database transaction and returns
	// all stored currently active/open channels associated with the
	// target nodeID. In the case that no active channels are known to
	// have been created with this node, then a zero-length slice is
	// returned.
	FetchOpenChannels(nodeID *btcec.PublicKey) ([]*OpenChannel, error)

	// FetchChannel attempts to locate a channel specified by the passed
	// channel point. If the channel cannot be found, then an error will
	// be returned.
	FetchChannel(chanPoint wire.OutPoint) (*OpenChannel, error)

	// FetchChannelByID attempts to locate a channel specified by the
	// passed channel ID. If the channel cannot be found, then an error
	// will be returned.
	FetchChannelByID(id lnwire.ChannelID) (*OpenChannel, error)

	// FetchAllChannels attempts to retrieve all open channels currently
	// stored within the database, including pending open, fully open and
	// channels waiting for a closing transaction to confirm.
	FetchAllChannels() ([]*OpenChannel, error)

	// FetchAllOpenChannels will return all channels that have the
	// funding transaction confirmed, and is not waiting for a closing
	// transaction to be confirmed.
	FetchAllOpenChannels() ([]*OpenChannel, error)

	// FetchPendingChannels will return channels that have completed the
	// process of generating and broadcasting funding transactions, but
	// whose funding transactions have yet to be confirmed on the
	// blockchain.
	FetchPendingChannels() ([]*OpenChannel, error)

	// FetchWaitingCloseChannels will return all channels that have been
	// opened, but are now waiting for a closing transaction to be
	// confirmed.
	//
	// NOTE: This includes channels that are also pending to be opened.
	FetchWaitingCloseChannels() ([]*OpenChannel, error)

	// FetchPermAndTempPeers returns a map where the key is the remote
	// node's public key and the value is a struct that has a tally of
	// the pending-open channels and whether the peer has an open or
	// closed channel with us.
	FetchPermAndTempPeers(chainHash []byte) (map[string]ChanCount, error)

	// RestoreChannelShells reconstructs the state of an OpenChannel from
	// the ChannelShell. We'll attempt to write the new channel to disk,
	// create a LinkNode instance with the passed node addresses, and
	// finally create an edge within the graph for the channel as well.
	// This method is idempotent, so repeated calls with the same set of
	// channel shells won't modify the database after the initial call.
	RestoreChannelShells(channelShells ...*ChannelShell) error
}

// HistoricalChannelStore owns the post-close historical channel view.
type HistoricalChannelStore interface {
	// FetchHistoricalChannel fetches open channel data from the
	// historical channel bucket.
	FetchHistoricalChannel(outPoint *wire.OutPoint) (*OpenChannel, error)
}

// OpenChannelLifecycleStore owns persisted lifecycle state for open channel
// records.
type OpenChannelLifecycleStore interface {
	// SyncPendingChannel writes a pending channel to the store and records
	// the funding broadcast height.
	SyncPendingChannel(channel *OpenChannel, addr net.Addr,
		pendingHeight uint32) error

	// RefreshChannel updates the in-memory channel state using the latest
	// state observed on disk.
	RefreshChannel(channel *OpenChannel) error

	// MarkChannelConfirmationHeight updates the channel's confirmation
	// height once the channel opening transaction receives one
	// confirmation.
	MarkChannelConfirmationHeight(channel *OpenChannel, height uint32) error

	// MarkChannelCloseConfirmationHeight updates the channel's close
	// confirmation height when the closing transaction is first detected
	// in a block.
	MarkChannelCloseConfirmationHeight(channel *OpenChannel,
		height fn.Option[uint32]) error

	// MarkChannelOpen marks a channel as fully open given a locator that
	// uniquely describes its location within the chain.
	MarkChannelOpen(channel *OpenChannel,
		openLoc lnwire.ShortChannelID) error

	// MarkChannelRealScid marks the zero-conf channel's confirmed
	// ShortChannelID.
	MarkChannelRealScid(channel *OpenChannel,
		realScid lnwire.ShortChannelID) error

	// MarkChannelScidAliasNegotiated marks that the scid-alias feature
	// bit was negotiated during the lifetime of the channel.
	MarkChannelScidAliasNegotiated(channel *OpenChannel) error
}

// OpenChannelStatusStore owns persisted status flags for open channel records.
type OpenChannelStatusStore interface {
	// ApplyChannelStatus adds the target status to the channel's
	// persisted status bit field.
	ApplyChannelStatus(channel *OpenChannel, status ChannelStatus) error

	// ClearChannelStatus clears the target status from the channel's
	// persisted status bit field.
	ClearChannelStatus(channel *OpenChannel, status ChannelStatus) error

	// MarkChannelDataLoss marks the channel as local-data-loss and stores
	// the commit point needed if the remote force closes.
	MarkChannelDataLoss(channel *OpenChannel,
		commitPoint *btcec.PublicKey) error

	// FetchChannelDataLossCommitPoint retrieves the commit point stored
	// when the channel was marked as local-data-loss.
	FetchChannelDataLossCommitPoint(channel *OpenChannel) (
		*btcec.PublicKey, error)

	// MarkChannelBorked marks the channel as irreconcilable.
	MarkChannelBorked(channel *OpenChannel) error
}

// OpenChannelShutdownStore owns persisted shutdown state.
type OpenChannelShutdownStore interface {
	// StoreChannelShutdownInfo persists the ShutdownInfo for the target
	// channel.
	StoreChannelShutdownInfo(channel *OpenChannel, info *ShutdownInfo) error

	// FetchChannelShutdownInfo fetches the persisted ShutdownInfo for the
	// target channel.
	FetchChannelShutdownInfo(channel *OpenChannel) (
		fn.Option[ShutdownInfo], error)
}

// OpenChannelCloseTxStore owns persisted closing transaction state.
type OpenChannelCloseTxStore interface {
	// MarkChannelCommitmentBroadcasted marks the channel as having a
	// commitment transaction broadcast.
	MarkChannelCommitmentBroadcasted(channel *OpenChannel,
		closeTx *wire.MsgTx, closer lntypes.ChannelParty) error

	// MarkChannelCoopBroadcasted marks the channel as having a
	// cooperative close transaction broadcast.
	MarkChannelCoopBroadcasted(channel *OpenChannel, closeTx *wire.MsgTx,
		closer lntypes.ChannelParty) error

	// FetchChannelBroadcastedCommitment fetches the stored unilateral
	// closing transaction.
	FetchChannelBroadcastedCommitment(channel *OpenChannel) (*wire.MsgTx,
		error)

	// FetchChannelBroadcastedCooperative fetches the stored cooperative
	// closing transaction.
	FetchChannelBroadcastedCooperative(channel *OpenChannel) (*wire.MsgTx,
		error)
}

// OpenChannelCommitmentStore owns persisted commitment state for open channel
// records.
type OpenChannelCommitmentStore interface {
	OpenChannelCommitmentMutationStore
	OpenChannelCommitmentQueryStore
}

// OpenChannelCommitmentMutationStore owns persisted commitment mutations for
// open channel records.
type OpenChannelCommitmentMutationStore interface {
	// UpdateChannelCommitment updates the local commitment state. It
	// locks in pending local updates received from the remote party and
	// persists remote log updates that have been acked, but not signed
	// for yet. The returned map contains all HTLC resolutions locked into
	// this commitment, keyed by HTLC index.
	UpdateChannelCommitment(channel *OpenChannel,
		newCommitment *ChannelCommitment,
		unsignedAckedUpdates []LogUpdate) (map[uint64]bool, error)

	// AppendRemoteCommitChain appends a new CommitDiff to the remote
	// party's commitment chain. This is used after preparing a new remote
	// commitment state, before transmitting it to the remote party.
	AppendRemoteCommitChain(channel *OpenChannel, diff *CommitDiff) error

	// RemoteCommitChainTip returns the "tip" of the current remote
	// commitment chain.
	RemoteCommitChainTip(channel *OpenChannel) (*CommitDiff, error)

	// UnsignedAckedUpdates retrieves the persisted unsigned acked remote
	// log updates that still need to be signed for.
	UnsignedAckedUpdates(channel *OpenChannel) ([]LogUpdate, error)

	// RemoteUnsignedLocalUpdates retrieves the persisted, unsigned local
	// log updates that the remote still needs to sign for.
	RemoteUnsignedLocalUpdates(channel *OpenChannel) ([]LogUpdate, error)

	// InsertNextRevocation inserts the next commitment point into the
	// persisted channel state.
	InsertNextRevocation(channel *OpenChannel,
		revKey *btcec.PublicKey) error

	// AdvanceCommitChainTail records the new state transition within the
	// revocation log and promotes the pending remote commitment to the
	// current remote commitment.
	AdvanceCommitChainTail(channel *OpenChannel, fwdPkg *FwdPkg,
		updates []LogUpdate, ourOutputIndex,
		theirOutputIndex uint32) error
}

// OpenChannelCommitmentQueryStore owns persisted commitment queries for open
// channel records.
type OpenChannelCommitmentQueryStore interface {
	// CommitmentHeight returns the current persisted commitment height.
	CommitmentHeight(channel *OpenChannel) (uint64, error)

	// LatestCommitments returns the two latest commitments for both the
	// local and remote party.
	LatestCommitments(channel *OpenChannel) (*ChannelCommitment,
		*ChannelCommitment, error)

	// RemoteRevocationStore returns the most up to date commitment version
	// of the revocation storage tree for the remote party.
	RemoteRevocationStore(channel *OpenChannel) (shachain.Store, error)

	// FindPreviousState scans through the append-only log in an attempt to
	// recover the previous channel state indicated by the update number.
	FindPreviousState(channel *OpenChannel, updateNum uint64) (
		*RevocationLog, *ChannelCommitment, error)
}

// OpenChannelFwdPkgStore owns forwarding packages tied to open channel
// records.
type OpenChannelFwdPkgStore interface {
	// LoadFwdPkgs loads forwarding packages that have not been processed.
	LoadFwdPkgs(channel *OpenChannel) ([]*FwdPkg, error)

	// AckAddHtlcs marks add HTLCs in forwarding packages as resolved.
	AckAddHtlcs(channel *OpenChannel, addRefs ...AddRef) error

	// AckSettleFails marks settles or fails as delivered to the incoming
	// link.
	AckSettleFails(channel *OpenChannel,
		settleFailRefs ...SettleFailRef) error

	// SetFwdFilter writes the forwarding filter for the forwarding package
	// identified by height.
	SetFwdFilter(channel *OpenChannel, height uint64,
		fwdFilter *PkgFilter) error

	// RemoveFwdPkgs removes forwarding packages by remote commitment
	// height.
	RemoveFwdPkgs(channel *OpenChannel, heights ...uint64) error
}

// ClosedChannelStore owns closed-channel summaries and lifecycle mutations.
type ClosedChannelStore interface {
	// FetchClosedChannels attempts to fetch all closed channels from the
	// database. The pendingOnly bool toggles if channels that aren't yet
	// fully closed should be returned in the response or not. When a
	// channel was cooperatively closed, it becomes fully closed after a
	// single confirmation. When a channel was forcibly closed, it will
	// become fully closed after _all_ the pending funds (if any) have
	// been swept.
	FetchClosedChannels(pendingOnly bool) (
		[]*ChannelCloseSummary, error)

	// FetchClosedChannel queries for a channel close summary using the
	// channel point of the channel in question.
	FetchClosedChannel(chanID *wire.OutPoint) (
		*ChannelCloseSummary, error)

	// FetchClosedChannelForID queries for a channel close summary using
	// the channel ID of the channel in question.
	FetchClosedChannelForID(cid lnwire.ChannelID) (
		*ChannelCloseSummary, error)

	// MarkChanFullyClosed marks a channel as fully closed within the
	// database. A channel should be marked as fully closed if the
	// channel was initially cooperatively closed and it's reached a
	// single confirmation, or after all the pending funds in a channel
	// that has been forcibly closed have been swept.
	MarkChanFullyClosed(chanPoint *wire.OutPoint) error

	// CloseChannel marks the given channel as closed: the open-channel
	// record is removed and the supplied ChannelCloseSummary is
	// archived so the channel becomes retrievable via
	// FetchClosedChannel and FetchClosedChannelForID. Any ChannelStatus
	// values are merged into the archived summary. Returns
	// ErrChannelCloseSummaryNil if summary is nil.
	CloseChannel(channel *OpenChannel, summary *ChannelCloseSummary,
		statuses ...ChannelStatus) error

	// AbandonChannel attempts to remove the target channel from the open
	// channel database. If the channel was already removed (has a closed
	// channel entry), then we'll return a nil error. Otherwise, we'll
	// insert a new close summary into the database.
	AbandonChannel(chanPoint *wire.OutPoint, bestHeight uint32) error
}

// FinalHTLCStore owns final HTLC outcome data.
type FinalHTLCStore interface {
	// LookupFinalHtlc retrieves a final htlc resolution from the
	// database. If the htlc has no final resolution yet, ErrHtlcUnknown
	// is returned.
	LookupFinalHtlc(chanID lnwire.ShortChannelID,
		htlcIndex uint64) (*FinalHtlcInfo, error)

	// PutOnchainFinalHtlcOutcome stores the final on-chain outcome of an
	// htlc in the database.
	PutOnchainFinalHtlcOutcome(chanID lnwire.ShortChannelID,
		htlcID uint64, settled bool) error
}

// ChannelSetupStore owns temporary state used while setting up a channel. This
// state should be deleted once the link comes up.
type ChannelSetupStore interface {
	// SaveChannelOpeningState saves the serialized channel state for the
	// provided chanPoint to the channelOpeningStateBucket.
	SaveChannelOpeningState(outPoint, serializedState []byte) error

	// GetChannelOpeningState fetches the serialized channel state for
	// the provided outPoint from the database, or returns
	// ErrChannelNotFound if the channel is not found.
	GetChannelOpeningState(outPoint []byte) ([]byte, error)

	// DeleteChannelOpeningState removes any state for outPoint from the
	// database.
	DeleteChannelOpeningState(outPoint []byte) error

	// SaveInitialForwardingPolicy saves the serialized forwarding policy
	// for the provided permanent channel id.
	SaveInitialForwardingPolicy(chanID lnwire.ChannelID,
		forwardingPolicy *models.ForwardingPolicy) error

	// GetInitialForwardingPolicy fetches the serialized forwarding policy
	// for the provided channel id from the database, or returns
	// ErrChannelNotFound if a forwarding policy for this channel id is not
	// found.
	GetInitialForwardingPolicy(chanID lnwire.ChannelID) (
		*models.ForwardingPolicy, error)

	// DeleteInitialForwardingPolicy removes the forwarding policy for a
	// given channel from the database.
	DeleteInitialForwardingPolicy(chanID lnwire.ChannelID) error
}

// LinkNodeMaintainer owns link-node maintenance derived from channel state.
type LinkNodeMaintainer interface {
	// PruneLinkNodes attempts to prune all link nodes found within the
	// database with whom we no longer have any open channels with.
	PruneLinkNodes() error

	// RepairLinkNodes scans all channels in the database and ensures
	// that a link node exists for each remote peer. This should be
	// called on startup to ensure that our database is consistent.
	RepairLinkNodes(network wire.BitcoinNet) error
}
