package middleware

import (
	
	
	
)

// Stack provides protocol and transport agnostic set of middleware split into
// distinct steps. Steps have specific transitions between them, that are
// managed by the individual step.
//
// Steps are composed as middleware around the underlying handler in the
// following order:
//
//   Initialize -> Serialize -> Build -> Finalize -> Deserialize -> Handler
//
// Any middleware within the chain may choose to stop and return an error or
// response. Since the middleware decorate the handler like a call stack, each
// middleware will receive the result of the next middleware in the chain.
// Middleware that does not need to react to an input, or result must forward
// along the input down the chain, or return the result back up the chain.
//
//   Initialize <- Serialize -> Build -> Finalize <- Deserialize <- Handler
type Stack struct {
	// Initialize prepares the input, and sets any default parameters as
	// needed, (e.g. idempotency token, and presigned URLs).
	//
	// Takes Input Parameters, and returns result or error.
	//
	// Receives result or error from Serialize step.
	Initialize *InitializeStep

	// Serialize serializes the prepared input into a data structure that can be consumed
	// by the target transport's message, (e.g. REST-JSON serialization)
	//
	// Converts Input Parameters into a Request, and returns the result or error.
	//
	// Receives result or error from Build step.
	Serialize *SerializeStep

	// Build adds additional metadata to the serialized transport message
	// (e.g. HTTP's Content-Length header, or body checksum). Decorations and
	// modifications to the message should be copied to all message attempts.
	//
	// Takes Request, and returns result or error.
	//
	// Receives result or error from Finalize step.
	Build *BuildStep

	// Finalize performs final preparations needed before sending the message. The
	// message should already be complete by this stage, and is only alternated
	// to meet the expectations of the recipient (e.g. Retry and AWS SigV4
	// request signing)
	//
	// Takes Request, and returns result or error.
	//
	// Receives result or error from Deserialize step.
	Finalize *FinalizeStep

	// Deserialize reacts to the handler's response returned by the recipient of the request
	// message. Deserializes the response into a structured type or error above
	// stacks can react to.
	//
	// Should only forward Request to underlying handler.
	//
	// Takes Request, and returns result or error.
	//
	// Receives raw response, or error from underlying handler.
	Deserialize *DeserializeStep

	id string
}

// NewStack returns an initialize empty stack.
func ( string,  func() interface{}) *Stack {
	return &Stack{
		id:          ,
		Initialize:  NewInitializeStep(),
		Serialize:   NewSerializeStep(),
		Build:       NewBuildStep(),
		Finalize:    NewFinalizeStep(),
		Deserialize: NewDeserializeStep(),
	}
}

// ID returns the unique ID for the stack as a middleware.
func ( *Stack) () string { return .id }

// HandleMiddleware invokes the middleware stack decorating the next handler.
// Each step of stack will be invoked in order before calling the next step.
// With the next handler call last.
//
// The input value must be the input parameters of the operation being
// performed.
//
// Will return the result of the operation, or error.
func ( *Stack) ( context.Context,  interface{},  Handler) (
	 interface{},  Metadata,  error,
) {
	 := DecorateHandler(,
		.Initialize,
		.Serialize,
		.Build,
		.Finalize,
		.Deserialize,
	)

	return .Handle(, )
}

// List returns a list of all middleware in the stack by step.
func ( *Stack) () []string {
	var  []string
	 = append(, .id)

	 = append(, .Initialize.ID())
	 = append(, .Initialize.List()...)

	 = append(, .Serialize.ID())
	 = append(, .Serialize.List()...)

	 = append(, .Build.ID())
	 = append(, .Build.List()...)

	 = append(, .Finalize.ID())
	 = append(, .Finalize.List()...)

	 = append(, .Deserialize.ID())
	 = append(, .Deserialize.List()...)

	return 
}

func ( *Stack) () string {
	var  strings.Builder

	 := &indentWriter{w: &}

	.WriteLine(.id)
	.Push()

	writeStepItems(, .Initialize)
	writeStepItems(, .Serialize)
	writeStepItems(, .Build)
	writeStepItems(, .Finalize)
	writeStepItems(, .Deserialize)

	return .String()
}

type stackStepper interface {
	ID() string
	List() []string
}

func ( *indentWriter,  stackStepper) {
	type  interface {
		() []string
	}

	.WriteLine(.ID())
	.Push()

	defer .Pop()

	// ignore stack to prevent circular iterations
	if ,  := .(*Stack);  {
		return
	}

	for ,  := range .List() {
		.WriteLine()
	}
}

type stringWriter interface {
	io.Writer
	WriteString(string) (int, error)
	WriteRune(rune) (int, error)
}

type indentWriter struct {
	w     stringWriter
	depth int
}

const indentDepth = "\t\t\t\t\t\t\t\t\t\t"

func ( *indentWriter) () {
	.depth++
}

func ( *indentWriter) () {
	.depth--
	if .depth < 0 {
		.depth = 0
	}
}

func ( *indentWriter) ( string) {
	.w.WriteString(indentDepth[:.depth])

	 = strings.ReplaceAll(, "\n", "\\n")
	 = strings.ReplaceAll(, "\r", "\\r")

	.w.WriteString()
	.w.WriteRune('\n')
}