package authority

import (
	"crypto"
	"io"

	"go.step.sm/crypto/kms"
	kmsapi "go.step.sm/crypto/kms/apiv1"

	"github.com/smallstep/certificates/authority/provisioner"
)

// Meter wraps the set of defined callbacks for metrics gatherers.
type Meter interface {
	// X509Signed is called whenever an X509 certificate is signed.
	X509Signed(provisioner.Interface, error)

	// X509Renewed is called whenever an X509 certificate is renewed.
	X509Renewed(provisioner.Interface, error)

	// X509Rekeyed is called whenever an X509 certificate is rekeyed.
	X509Rekeyed(provisioner.Interface, error)

	// X509WebhookAuthorized is called whenever an X509 authoring webhook is called.
	X509WebhookAuthorized(provisioner.Interface, error)

	// X509WebhookEnriched is called whenever an X509 enriching webhook is called.
	X509WebhookEnriched(provisioner.Interface, error)

	// SSHSigned is called whenever an SSH certificate is signed.
	SSHSigned(provisioner.Interface, error)

	// SSHRenewed is called whenever an SSH certificate is renewed.
	SSHRenewed(provisioner.Interface, error)

	// SSHRekeyed is called whenever an SSH certificate is rekeyed.
	SSHRekeyed(provisioner.Interface, error)

	// SSHWebhookAuthorized is called whenever an SSH authoring webhook is called.
	SSHWebhookAuthorized(provisioner.Interface, error)

	// SSHWebhookEnriched is called whenever an SSH enriching webhook is called.
	SSHWebhookEnriched(provisioner.Interface, error)

	// KMSSigned is called per KMS signer signature.
	KMSSigned(error)
}

// noopMeter implements a noop [Meter].
type noopMeter struct{}

func (noopMeter) SSHRekeyed(provisioner.Interface, error)            {}
func (noopMeter) SSHRenewed(provisioner.Interface, error)            {}
func (noopMeter) SSHSigned(provisioner.Interface, error)             {}
func (noopMeter) SSHWebhookAuthorized(provisioner.Interface, error)  {}
func (noopMeter) SSHWebhookEnriched(provisioner.Interface, error)    {}
func (noopMeter) X509Rekeyed(provisioner.Interface, error)           {}
func (noopMeter) X509Renewed(provisioner.Interface, error)           {}
func (noopMeter) X509Signed(provisioner.Interface, error)            {}
func (noopMeter) X509WebhookAuthorized(provisioner.Interface, error) {}
func (noopMeter) X509WebhookEnriched(provisioner.Interface, error)   {}
func (noopMeter) KMSSigned(error)                                    {}

type instrumentedKeyManager struct {
	kms.KeyManager
	meter Meter
}

func (i *instrumentedKeyManager) CreateSigner(req *kmsapi.CreateSignerRequest) (s crypto.Signer, err error) {
	if s, err = i.KeyManager.CreateSigner(req); err == nil {
		s = &instrumentedKMSSigner{s, i.meter}
	}

	return
}

type instrumentedKMSSigner struct {
	crypto.Signer
	meter Meter
}

func (i *instrumentedKMSSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
	signature, err = i.Signer.Sign(rand, digest, opts)
	i.meter.KMSSigned(err)

	return
}
