package ssss

import (
	"bytes"
	"crypto/rand"
	"encoding/asn1"
	"fmt"
	"io"
	"math/big"
	"testing"

	"xdx.jelly/xgcl/gmath"
)

func TestSharingASN1(t *testing.T) {
	threshold := 3
	n := 5

	// 16字节b需要拆分
	b := make([]byte, 16)
	_, _ = io.ReadFull(rand.Reader, b)

	// Shares是n个秘密分片
	shares, err := Split(b, threshold, n, 0, rand.Reader)
	if err != nil {
		t.Fatal(err)
	}

	// recoverdShares 凑齐threshold个秘密分片
	recoverdShares := make([][]byte, 0)
	i := 0
	for _, s := range shares {
		recoverdShares = append(recoverdShares, s)
		i++
		if i == threshold {
			break
		}
	}

	// recovered是恢复的秘密值
	recovered, err := Restore(recoverdShares)
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Equal(b, recovered) {
		t.Fatal("recoverd and secret are different")
	}
}

func TestSharingASN1Fuzz(t *testing.T) {
	b := make([]byte, 16)
	for k := 0; k < 1000; k++ {
		for n := 3; n < 8; n++ {
			for threshold := 1; threshold <= n; threshold++ {
				// 16字节b需要拆分
				_, _ = io.ReadFull(rand.Reader, b)
				shares, err := Split(b, threshold, n, 0, rand.Reader)
				if err != nil {
					t.Fatal(err)
				}
				recoverdShares := make([][]byte, 0)
				i := 0
				for _, s := range shares {
					recoverdShares = append(recoverdShares, s)
					i++
					if i == threshold {
						break
					}
				}

				recovered, err := Restore(recoverdShares)
				if err != nil {
					t.Fatal(err)
				}

				if !bytes.Equal(b, recovered) {
					t.Fatal("recoverd and secret are different")
				}
			}
		}
	}
}

func TestFieldMul(t *testing.T) {

	f := newGF2x(8)
	var x *big.Int
	y := new(big.Int)
	one := new(big.Int).SetInt64(1)

	for k := 0; k < 10000; k++ {
		x, _ = f.rand(rand.Reader)
		y.Set(one)
		for i := 0; i < 1<<8-1; i++ {
			f.mul(y, y, x)
		}
		if y.Cmp(one) != 0 {
			t.Log("error x:", x.Text(16))
			t.Fatal("mul error")
		}
	}
}

func TestFieldInvert(t *testing.T) {

	f := newGF2x(8)
	z := new(big.Int)
	y := new(big.Int)
	one := new(big.Int).SetInt64(1)

	for k := 0; k < 10000; k++ {
		x, _ := f.rand(rand.Reader)
		if f.invert(z, x) != nil {
			t.Fatal()
		}
		f.mul(y, x, z)
		if y.Cmp(one) != 0 {
			t.Fatal("invert error")
		}
	}

}

func TestHorner(t *testing.T) {
	//f(x) = x^2 + x + 2
	f := newGF2x(64)
	coeff := []*big.Int{
		new(big.Int).SetInt64(2),
		new(big.Int).SetInt64(1),
	}

	x := new(big.Int).SetInt64(3)

	y := new(big.Int)
	horner(y, x, coeff, f)
	if y.Cmp(gmath.BigInt4) != 0 {
		t.Fatal()
	}
}

func TestSharing(t *testing.T) {
	b := make([]byte, 16)
	f := newGF2x(128)
	for k := 0; k < 1000; k++ {
		// 16字节b需要拆分
		_, _ = io.ReadFull(rand.Reader, b)
		// secret, _ := f.rand(rand.Reader)
		secret := new(big.Int).SetBytes(b)
		for n := 3; n < 8; n++ {
			for threshold := 1; threshold <= n; threshold++ {
				shares, err := split(rand.Reader, threshold, n, secret, f)
				if err != nil {
					t.Fatal(err)
				}
				recovered, err := restore(threshold, shares, f)
				if err != nil || recovered.Cmp(secret) != 0 {
					t.Log("restore secret:", recovered.Text(16))
					t.Log("secret        :", secret.Text(16))
					t.Fatal(err)
				}
			}
		}
	}
}

func TestSigle(t *testing.T) {
	f := newGF2x(64)
	secret, _ := f.rand(rand.Reader)
	threshold := 1
	n := 5
	shares, err := split(rand.Reader, threshold, n, secret, f)
	if err != nil {
		t.Fatal(err)
	}
	for k, v := range shares {
		t.Logf("%d-%s\n", k, v.Text(16))
	}

	var v0 big.Int
	for k, v := range shares {
		v0.SetInt64(int64(k))
		f.add(&v0, &v0, v)
		if v0.Cmp(secret) != 0 {
			t.Fatal()
		}
	}

	recovered, err := restore(threshold, shares, f)
	if err != nil || recovered.Cmp(secret) != 0 {
		t.Log("restore secret:", recovered.Text(16))
		t.Log("secret        :", secret.Text(16))
		t.Fatal(err)
	}
}

func TestASN1(t *testing.T) {
	slice := SharedSlice{
		Version:   0,
		Threshold: 3,
		Length:    16,
		X:         1,
		Y:         new(big.Int).SetUint64(16),
		Token:     make([]byte, 16),
	}
	b, err := asn1.Marshal(slice)
	if err != nil {
		t.Fatal(err)
	}
	fmt.Printf("%x\n", b)

	slice2 := new(SharedSlice)
	_, err = asn1.Unmarshal(b, slice2)
	fmt.Println(err, slice2)
}
