package dkgc

import (
	"crypto/rand"
	"fmt"
	"math/big"
	"testing"
	"time"

	"xdx.jelly/xgcl/grand"
	"xdx.jelly/xgcl/internal"
	"xdx.jelly/xgcl/sm/sm9"
)

func spiner() {
	start := time.Now()
	for {
		for _, c := range "|/-\\" {
			fmt.Printf("\r%c  ", c)
			now := time.Since(start)
			s := int(now.Seconds())
			if s > 3600 {
				fmt.Printf("%dh%dm%ds       ", s/3600, s%3600/60, s%60)
			} else if s > 60 {
				fmt.Printf("%dm%ds       ", s/60, s%60)
			} else {
				fmt.Printf("%.1fs        ", now.Seconds())
			}
			time.Sleep(100 * time.Millisecond)
		}
	}
}

func BenchmarkReportSpeedDKGC(b *testing.B) {

	ks1, pubs1, err := sm9.GenerateMastSignPrivateKey(rand.Reader)
	if err != nil {
		b.Fatal(err)
	}
	ks2, pubs2, err := sm9.GenerateMastSignPrivateKey(rand.Reader)
	if err != nil {
		b.Fatal(err)
	}

	ks := new(sm9.MastSignPrivateKey)
	ks.Int.Mul(&ks1.Int, &ks2.Int)
	ks.Int.Mod(&ks.Int, sm9.Order())
	ks.Public().G2.ScalarBaseMult(&ks.Int)
	pubs := ks.Public()

	uid := grand.GetRandom(10)
	msg := grand.GetRandom(10)
	// uid := []byte("Alice")
	// msg := []byte("Chinese IBS standard")
	ra, R, err := UserRandom0(rand.Reader, sm9.G1Generator())
	if err != nil {
		b.Fatal(err)
	}

	_, hh, _ := ComputeHHat(uid)

	t1, T1, err := KGC1ComputeData(rand.Reader, R, ks1, hh)
	if err != nil {
		b.Fatal(err)
	}
	T2, err := KGC2ComputeData(T1, ks2, hh)
	if err != nil {
		b.Fatal(err)
	}
	ds, userPubs, err := UserComputeSignKey(uid, t1, T2, ra, pubs1, pubs2, pubs, sm9.G1Generator())
	if err != nil {
		b.Fatal(err)
	}

	signature, _ := sm9.Sign(msg, ds, userPubs, grand.GetRandom(32))

	//bn256.SetUseLattice(false)
	if !sm9.Verify(signature, uid, msg, userPubs) {
		b.Logf("\n%x\n%x\n", uid, msg)
		b.Fatal("verify failed")
	}

}

func TestDKGC(t *testing.T) {
	go spiner()
	for {
		for i := 1; i < 1000; i++ {
			ks1, pubs1, err := sm9.GenerateMastSignPrivateKey(rand.Reader)
			if err != nil {
				t.Fatal(err)
			}
			ks2, pubs2, err := sm9.GenerateMastSignPrivateKey(rand.Reader)
			if err != nil {
				t.Fatal(err)
			}

			ks := new(sm9.MastSignPrivateKey)
			ks.Int.Mul(&ks1.Int, &ks2.Int)
			ks.Int.Mod(&ks.Int, sm9.Order())
			ks.Public().G2.ScalarBaseMult(&ks.Int)
			pubs := ks.Public()

			uid := grand.GetRandom(i)
			msg := grand.GetRandom(i)
			// uid := []byte("Alice")
			// msg := []byte("Chinese IBS standard")
			ra, R, err := UserRandom0(rand.Reader, sm9.G1Generator())
			if err != nil {
				t.Fatal(err)
			}

			_, hh, _ := ComputeHHat(uid)

			t1, T1, err := KGC1ComputeData(rand.Reader, R, ks1, hh)
			if err != nil {
				t.Fatal(err)
			}
			T2, err := KGC2ComputeData(T1, ks2, hh)
			if err != nil {
				t.Fatal(err)
			}
			ds, userPubs, err := UserComputeSignKey(uid, t1, T2, ra, pubs1, pubs2, pubs, sm9.G1Generator())
			if err != nil {
				t.Fatal(err)
			}

			signature, _ := sm9.Sign(msg, ds, userPubs, grand.GetRandom(32))
			//bn256.SetUseLattice(false)
			if !sm9.Verify(signature, uid, msg, userPubs) {
				t.Logf("\n%x\n%x\n", uid, msg)
				t.Fatal("verify failed")
			}
		}
	}
}

