package feature

import (
	"reflect"
	"testing"

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

type depTest struct {
	name   string
	raw    *lnwire.RawFeatureVector
	expErr error
}

var depTests = []depTest{
	{
		name: "empty",
		raw:  lnwire.NewRawFeatureVector(),
	},
	{
		name: "no deps optional",
		raw: lnwire.NewRawFeatureVector(
			lnwire.GossipQueriesOptional,
		),
	},
	{
		name: "no deps required",
		raw: lnwire.NewRawFeatureVector(
			lnwire.TLVOnionPayloadRequired,
		),
	},
	{
		name: "one dep optional",
		raw: lnwire.NewRawFeatureVector(
			lnwire.TLVOnionPayloadRequired,
			lnwire.PaymentAddrOptional,
		),
	},
	{
		name: "one dep required",
		raw: lnwire.NewRawFeatureVector(
			lnwire.TLVOnionPayloadRequired,
			lnwire.PaymentAddrRequired,
		),
	},
	{
		name: "one missing optional",
		raw: lnwire.NewRawFeatureVector(
			lnwire.PaymentAddrOptional,
		),
		expErr: ErrMissingFeatureDep{lnwire.TLVOnionPayloadOptional},
	},
	{
		name: "one missing required",
		raw: lnwire.NewRawFeatureVector(
			lnwire.PaymentAddrRequired,
		),
		expErr: ErrMissingFeatureDep{lnwire.TLVOnionPayloadOptional},
	},
	{
		name: "two dep optional",
		raw: lnwire.NewRawFeatureVector(
			lnwire.TLVOnionPayloadRequired,
			lnwire.PaymentAddrOptional,
			lnwire.MPPOptional,
		),
	},
	{
		name: "two dep required",
		raw: lnwire.NewRawFeatureVector(
			lnwire.TLVOnionPayloadRequired,
			lnwire.PaymentAddrRequired,
			lnwire.MPPRequired,
		),
	},
	{
		name: "two dep last missing optional",
		raw: lnwire.NewRawFeatureVector(
			lnwire.PaymentAddrOptional,
			lnwire.MPPOptional,
		),
		expErr: ErrMissingFeatureDep{lnwire.TLVOnionPayloadOptional},
	},
	{
		name: "two dep last missing required",
		raw: lnwire.NewRawFeatureVector(
			lnwire.PaymentAddrRequired,
			lnwire.MPPRequired,
		),
		expErr: ErrMissingFeatureDep{lnwire.TLVOnionPayloadOptional},
	},
	{
		name: "two dep first missing optional",
		raw: lnwire.NewRawFeatureVector(
			lnwire.TLVOnionPayloadRequired,
			lnwire.MPPOptional,
		),
		expErr: ErrMissingFeatureDep{lnwire.PaymentAddrOptional},
	},
	{
		name: "two dep first missing required",
		raw: lnwire.NewRawFeatureVector(
			lnwire.TLVOnionPayloadRequired,
			lnwire.MPPRequired,
		),
		expErr: ErrMissingFeatureDep{lnwire.PaymentAddrOptional},
	},
	{
		name: "forest optional",
		raw: lnwire.NewRawFeatureVector(
			lnwire.GossipQueriesOptional,
			lnwire.TLVOnionPayloadRequired,
			lnwire.PaymentAddrOptional,
			lnwire.MPPOptional,
		),
	},
	{
		name: "forest required",
		raw: lnwire.NewRawFeatureVector(
			lnwire.GossipQueriesRequired,
			lnwire.TLVOnionPayloadRequired,
			lnwire.PaymentAddrRequired,
			lnwire.MPPRequired,
		),
	},
	{
		name: "broken forest optional",
		raw: lnwire.NewRawFeatureVector(
			lnwire.GossipQueriesOptional,
			lnwire.TLVOnionPayloadRequired,
			lnwire.MPPOptional,
		),
		expErr: ErrMissingFeatureDep{lnwire.PaymentAddrOptional},
	},
	{
		name: "broken forest required",
		raw: lnwire.NewRawFeatureVector(
			lnwire.GossipQueriesRequired,
			lnwire.TLVOnionPayloadRequired,
			lnwire.MPPRequired,
		),
		expErr: ErrMissingFeatureDep{lnwire.PaymentAddrOptional},
	},
}

// TestValidateDeps tests that ValidateDeps correctly asserts whether or not the
// set features constitute a valid feature chain when accounting for transititve
// dependencies.
func TestValidateDeps(t *testing.T) {
	for _, test := range depTests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			testValidateDeps(t, test)
		})
	}
}

func testValidateDeps(t *testing.T, test depTest) {
	fv := lnwire.NewFeatureVector(test.raw, lnwire.Features)
	err := ValidateDeps(fv)
	if !reflect.DeepEqual(err, test.expErr) {
		t.Fatalf("validation mismatch, want: %v, got: %v",
			test.expErr, err)
	}
}
