package signature

import (
	"crypto/ecdsa"

	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
	crypto "git.frostfs.info/TrueCloudLab/frostfs-crypto"
)

type DataSource interface {
	ReadSignedData([]byte) ([]byte, error)
	SignedDataSize() int
}

type DataWithSignature interface {
	DataSource
	GetSignature() *refs.Signature
	SetSignature(*refs.Signature)
}

type SignOption func(*cfg)

type KeySignatureHandler func(*refs.Signature)

type KeySignatureSource func() *refs.Signature

func SignDataWithHandler(key *ecdsa.PrivateKey, src DataSource, handler KeySignatureHandler, opts ...SignOption) error {
	if key == nil {
		return crypto.ErrEmptyPrivateKey
	}

	cfg := defaultCfg()

	for i := range opts {
		opts[i](cfg)
	}

	buffer := newBufferFromPool(src.SignedDataSize())
	defer returnBufferToPool(buffer)

	data, err := src.ReadSignedData(buffer.data)
	if err != nil {
		return err
	}

	sigData, err := sign(cfg, key, data)
	if err != nil {
		return err
	}

	sig := new(refs.Signature)
	sig.SetScheme(cfg.scheme)
	sig.SetKey(crypto.MarshalPublicKey(&key.PublicKey))
	sig.SetSign(sigData)
	handler(sig)

	return nil
}

func VerifyDataWithSource(dataSrc DataSource, sigSrc KeySignatureSource, opts ...SignOption) error {
	cfg := defaultCfg()

	for i := range opts {
		opts[i](cfg)
	}

	buffer := newBufferFromPool(dataSrc.SignedDataSize())
	defer returnBufferToPool(buffer)

	data, err := dataSrc.ReadSignedData(buffer.data)
	if err != nil {
		return err
	}

	return verify(cfg, data, sigSrc())
}

func SignData(key *ecdsa.PrivateKey, v DataWithSignature, opts ...SignOption) error {
	return SignDataWithHandler(key, v, v.SetSignature, opts...)
}

func VerifyData(src DataWithSignature, opts ...SignOption) error {
	return VerifyDataWithSource(src, src.GetSignature, opts...)
}