package dsig

import (
	"crypto/hmac"
	"fmt"
	"hash"
)

func toHMACKey(dst *[]byte, key any) error {
	keyBytes, ok := key.([]byte)
	if !ok {
		return fmt.Errorf(`dsig.toHMACKey: invalid key type %T. []byte is required`, key)
	}

	if len(keyBytes) == 0 {
		return fmt.Errorf(`dsig.toHMACKey: missing key while signing payload`)
	}

	*dst = keyBytes
	return nil
}

// SignHMAC generates an HMAC signature for the given payload using the specified hash function and key.
// The raw parameter should be the pre-computed signing input (typically header.payload).
func SignHMAC(key, payload []byte, hfunc func() hash.Hash) ([]byte, error) {
	h := hmac.New(hfunc, key)
	if _, err := h.Write(payload); err != nil {
		return nil, fmt.Errorf(`failed to write payload using hmac: %w`, err)
	}
	return h.Sum(nil), nil
}

// VerifyHMACDigest verifies an HMAC signature given a pre-computed MAC.
func VerifyHMACDigest(computedMAC, signature []byte) error {
	if !hmac.Equal(computedMAC, signature) {
		return NewVerificationError("invalid HMAC signature")
	}
	return nil
}

// VerifyHMAC verifies an HMAC signature for the given payload.
// This function verifies the signature using the specified key and hash function.
// The payload parameter should be the pre-computed signing input (typically header.payload).
func VerifyHMAC(key, payload, signature []byte, hfunc func() hash.Hash) error {
	expected, err := SignHMAC(key, payload, hfunc)
	if err != nil {
		return fmt.Errorf("failed to sign payload for verification: %w", err)
	}
	if !hmac.Equal(signature, expected) {
		return NewVerificationError("invalid HMAC signature")
	}
	return nil
}
