package sm2m

import (
	"crypto/rand"
	"fmt"
	"io"
	"math/big"

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

var (
	ErrIndataError = fmt.Errorf("data input error")
)

type ClientDecContext struct {
	cipher sm2.Cipher
}

func NewClientDecContext() *ClientDecContext {
	return &ClientDecContext{cipher: *sm2.NewCipher()}
}

// Initial 客户端解密，将密文中的C1发送给服务端
func (c *ClientDecContext) Initial(cipher *sm2.Cipher) (out []byte, err error) {
	c.cipher.Set(cipher)
	out = make([]byte, 0, 2*sm2.ByteSize())
	out = append(out, gmath.BigIntToNByte(cipher.X, sm2.ByteSize())...)
	out = append(out, gmath.BigIntToNByte(cipher.Y, sm2.ByteSize())...)
	return out, nil
}

// Final 客户端解密得到密文
func (c *ClientDecContext) Final(clientKey *sm2.PrivateKey, in []byte) ([]byte, error) {
	if len(in) < 2*sm2.ByteSize() {
		return []byte{}, ErrIndataError
	}
	// d*C1 = dc^(-1)*ds^(-1)*C1 - C1
	x := new(big.Int)
	x.SetBytes(in[:sm2.ByteSize()])
	y := new(big.Int)
	y.SetBytes(in[sm2.ByteSize() : 2*sm2.ByteSize()])
	cInv := new(big.Int)
	cInv.Set(clientKey.D)
	cInv.ModInverse(cInv, sm2.OrderN())

	x, y = sm2.Curve256.ScalarMult(x, y, cInv.Bytes())
	gx := new(big.Int)
	gy := new(big.Int)
	gx.Set(c.cipher.X)
	gy.Set(c.cipher.Y)
	gy.Sub(sm2.Prime(), gy)
	x, y = sm2.Curve().Add(x, y, gx, gy)

	return sm2.Decrypt_aux(x, y, &c.cipher)
}

func ServerDec(serverKey *sm2.PrivateKey, in []byte) ([]byte, error) {
	return ServerImportKey(serverKey, in)
}

// 将加密密钥拆分为服务端和客户端的密钥分量
func SplitDecryptKey(de *sm2.PrivateKey, rnd io.Reader) (*sm2.PrivateKey, *sm2.PrivateKey, error) {
	dc := new(big.Int).Add(de.D, gmath.BigInt1)
	if dc.Cmp(sm2.OrderN()) == 0 {
		return &sm2.PrivateKey{D: big.NewInt(0)}, &sm2.PrivateKey{D: big.NewInt(0)}, nil
	}

	var ds *big.Int
	var err error
	for {
		ds, err = rand.Int(rnd, sm2.OrderN())
		if err != nil {
			return nil, nil, err
		}
		if ds.Sign() > 0 {
			break
		}
	}

	dc.Mul(dc, ds)
	dc.Mod(dc, sm2.OrderN())
	dc.ModInverse(dc, sm2.OrderN())

	skc := &sm2.PrivateKey{D: dc}
	sks := &sm2.PrivateKey{D: ds}
	return skc, sks, nil
}
