package lite

import (
	"bytes"

	"github.com/pkg/errors"

	"github.com/tendermint/tendermint/types"

	liteErr "github.com/tendermint/tendermint/lite/errors"
)

var _ Certifier = (*StaticCertifier)(nil)

// StaticCertifier assumes a static set of validators, set on
// initilization and checks against them.
// The signatures on every header is checked for > 2/3 votes
// against the known validator set upon Certify
//
// Good for testing or really simple chains.  Building block
// to support real-world functionality.
type StaticCertifier struct {
	chainID string
	vSet    *types.ValidatorSet
	vhash   []byte
}

// NewStaticCertifier returns a new certifier with a static validator set.
func NewStaticCertifier(chainID string, vals *types.ValidatorSet) *StaticCertifier {
	return &StaticCertifier{
		chainID: chainID,
		vSet:    vals,
	}
}

// ChainID returns the chain id.
// Implements Certifier.
func (sc *StaticCertifier) ChainID() string {
	return sc.chainID
}

// Validators returns the validator set.
func (sc *StaticCertifier) Validators() *types.ValidatorSet {
	return sc.vSet
}

// Hash returns the hash of the validator set.
func (sc *StaticCertifier) Hash() []byte {
	if len(sc.vhash) == 0 {
		sc.vhash = sc.vSet.Hash()
	}
	return sc.vhash
}

// Certify makes sure that the commit is valid.
// Implements Certifier.
func (sc *StaticCertifier) Certify(commit Commit) error {
	// do basic sanity checks
	err := commit.ValidateBasic(sc.chainID)
	if err != nil {
		return err
	}

	// make sure it has the same validator set we have (static means static)
	if !bytes.Equal(sc.Hash(), commit.Header.ValidatorsHash) {
		return liteErr.ErrValidatorsChanged()
	}

	// then make sure we have the proper signatures for this
	err = sc.vSet.VerifyCommit(sc.chainID, commit.Commit.BlockID,
		commit.Header.Height, commit.Commit)
	return errors.WithStack(err)
}