func TestDKGCWithTPC(t *testing.T) {
	go spiner()
	for {
		for i := 1; i < 1000; i++ {
			ks1, pubs1, err := sm9.GenerateMastSignPrivateKey(rand.Reader)
			if err != nil {
				t.Fatal(err)
			}
			ks2, pubs2, err := sm9.GenerateMastSignPrivateKey(rand.Reader)
			if err != nil {
				t.Fatal(err)
			}

			ks := new(sm9.MastSignPrivateKey)
			ks.Int.Mul(&ks1.Int, &ks2.Int)
			ks.Int.Mod(&ks.Int, sm9.Order())
			ks.Public().G2.ScalarBaseMult(&ks.Int)
			pubs := ks.Public()

			uid := grand.GetRandom(i)
			msg := grand.GetRandom(i)
			// uid := []byte("Alice")
			// msg := []byte("Chinese IBS standard")

			serverKey, basePoint, err := ServerGenerateKey(rand.Reader)
			if err != nil {
				t.Fatal(err)
			}

			ra, R, err := UserRandom0(rand.Reader, &basePoint.G1)
			if err != nil {
				t.Fatal(err)
			}

			_, hh, _ := ComputeHHat(uid)

			t1, T1, err := KGC1ComputeData(rand.Reader, R, ks1, hh)
			if err != nil {
				t.Fatal(err)
			}
			T2, err := KGC2ComputeData(T1, ks2, hh)
			if err != nil {
				t.Fatal(err)
			}

			clientKey, userPubs, err := UserComputeSignKey(uid, t1, T2, ra, pubs1, pubs2, pubs, &basePoint.G1)
			if err != nil {
				t.Fatal(err)
			}

			// signature := sm9.Sign(msg, ds, userPubs, grand.GetRandom(32))
			csc := &ClientSignContext{}
			u, err := csc.ComputeSignData(rand.Reader, userPubs)
			if err != nil {
				t.Fatal(err)
			}
			h, k1, k2, err := ServerComputeSignData(rand.Reader, u, msg, userPubs, serverKey)
			if err != nil {
				t.Fatal(err)
			}

			signature, err := csc.ComputeSignature(uid, h, k1, k2, clientKey)
			if err != nil {
				t.Fatal(err)
			}

			if !sm9.Verify(signature, uid, msg, userPubs) {
				t.Logf("\n%x\n%x\n", uid, msg)
				t.Fatal("verify failed")
			}
		}
	}
}

func TestDKGCReport(t *testing.T) {
	ks1, pubs1, err := sm9.GenerateMastSignPrivateKey(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	ks2, pubs2, err := sm9.GenerateMastSignPrivateKey(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}

	ks := new(sm9.MastSignPrivateKey)
	ks.Int.Mul(&ks1.Int, &ks2.Int)
	ks.Int.Mod(&ks.Int, sm9.Order())
	ks.Public().G2.ScalarBaseMult(&ks.Int)
	pubs := ks.Public()

	uid := grand.GetRandom(10)
	msg := grand.GetRandom(10)

	ra, R, err := UserRandom0(rand.Reader, sm9.G1Generator())
	if err != nil {
		t.Fatal(err)
	}
	var t1 *big.Int
	var T1, T2 *sm9.G1
	count, duation := internal.SingleThreadTester(func() {
		_, hh, _ := ComputeHHat(uid)
		t1, T1, err = KGC1ComputeData(rand.Reader, R, ks1, hh)
		if err != nil {
			t.Fatal(err)
		}
	})
	fmt.Printf("KGC1 compute: %d, used time: %d ms, %d pcs/s\n", count, duation.Milliseconds(), int(internal.Rate(count, duation)))

	count, duation = internal.SingleThreadTester(func() {
		_, hh, _ := ComputeHHat(uid)
		T2, err = KGC2ComputeData(T1, ks2, hh)
		if err != nil {
			t.Fatal(err)
		}
	})
	fmt.Printf("KGC2 compute: %d, used time: %d ms, %d pcs/s\n", count, duation.Milliseconds(), int(internal.Rate(count, duation)))

	var ds *sm9.UserSignKey
	var userPubs *sm9.MastSignPublicKey
	count, duation = internal.SingleThreadTester(func() {
		ds, userPubs, err = UserComputeSignKey(uid, t1, T2, ra, pubs1, pubs2, pubs, sm9.G1Generator())
		if err != nil {
			t.Fatal(err)
		}
	})
	fmt.Printf("User compute: %d, used time: %d ms, %d pcs/s\n", count, duation.Milliseconds(), int(internal.Rate(count, duation)))

	signature, _ := sm9.Sign(msg, ds, userPubs, grand.GetRandom(32))

	if !sm9.Verify(signature, uid, msg, userPubs) {
		t.Logf("\n%x\n%x\n", uid, msg)
		t.Fatal("verify failed")
	}

}
