package v4a

import (
	
	
	
	

	awsmiddleware 
	v4 
	
	smithyhttp 
)

// HTTPSigner is SigV4a HTTP signer implementation
type HTTPSigner interface {
	SignHTTP(ctx context.Context, credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, optfns ...func(*SignerOptions)) error
}

// SignHTTPRequestMiddlewareOptions is the middleware options for constructing a SignHTTPRequestMiddleware.
type SignHTTPRequestMiddlewareOptions struct {
	Credentials CredentialsProvider
	Signer      HTTPSigner
	LogSigning  bool
}

// SignHTTPRequestMiddleware is a middleware for signing an HTTP request using SigV4a.
type SignHTTPRequestMiddleware struct {
	credentials CredentialsProvider
	signer      HTTPSigner
	logSigning  bool
}

// NewSignHTTPRequestMiddleware constructs a SignHTTPRequestMiddleware using the given SignHTTPRequestMiddlewareOptions.
func ( SignHTTPRequestMiddlewareOptions) *SignHTTPRequestMiddleware {
	return &SignHTTPRequestMiddleware{
		credentials: .Credentials,
		signer:      .Signer,
		logSigning:  .LogSigning,
	}
}

// ID the middleware identifier.
func ( *SignHTTPRequestMiddleware) () string {
	return "Signing"
}

// HandleFinalize signs an HTTP request using SigV4a.
func ( *SignHTTPRequestMiddleware) (
	 context.Context,  middleware.FinalizeInput,  middleware.FinalizeHandler,
) (
	 middleware.FinalizeOutput,  middleware.Metadata,  error,
) {
	if !hasCredentialProvider(.credentials) {
		return .HandleFinalize(, )
	}

	,  := .Request.(*smithyhttp.Request)
	if ! {
		return , , fmt.Errorf("unexpected request middleware type %T", .Request)
	}

	,  := awsmiddleware.GetSigningName(), awsmiddleware.GetSigningRegion()
	 := v4.GetPayloadHash()
	if len() == 0 {
		return , , &SigningError{Err: fmt.Errorf("computed payload hash missing from context")}
	}

	,  := .credentials.RetrievePrivateKey()
	if  != nil {
		return , , &SigningError{Err: fmt.Errorf("failed to retrieve credentials: %w", )}
	}

	 = .signer.SignHTTP(, , .Request, , , []string{}, time.Now().UTC(), func( *SignerOptions) {
		.Logger = middleware.GetLogger()
		.LogSigning = .logSigning
	})
	if  != nil {
		return , , &SigningError{Err: fmt.Errorf("failed to sign http request, %w", )}
	}

	return .HandleFinalize(, )
}

func ( CredentialsProvider) bool {
	if  == nil {
		return false
	}

	return true
}

// RegisterSigningMiddleware registers the SigV4a signing middleware to the stack. If a signing middleware is already
// present, this provided middleware will be swapped. Otherwise the middleware will be added at the tail of the
// finalize step.
func ( *middleware.Stack,  *SignHTTPRequestMiddleware) ( error) {
	const  = "Signing"
	,  := .Finalize.Get()
	if  {
		_,  = .Finalize.Swap(, )
	} else {
		 = .Finalize.Add(, middleware.After)
	}
	return 
}