// +build gofuzz

package jwt

import (
	"bytes"
	"crypto/rsa"
	"errors"
	"fmt"
	"math/rand"
)

var Rnd = RndReader{
	rand.New(rand.NewSource(1337)),
}

var Pkey *rsa.PrivateKey

type RndReader struct {
	*rand.Rand
}

func (r RndReader) Read(b []byte) (n int, err error) {
	for i := range b {
		b[i] = uint8(r.Intn(255))
		n++
	}

	return
}

var Secret []byte

var Algorithms []Algorithm

type BodyType struct {
	A string
	B struct{ C string }
}

var Body = BodyType{
	A: "hi",
	B: struct{ C string }{C: "hi2"},
}

func init() {
	if err := InitVars(); err != nil {
		panic(err)
	}
}

var UnsafeAlgorithmMap map[string]Algorithm

var NoSuchAlgorithm = errors.New("no such algorithm")

func UnsafeDecodeAndValidate(b []byte, h *Header, claims interface{}) (err error) {
	f, err := Parse(b)
	if err != nil {
		return
	}

	if err = f.Decode(h, claims); err != nil {
		return
	}

	alg, ok := UnsafeAlgorithmMap[h.Algorithm]
	if !ok {
		return NoSuchAlgorithm
	}

	if err = f.Validate(alg); err != nil {
		return
	}

	return
}

func InitVars() (err error) {
	if Pkey, err = rsa.GenerateKey(Rnd, 2048); err != nil {
		return
	}

	Pkey, err = ReadRSAPrivateKey("_key/id_rsa.pem")

	Secret = []byte("secret")

	Algorithms = []Algorithm{
		//no sig
		None,
		//symm
		HS256(Secret[:]),
		HS384(Secret[:]),
		HS512(Secret[:]),
		//asymm
		RS256(Pkey),
		RS384(Pkey),
		RS512(Pkey),
	}

	UnsafeAlgorithmMap = make(map[string]Algorithm)

	for _, v := range Algorithms {
		UnsafeAlgorithmMap[v.Name()] = v
	}

	return
}

func Fuzz(data []byte) int {
	f, err := Parse(data)
	if err != nil {
		return 0
	}

	var h Header
	var claims map[string]interface{}
	if err = f.Decode(&h, claims); err != nil {
		return 0
	}

	alg, ok := UnsafeAlgorithmMap[h.Algorithm]
	if !ok {
		return 0
	}

	var bt []byte

	if bt, err = Encode(h, claims, alg); err != nil {
		panic(fmt.Sprintf("unable to unmarshal something we could marshal err: %s input: %+q", err, data))
	}

	if err = f.Validate(alg); err != nil {
		return 0
	}

	var genDec Fragment
	if genDec, err = Parse(bt); err != nil {
		panic(fmt.Sprintf("can't parse generated jwt err: %s input: %+q", err, data))
	}

	if !bytes.Equal(genDec.Signature(), f.Signature()) {
		panic(fmt.Sprintf("an input passed validation, but re-generating it gives a different signature input: %+q", data))
	}

	return 1
}
