package contractcourt

import (
	"math"

	"github.com/btcsuite/btcd/wire"
	"github.com/lightningnetwork/lnd/chainntnfs"
	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/lightningnetwork/lnd/input"
)

// htlcLeaseResolver is a struct that houses the lease specific HTLC resolution
// logic. This includes deriving the _true_ waiting height, as well as the
// input to offer to the sweeper.
type htlcLeaseResolver struct {
	// channelInitiator denotes whether the party responsible for resolving
	// the contract initiated the channel.
	channelInitiator bool

	// leaseExpiry denotes the additional waiting period the contract must
	// hold until it can be resolved. This waiting period is known as the
	// expiration of a script-enforced leased channel and only applies to
	// the channel initiator.
	//
	// NOTE: This value should only be set when the contract belongs to a
	// leased channel.
	leaseExpiry uint32
}

// hasCLTV denotes whether the resolver must wait for an additional CLTV to
// expire before resolving the contract.
func (h *htlcLeaseResolver) hasCLTV() bool {
	return h.channelInitiator && h.leaseExpiry > 0
}

// deriveWaitHeight computes the height the resolver needs to wait until it can
// sweep the input.
func (h *htlcLeaseResolver) deriveWaitHeight(csvDelay uint32,
	commitSpend *chainntnfs.SpendDetail) uint32 {

	waitHeight := uint32(commitSpend.SpendingHeight) + csvDelay - 1
	if h.hasCLTV() {
		waitHeight = uint32(math.Max(
			float64(waitHeight), float64(h.leaseExpiry),
		))
	}

	return waitHeight
}

// makeSweepInput constructs the type of input (either just csv or csv+ctlv) to
// send to the sweeper so the output can ultimately be swept.
func (h *htlcLeaseResolver) makeSweepInput(op *wire.OutPoint,
	wType, cltvWtype input.StandardWitnessType,
	signDesc *input.SignDescriptor,
	csvDelay, broadcastHeight uint32, payHash [32]byte) *input.BaseInput {

	if h.hasCLTV() {
		log.Infof("%T(%x): CSV and CLTV locks expired, offering "+
			"second-layer output to sweeper: %v", h, payHash, op)

		return input.NewCsvInputWithCltv(
			op, cltvWtype, signDesc,
			broadcastHeight, csvDelay,
			h.leaseExpiry,
		)
	}

	log.Infof("%T(%x): CSV lock expired, offering second-layer output to "+
		"sweeper: %v", h, payHash, op)

	return input.NewCsvInput(op, wType, signDesc, broadcastHeight, csvDelay)
}

// SupplementState allows the user of a ContractResolver to supplement it with
// state required for the proper resolution of a contract.
//
// NOTE: Part of the ContractResolver interface.
func (h *htlcLeaseResolver) SupplementState(state *channeldb.OpenChannel) {
	if state.ChanType.HasLeaseExpiration() {
		h.leaseExpiry = state.ThawHeight
	}
	h.channelInitiator = state.IsInitiator
}
