//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

package bbs

import (
	"errors"
	"fmt"

	"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
	"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/signatures/common"
)

// BlindSignature is a BBS+ blind signature
// structurally identical to `Signature` but
// is used to help avoid misuse and confusion.
//
// 1 or more message have been hidden by the
// potential signature holder so the signer
// only knows a subset of the messages to be signed
type BlindSignature struct {
	a    curves.PairingPoint
	e, s curves.Scalar
}

// Init creates an empty signature to a specific curve
// which should be followed by UnmarshalBinary
func (sig *BlindSignature) Init(curve *curves.PairingCurve) *BlindSignature {
	sig.a = curve.NewG1IdentityPoint()
	sig.e = curve.NewScalar()
	sig.s = curve.NewScalar()
	return sig
}

func (sig BlindSignature) MarshalBinary() ([]byte, error) {
	out := append(sig.a.ToAffineCompressed(), sig.e.Bytes()...)
	out = append(out, sig.s.Bytes()...)
	return out, nil
}

func (sig *BlindSignature) UnmarshalBinary(data []byte) error {
	pointLength := len(sig.a.ToAffineCompressed())
	scalarLength := len(sig.s.Bytes())
	expectedLength := pointLength + scalarLength*2
	if len(data) != expectedLength {
		return fmt.Errorf("invalid byte sequence")
	}
	a, err := sig.a.FromAffineCompressed(data[:pointLength])
	if err != nil {
		return err
	}
	e, err := sig.e.SetBytes(data[pointLength:(pointLength + scalarLength)])
	if err != nil {
		return err
	}
	s, err := sig.s.SetBytes(data[(pointLength + scalarLength):])
	if err != nil {
		return err
	}
	var ok bool
	sig.a, ok = a.(curves.PairingPoint)
	if !ok {
		return errors.New("incorrect type conversion")
	}
	sig.e = e
	sig.s = s
	return nil
}

func (sig BlindSignature) ToUnblinded(blinder common.SignatureBlinding) *Signature {
	return &Signature{
		a: sig.a,
		e: sig.e,
		s: sig.s.Add(blinder),
	}
}