package presignedurl

import (
	
	

	awsmiddleware 
	v4 

	
)

// URLPresigner provides the interface to presign the input parameters in to a
// presigned URL.
type URLPresigner interface {
	// PresignURL presigns a URL.
	PresignURL(ctx context.Context, srcRegion string, params interface{}) (*v4.PresignedHTTPRequest, error)
}

// ParameterAccessor provides an collection of accessor to for retrieving and
// setting the values needed to PresignedURL generation
type ParameterAccessor struct {
	// GetPresignedURL accessor points to a function that retrieves a presigned url if present
	GetPresignedURL func(interface{}) (string, bool, error)

	// GetSourceRegion accessor points to a function that retrieves source region for presigned url
	GetSourceRegion func(interface{}) (string, bool, error)

	// CopyInput accessor points to a function that takes in an input, and returns a copy.
	CopyInput func(interface{}) (interface{}, error)

	// SetDestinationRegion accessor points to a function that sets destination region on api input struct
	SetDestinationRegion func(interface{}, string) error

	// SetPresignedURL accessor points to a function that sets presigned url on api input struct
	SetPresignedURL func(interface{}, string) error
}

// Options provides the set of options needed by the presigned URL middleware.
type Options struct {
	// Accessor are the parameter accessors used by this middleware
	Accessor ParameterAccessor

	// Presigner is the URLPresigner used by the middleware
	Presigner URLPresigner
}

// AddMiddleware adds the Presign URL middleware to the middleware stack.
func ( *middleware.Stack,  Options) error {
	return .Initialize.Add(&presign{options: }, middleware.Before)
}

// RemoveMiddleware removes the Presign URL middleware from the stack.
func ( *middleware.Stack) error {
	,  := .Initialize.Remove((*presign)(nil).ID())
	return 
}

type presign struct {
	options Options
}

func ( *presign) () string { return "Presign" }

func ( *presign) (
	 context.Context,  middleware.InitializeInput,  middleware.InitializeHandler,
) (
	 middleware.InitializeOutput,  middleware.Metadata,  error,
) {
	// If PresignedURL is already set ignore middleware.
	if , ,  := .options.Accessor.GetPresignedURL(.Parameters);  != nil {
		return , , fmt.Errorf("presign middleware failed, %w", )
	} else if  {
		return .HandleInitialize(, )
	}

	// If have source region is not set ignore middleware.
	, ,  := .options.Accessor.GetSourceRegion(.Parameters)
	if  != nil {
		return , , fmt.Errorf("presign middleware failed, %w", )
	} else if ! || len() == 0 {
		return .HandleInitialize(, )
	}

	// Create a copy of the original input so the destination region value can
	// be added. This ensures that value does not leak into the original
	// request parameters.
	,  := .options.Accessor.CopyInput(.Parameters)
	if  != nil {
		return , , fmt.Errorf("unable to create presigned URL, %w", )
	}

	// Destination region is the API client's configured region.
	 := awsmiddleware.GetRegion()
	if  = .options.Accessor.SetDestinationRegion(, );  != nil {
		return , , fmt.Errorf("presign middleware failed, %w", )
	}

	,  := .options.Presigner.PresignURL(, , )
	if  != nil {
		return , , fmt.Errorf("unable to create presigned URL, %w", )
	}

	// Update the original input with the presigned URL value.
	if  = .options.Accessor.SetPresignedURL(.Parameters, .URL);  != nil {
		return , , fmt.Errorf("presign middleware failed, %w", )
	}

	return .HandleInitialize(, )
}