package chain33
import (
	"errors"
	"fmt"
	"strings"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/crypto"
)

type EIP191 struct {
	msg       string
	signature string
	address   string
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func hasValidLastByte(sig []byte) bool {
	return sig[64] == 0 || sig[64] == 1
}

func hasMatchingAddress(knownAddress string, recoveredAddress string) bool {
	return strings.ToLower(knownAddress) == strings.ToLower(recoveredAddress)
}

func signEIP191(message string) common.Hash {
	msg := []byte(message)
	formattedMsg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(msg), msg)
	return crypto.Keccak256Hash([]byte(formattedMsg))
}

func VerifyPersonalSign(eipChallenge EIP191) (bool,error) {
	decodedSig, err := hexutil.Decode(eipChallenge.signature)
	if err != nil {
		return false,err
	}

	if decodedSig[64] < 27 {
		if !hasValidLastByte(decodedSig) {
			//panic("Invalid last byte")
			return false, errors.New("Invalid last byte")
		}
	} else {
		decodedSig[64] -= 27 // shift byte?
	}

	hash := signEIP191(eipChallenge.msg)

	recoveredPublicKey, err := crypto.Ecrecover(hash.Bytes(), decodedSig)
	if err != nil {
		return false,err
	}

	secp256k1RecoveredPublicKey, err := crypto.UnmarshalPubkey(recoveredPublicKey)
	if err != nil {
		return false,err
	}

	recoveredAddress := crypto.PubkeyToAddress(*secp256k1RecoveredPublicKey).Hex()

	fmt.Println("Signature matches known address!","recover addr ",recoveredAddress ,"req addr",eipChallenge.address)

	return hasMatchingAddress(eipChallenge.address, recoveredAddress),nil
}