package signature

import (
	"crypto/ecdsa"
	"encoding/base64"
	"fmt"

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

type cfg struct {
	schemeFixed bool
	scheme      refs.SignatureScheme
}

func defaultCfg() *cfg {
	return new(cfg)
}

func verify(cfg *cfg, data []byte, sig *refs.Signature) error {
	if !cfg.schemeFixed {
		cfg.scheme = sig.GetScheme()
	}

	pub := crypto.UnmarshalPublicKey(sig.GetKey())
	if pub == nil {
		return crypto.ErrEmptyPublicKey
	}

	switch cfg.scheme {
	case refs.ECDSA_SHA512:
		return crypto.Verify(pub, data, sig.GetSign())
	case refs.ECDSA_RFC6979_SHA256:
		return crypto.VerifyRFC6979(pub, data, sig.GetSign())
	case refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT:
		buffer := buffersPool.Get(uint32(base64.StdEncoding.EncodedLen(len(data))))
		defer buffersPool.Put(buffer)
		base64.StdEncoding.Encode(buffer.Data, data)
		if !walletconnect.Verify(pub, buffer.Data, sig.GetSign()) {
			return crypto.ErrInvalidSignature
		}
		return nil
	default:
		return fmt.Errorf("unsupported signature scheme %s", cfg.scheme)
	}
}

func sign(cfg *cfg, key *ecdsa.PrivateKey, data []byte) ([]byte, error) {
	switch cfg.scheme {
	case refs.ECDSA_SHA512:
		return crypto.Sign(key, data)
	case refs.ECDSA_RFC6979_SHA256:
		return crypto.SignRFC6979(key, data)
	case refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT:
		buffer := buffersPool.Get(uint32(base64.StdEncoding.EncodedLen(len(data))))
		defer buffersPool.Put(buffer)
		base64.StdEncoding.Encode(buffer.Data, data)
		return walletconnect.Sign(key, buffer.Data)
	default:
		panic(fmt.Sprintf("unsupported scheme %s", cfg.scheme))
	}
}

func SignWithRFC6979() SignOption {
	return func(c *cfg) {
		c.schemeFixed = true
		c.scheme = refs.ECDSA_RFC6979_SHA256
	}
}

func SignWithWalletConnect() SignOption {
	return func(c *cfg) {
		c.scheme = refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT
	}
}