package validator

import (
	"github.com/iotaledger/goshimmer/packages/events"
	"github.com/iotaledger/goshimmer/packages/model/bundle"
	"github.com/iotaledger/goshimmer/packages/model/value_transaction"
	"github.com/iotaledger/goshimmer/packages/node"
	"github.com/iotaledger/goshimmer/plugins/bundleprocessor"
	"github.com/iotaledger/iota.go/address"
	. "github.com/iotaledger/iota.go/consts"
	"github.com/iotaledger/iota.go/signing"
	. "github.com/iotaledger/iota.go/trinary"
)

var PLUGIN = node.NewPlugin("Validator", configure, run)

// Creates bundle signature fragments and the corresponding address to validate against.
// Each signature fragment after the first must go into its own meta transaction with value = 0.
func demoSign(seed Trytes, index uint64, sec SecurityLevel, bundleHash Hash) (Hash, []Trytes) {
	addr, _ := address.GenerateAddress(seed, index, sec)

	// compute seed based on address index
	subseed, _ := signing.Subseed(seed, index)
	// generate the private key
	prvKey, _ := signing.Key(subseed, sec)

	normalizedBundleHash := signing.NormalizedBundleHash(bundleHash)

	signatureFragments := make([]Trytes, sec)
	for i := 0; i < int(sec); i++ {
		// each security level signs one third of the (normalized) bundle hash
		signedFragTrits, _ := signing.SignatureFragment(
			normalizedBundleHash[i*HashTrytesSize/3:(i+1)*HashTrytesSize/3],
			prvKey[i*KeyFragmentLength:(i+1)*KeyFragmentLength],
		)
		signatureFragments[i] = MustTritsToTrytes(signedFragTrits)
	}

	return addr, signatureFragments
}

func validateSignatures(bundleHash Hash, txs []*value_transaction.ValueTransaction) (bool, error) {
	for i, tx := range txs {
		// ignore all non-input transactions
		if tx.GetValue() >= 0 {
			continue
		}

		address := tx.GetAddress()

		// it is unknown how many fragments there will be
		fragments := []Trytes{tx.GetSignatureMessageFragment()}

		// each consecutive meta transaction with the same address contains another signature fragment
		for j := i; j < len(txs)-1; j++ {
			otherTx := txs[j+1]
			if otherTx.GetValue() != 0 || otherTx.GetAddress() != address {
				break
			}

			fragments = append(fragments, otherTx.GetSignatureMessageFragment())
		}

		// validate all the fragments against the address using Kerl
		valid, err := signing.ValidateSignatures(address, fragments, bundleHash, signing.NewKerl)
		if err != nil {
			return false, err
		}
		if !valid {
			return false, nil
		}
	}

	return true, nil
}

func configure(plugin *node.Plugin) {

	bundleprocessor.Events.BundleSolid.Attach(events.NewClosure(func(b *bundle.Bundle, txs []*value_transaction.ValueTransaction) {
		// signature are verified against the bundle hash
		valid, _ := validateSignatures(b.GetBundleEssenceHash(), txs)
		if !valid {
			plugin.LogFailure("Invalid signature")
		}
	}))
}

func run(*node.Plugin) {
}