package contractcourt

import (
	"encoding/binary"
	"io"

	"github.com/lightningnetwork/lnd/channeldb"
)

// breachResolver is a resolver that will handle breached closes. In the
// future, this will likely take over the duties the current BreachArbitrator
// has.
type breachResolver struct {
	// resolved reflects if the contract has been fully resolved or not.
	resolved bool

	// subscribed denotes whether or not the breach resolver has subscribed
	// to the BreachArbitrator for breach resolution.
	subscribed bool

	// replyChan is closed when the breach arbiter has completed serving
	// justice.
	replyChan chan struct{}

	contractResolverKit
}

// newBreachResolver instantiates a new breach resolver.
func newBreachResolver(resCfg ResolverConfig) *breachResolver {
	r := &breachResolver{
		contractResolverKit: *newContractResolverKit(resCfg),
		replyChan:           make(chan struct{}),
	}

	r.initLogger(r)

	return r
}

// ResolverKey returns the unique identifier for this resolver.
func (b *breachResolver) ResolverKey() []byte {
	key := newResolverID(b.ChanPoint)
	return key[:]
}

// Resolve queries the BreachArbitrator to see if the justice transaction has
// been broadcast.
//
// TODO(yy): let sweeper handle the breach inputs.
func (b *breachResolver) Resolve(_ bool) (ContractResolver, error) {
	if !b.subscribed {
		complete, err := b.SubscribeBreachComplete(
			&b.ChanPoint, b.replyChan,
		)
		if err != nil {
			return nil, err
		}

		// If the breach resolution process is already complete, then
		// we can cleanup and checkpoint the resolved state.
		if complete {
			b.resolved = true
			return nil, b.Checkpoint(b)
		}

		// Prevent duplicate subscriptions.
		b.subscribed = true
	}

	select {
	case <-b.replyChan:
		// The replyChan has been closed, signalling that the breach
		// has been fully resolved. Checkpoint the resolved state and
		// exit.
		b.resolved = true
		return nil, b.Checkpoint(b)
	case <-b.quit:
	}

	return nil, errResolverShuttingDown
}

// Stop signals the breachResolver to stop.
func (b *breachResolver) Stop() {
	close(b.quit)
}

// IsResolved returns true if the breachResolver is fully resolved and cleanup
// can occur.
func (b *breachResolver) IsResolved() bool {
	return b.resolved
}

// SupplementState adds additional state to the breachResolver.
func (b *breachResolver) SupplementState(_ *channeldb.OpenChannel) {
}

// Encode encodes the breachResolver to the passed writer.
func (b *breachResolver) Encode(w io.Writer) error {
	return binary.Write(w, endian, b.resolved)
}

// newBreachResolverFromReader attempts to decode an encoded breachResolver
// from the passed Reader instance, returning an active breachResolver.
func newBreachResolverFromReader(r io.Reader, resCfg ResolverConfig) (
	*breachResolver, error) {

	b := &breachResolver{
		contractResolverKit: *newContractResolverKit(resCfg),
		replyChan:           make(chan struct{}),
	}

	if err := binary.Read(r, endian, &b.resolved); err != nil {
		return nil, err
	}

	b.initLogger(b)

	return b, nil
}

// A compile time assertion to ensure breachResolver meets the ContractResolver
// interface.
var _ ContractResolver = (*breachResolver)(nil)
