package sm2a

import (
	"fmt"
	"math/rand"
	"sync"
	"testing"
	"time"

	"xdx.jelly/xgcl/gmath"
	"xdx.jelly/xgcl/grand"
	"xdx.jelly/xgcl/sm/sm2"
)

func TestSign(t *testing.T) {
	// 生成客户端和服务端签名密钥分量
	serverKeySeg := NewServerSignKeyGenContext()
	clientKeySeg := NewClientSignKeyGenContext(grand.Reader)
	buf, err := serverKeySeg.ServerGenKey_one(grand.Reader)
	if err != nil {
		fmt.Println(err)
		return
	}
	buf, err = clientKeySeg.ClientKeyGen_one(buf)
	if err != nil {
		fmt.Println(err)
		return
	}
	buf, err = serverKeySeg.ServerGenKey_two(buf, grand.Reader)
	if err != nil {
		fmt.Println(err)
		return
	}
	buf, err = clientKeySeg.ClientKeyGen_two(buf)
	if err != nil {
		fmt.Println(err)
		return
	}
	err = serverKeySeg.ServerGenKey_three(buf)
	if err != nil {
		fmt.Println(err)
		return
	}
	dc := clientKeySeg.ClientKey
	ds := serverKeySeg.ServerKey
	dc.D.Mod(dc.D, sm2.Curve().Params().N)

	// 协同计算签名
	e := make([]byte, sm2.ByteSize())
	grand.GenerateRandom(e)
	printLog("签名数据e ", e)
	pk := clientKeySeg.PubKey
	printLog("终端签名分量", dc.Bytes())
	printLog("服务端签名分量", ds.Bytes())
	printLog("公钥", pk.Bytes())
	clientSign := NewClientSignContext(pk, grand.Reader)
	buf, _ = clientSign.Initial(e)
	printLog("T=", buf)

	buf, _ = ServerSign(ds, pk, buf, grand.Reader)
	printLog("r=", buf[:32])
	printLog("s1=", buf[32:])

	sig, _ := clientSign.Final(dc, buf)
	printLog("r=", sig.R.Bytes())
	printLog("s=", sig.S.Bytes())

	printLog("签名结果", sig.Bytes())

	if !sm2.Verify(e, pk, sig) {
		t.Fatal("验签失败")
	}
}

func TestBm(t *testing.T) {
	rand1 := rand.New(rand.NewSource(time.Now().UnixNano()))
	var wt time.Duration = 5

	serverKeySeg := NewServerSignKeyGenContext()
	clientKeySeg := NewClientSignKeyGenContext(grand.Reader)

	buf, err := serverKeySeg.ServerGenKey_one(grand.Reader)
	if err != nil {
		fmt.Println(err)
		return
	}
	serverctx, _ := serverKeySeg.MarshalBinary()
	fmt.Printf("%d\n", len(serverctx))
	//rand.Reader will got error? why

	buf, err = clientKeySeg.ClientKeyGen_one(buf)
	if err != nil {
		fmt.Println(err)
		return
	}
	// fmt.Printf("%x\n", buf)
	// fmt.Println("length of buf is ", len(buf))
	serverKeySeg.UnmarshalBinary(serverctx)
	buf, err = serverKeySeg.ServerGenKey_two(buf, grand.Reader)
	if err != nil {
		fmt.Println(err)
		return
	}
	serverctx, _ = serverKeySeg.MarshalBinary()
	fmt.Printf("%x\n", serverctx)

	buf, err = clientKeySeg.ClientKeyGen_two(buf)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Client public Key = ", clientKeySeg.PubKey)

	serverKeySeg.UnmarshalBinary(serverctx)
	err = serverKeySeg.ServerGenKey_three(buf)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Server public Key = ", serverKeySeg.PubKey)

	dc := clientKeySeg.ClientKey
	ds := serverKeySeg.ServerKey
	d := sm2.NewPrivateKey()
	d.D.Add(dc.D, ds.D)
	d.D.ModInverse(d.D, sm2.OrderN())
	d.D.Sub(d.D, gmath.BigInt1)
	px, py := sm2.Curve256.ScalarBaseMult(d.Bytes())
	fmt.Println("p=(", px.Text(16)+", "+py.Text(16)+")")

	e := make([]byte, sm2.ByteSize())
	pk := clientKeySeg.PubKey

	//
	wg := sync.WaitGroup{}

	totalSuccess := 0
	totalFailed := 0

	var (
		fail  float32
		total float32
	)

	beg := time.Now().UnixNano()

	for j := 0; j < 1; j++ {
		wg.Add(1)

		wt = time.Duration(rand1.Int() % 100)
		time.Sleep(wt * time.Millisecond)

		go func(wg *sync.WaitGroup) {
			defer wg.Done()

			for mi := 0; mi < 2000; mi++ {
				clientSign := NewClientSignContext(pk, grand.Reader)
				buf2, err := clientSign.Initial(e)
				if err != nil {
					totalFailed++
					continue
				}

				buf2, err = ServerSign(ds, pk, buf2, grand.Reader)
				if err != nil {
					totalFailed++
					continue
				}

				//sig, err := clientSign.Final(dc, buf2)
				_, err = clientSign.Final(dc, buf2)
				if err != nil {
					totalFailed++
					continue
				}

				totalSuccess++

				//fmt.Println(sig)
				//fmt.Println(sm2.Verify(e, pk, sig))
			}
		}(&wg)

	}

	wg.Wait()
	end := time.Now().UnixNano()

	fail = float32(totalFailed)
	total = float32(totalSuccess + totalFailed)
	rate := fail / total

	elapseDenom := float32(end - beg)
	elapse := elapseDenom / 1000000000
	one := elapse / total

	fmt.Printf("错误率: %f %% \n", rate*100)
	fmt.Printf("耗时时间(单位:秒): %f seconds \n", elapse)
	fmt.Printf("单次请求平均耗时(单位:秒) %f seconds \n", one)
	fmt.Println("TPS: ", total/elapse)
	fmt.Println("")
	fmt.Println("")
	fmt.Println("")

}
