//go:build ignore
// +build ignore

/*
Usage: execute from repository root:
$ go run middleware/generate.go && gofmt -w -s middleware/step_*.go
*/

/*
This file produces the discrete middleware steps used by generated Smithy
clients. Extreme care should be taken when editing the generation template.

We are electing to generate these steps for performance reasons. A previous
implementation of each step delegated to an underlying pair of list/mapping of
IDs to middlewares. This enabled code reuse but was expensive at runtime,
accounting for roughly 10% of the overall allocations in an operation.

The generated step here instead operates by directly decorating the phase
function in-place, which entirely removes that overhead and conveys a slight
runtime performance boost.

Hand-writing the decorator code in terms of generics is not feasible due to the
excessive amount of adapter structures that need to be used.
*/

package main

import (
	"fmt"
	"os"
	"strings"
	"text/template"
)

const stepTemplate = `// Code generated by smithy-go/middleware/generate.go DO NOT EDIT.
package middleware

import (
	"context"
	"fmt"
)

{{ .InputDoc }}
type {{.Phase}}Input struct {
{{- range .Inputs }}
	{{ .Name }} {{ .Type }}
{{- end }}
}

// {{.Phase}}Output provides the result returned by the next {{.Phase}}Handler.
type {{.Phase}}Output struct {
{{- range .Outputs }}
	{{ .Name }} {{ .Type }}
{{- end }}
}

// {{.Phase}}Handler provides the interface for the next handler the
// {{.Phase}}Middleware will call in the middleware chain.
type {{.Phase}}Handler interface {
	Handle{{.Phase}}(ctx context.Context, in {{.Phase}}Input) (
		out {{.Phase}}Output, metadata Metadata, err error,
	)
}

// {{.Phase}}Middleware provides the interface for middleware specific to the
// {{.PhaseLowercase}} step. Delegates to the next {{.Phase}}Handler for further
// processing.
type {{.Phase}}Middleware interface {
	// ID returns a unique ID for the middleware in the {{.Phase}}Step. The step does not
	// allow duplicate IDs.
	ID() string

	// Handle{{.Phase}} invokes the middleware behavior which must delegate to the next handler
	// for the middleware chain to continue. The method must return a result or
	// error to its caller.
	Handle{{.Phase}}(ctx context.Context, in {{.Phase}}Input, next {{.Phase}}Handler) (
		out {{.Phase}}Output, metadata Metadata, err error,
	)
}

// {{.Phase}}MiddlewareFunc returns a {{.Phase}}Middleware with the unique ID provided,
// and the func to be invoked.
func {{.Phase}}MiddlewareFunc(id string, fn func(context.Context, {{.Phase}}Input, {{.Phase}}Handler) ({{.Phase}}Output, Metadata, error)) {{.Phase}}Middleware {
	return {{.PhaseLowercase}}MiddlewareFunc{
		id: id,
		fn: fn,
	}
}

type {{.PhaseLowercase}}MiddlewareFunc struct {
	// Unique ID for the middleware.
	id string

	// Middleware function to be called.
	fn func(context.Context, {{.Phase}}Input, {{.Phase}}Handler) (
		{{.Phase}}Output, Metadata, error,
	)
}

// ID returns the unique ID for the middleware.
func (s {{.PhaseLowercase}}MiddlewareFunc) ID() string { return s.id }

// Handle{{.Phase}} invokes the middleware Fn.
func (s {{.PhaseLowercase}}MiddlewareFunc) Handle{{.Phase}}(ctx context.Context, in {{.Phase}}Input, next {{.Phase}}Handler) (
	out {{.Phase}}Output, metadata Metadata, err error,
) {
	return s.fn(ctx, in, next)
}

var _ {{.Phase}}Middleware = ({{.PhaseLowercase}}MiddlewareFunc{})

// {{.Phase}}Step provides the ordered grouping of {{.Phase}}Middleware to be
// invoked on a handler.
type {{.Phase}}Step struct {
	head *decorated{{.Phase}}Handler
	tail *decorated{{.Phase}}Handler
{{- range .StepFields }}
	{{ .Name }} {{ .Type }}
{{- end }}
}

// New{{.Phase}}Step returns an {{.Phase}}Step ready to have middleware for
// {{.PhaseLowercase}} added to it.
func New{{.Phase}}Step({{ range .StepFields }} {{ .Name }} {{ .Type }}, {{ end }}) *{{.Phase}}Step {
	return &{{.Phase}}Step{
{{- range .StepFields }}
	{{ .Name }}: {{ .Name }},
{{- end }}
	}
}

var _ Middleware = (*{{.Phase}}Step)(nil)

// ID returns the unique ID of the step as a middleware.
func (s *{{.Phase}}Step) ID() string {
	return "{{.Phase}} stack step"
}

// HandleMiddleware invokes the middleware by decorating the next handler
// provided. Returns the result of the middleware and handler being invoked.
//
// Implements Middleware interface.
func (s *{{.Phase}}Step) HandleMiddleware(ctx context.Context, in interface{}, next Handler) (
	out interface{}, metadata Metadata, err error,
) {
	sIn := {{.Phase}}Input{
{{- range .Inputs }}
	{{ .Name }}: {{ .Binding }},
{{- end }}
	}

	wh := &{{.PhaseLowercase}}WrapHandler{next}
	if s.head == nil {
		res, metadata, err := wh.Handle{{.Phase}}(ctx, sIn)
		return res.Result, metadata, err
	}

	s.tail.Next = wh
	res, metadata, err := s.head.Handle{{.Phase}}(ctx, sIn)
	return res.Result, metadata, err
}

// Get retrieves the middleware identified by id. If the middleware is not present, returns false.
func (s *{{.Phase}}Step) Get(id string) ({{.Phase}}Middleware, bool) {
	found, _ := s.get(id)
	if found == nil {
		return nil,false
	}

	return found.With, true
}

// Add injects the middleware to the relative position of the middleware group.
//
// Add never returns an error. It used to for duplicate phases but this
// behavior has since been removed as part of a performance optimization. The
// return value from Add can be ignored.
func (s *{{.Phase}}Step) Add(m {{.Phase}}Middleware, pos RelativePosition) error {
	if s.head == nil {
		s.head = &decorated{{.Phase}}Handler{nil, m}
		s.tail = s.head
		return nil
	}

	if pos == Before {
		s.head = &decorated{{.Phase}}Handler{s.head, m}
	} else {
		tail := &decorated{{.Phase}}Handler{nil, m}
		s.tail.Next = tail
		s.tail = tail
	}

	return nil
}

// Insert injects the middleware relative to an existing middleware ID.
// Returns error if the original middleware does not exist, or the middleware
// being added already exists.
func (s *{{.Phase}}Step) Insert(m {{.Phase}}Middleware, relativeTo string, pos RelativePosition) error {
	found, prev := s.get(relativeTo)
	if found == nil {
		return fmt.Errorf("not found: %s", m.ID())
	}

	if pos == Before {
		if prev == nil { // at the front
			s.head = &decorated{{.Phase}}Handler{s.head, m}
		} else { // somewhere in the middle
			prev.Next = &decorated{{.Phase}}Handler{found, m}
		}
	} else {
		if found.Next == nil { // at the end
			tail := &decorated{{.Phase}}Handler{nil, m}
			s.tail.Next = tail
			s.tail = tail
		} else { // somewhere in the middle
			found.Next = &decorated{{.Phase}}Handler{found.Next, m}
		}
	}

	return nil
}

// Swap removes the middleware by id, replacing it with the new middleware.
// Returns the middleware removed, or error if the middleware to be removed
// doesn't exist.
func (s *{{.Phase}}Step) Swap(id string, m {{.Phase}}Middleware) ({{.Phase}}Middleware, error) {
	found, _ := s.get(id)
	if found == nil {
		return nil, fmt.Errorf("not found: %s", m.ID())
	}

	swapped := found.With
	found.With = m
	return swapped, nil
}

// Remove removes the middleware by id. Returns error if the middleware
// doesn't exist.
func (s *{{.Phase}}Step) Remove(id string) ({{.Phase}}Middleware, error) {
	found, prev := s.get(id)
	if found == nil {
		return nil, fmt.Errorf("not found: %s", id)
	}

	if s.head == s.tail { // it's the only one
		s.head = nil
		s.tail = nil
	} else if found == s.head { // at the front
		s.head = s.head.Next.(*decorated{{.Phase}}Handler)
	} else if found == s.tail { // at the end
		prev.Next = nil
		s.tail = prev
	} else {
		prev.Next = found.Next // somewhere in the middle
	}

	return found.With, nil
}

// List returns a list of the middleware in the step.
func (s *{{.Phase}}Step) List() []string {
	var ids []string
	for h := s.head; h != nil; {
		ids = append(ids, h.With.ID())
		if h.Next == nil {
			break
		}

		// once executed, tail.Next of the list will be set to an
		// *{{.PhaseLowercase}}WrapHandler, make sure to check for that
		if hnext, ok := h.Next.(*decorated{{.Phase}}Handler); ok {
			h = hnext
		} else {
			break
		}
	}
	return ids
}

// Clear removes all middleware in the step.
func (s *{{.Phase}}Step) Clear() {
	s.head = nil
	s.tail = nil
}

func (s *{{.Phase}}Step) get(id string) (found, prev *decorated{{.Phase}}Handler) {
	for h := s.head; h != nil; {
		if h.With.ID() == id {
			found = h
			return
		}
		prev = h
		if h.Next == nil {
			return
		}

		// once executed, tail.Next of the list will be set to an
		// *{{.PhaseLowercase}}WrapHandler
		h, _ = h.Next.(*decorated{{.Phase}}Handler)
	}
	return
}

type {{.PhaseLowercase}}WrapHandler struct {
	Next Handler
}

var _ {{.Phase}}Handler = (*{{.PhaseLowercase}}WrapHandler)(nil)

// Handle{{.Phase}} implements {{.Phase}}Handler, converts types and delegates to underlying
// generic handler.
func (w {{.PhaseLowercase}}WrapHandler) Handle{{.Phase}}(ctx context.Context, in {{.Phase}}Input) (
	out {{.Phase}}Output, metadata Metadata, err error,
) {
	res, metadata, err := w.Next.Handle(ctx, {{ .NextInput }})
	return {{.Phase}}Output{
{{- range .Outputs }}
	{{ .Name }}: {{ .Binding }},
{{- end }}
	}, metadata, err
}

type decorated{{.Phase}}Handler struct {
	Next {{.Phase}}Handler
	With {{.Phase}}Middleware
}

var _ {{.Phase}}Handler = (*decorated{{.Phase}}Handler)(nil)

func (h decorated{{.Phase}}Handler) Handle{{.Phase}}(ctx context.Context, in {{.Phase}}Input) (
	out {{.Phase}}Output, metadata Metadata, err error,
) {
	return h.With.Handle{{.Phase}}(ctx, in, h.Next)
}

// {{.Phase}}HandlerFunc provides a wrapper around a function to be used as {{.PhaseLowercase}}Middleware.
type {{.Phase}}HandlerFunc func(context.Context, {{.Phase}}Input) ({{.Phase}}Output, Metadata, error)

// Handle{{.Phase}} calls the wrapped function with the provided arguments.
func (f {{.Phase}}HandlerFunc) Handle{{.Phase}}(ctx context.Context, in {{.Phase}}Input) ({{.Phase}}Output, Metadata, error) {
	return f(ctx, in)
}

var _ {{.Phase}}Handler = {{.Phase}}HandlerFunc(nil)
`

// Step describes how to generate a middleware.
type Step struct {
	Phase          string
	PhaseLowercase string // set automatically by generator

	// the input literal passed along to the phase that follows this one
	//
	// for the final phase that would just be the transport client call
	// (so for HTTP, the request)
	NextInput string

	// struct fields on the step itself
	StepFields []FieldDecl

	InputDoc string
	Inputs   []InputFieldDecl

	Outputs []OutputFieldDecl
}

type FieldDecl struct {
	Name, Type string
}

type InputFieldDecl struct {
	FieldDecl
	Binding string
}

type OutputFieldDecl struct {
	FieldDecl
	Binding string
}

func (s *Step) Filepath() string {
	return fmt.Sprintf("middleware/step_%s.go", s.PhaseLowercase)
}

func main() {
	tmpl, err := template.New("Step").Parse(stepTemplate)
	if err != nil {
		panic(err)
	}

	for _, step := range []Step{
		{
			Phase:     "Initialize",
			NextInput: "in.Parameters",

			InputDoc: `// InitializeInput wraps the input parameters for the InitializeMiddlewares to
// consume. InitializeMiddleware may modify the parameter value before
// forwarding it along to the next InitializeHandler.
`,
			Inputs: []InputFieldDecl{
				{
					FieldDecl: FieldDecl{"Parameters", "interface{}"},
					Binding:   "in",
				},
			},
			Outputs: []OutputFieldDecl{
				{
					FieldDecl: FieldDecl{"Result", "interface{}"},
					Binding:   "res",
				},
			},
		},
		{
			Phase:     "Serialize",
			NextInput: "in.Request",
			StepFields: []FieldDecl{
				{"newRequest", "func() interface{}"},
			},

			InputDoc: `// SerializeInput provides the input parameters for the SerializeMiddleware to
// consume. SerializeMiddleware may modify the Request value before forwarding
// SerializeInput along to the next SerializeHandler. The Parameters member
// should not be modified by SerializeMiddleware, InitializeMiddleware should
// be responsible for modifying the provided Parameter value.`,
			Inputs: []InputFieldDecl{
				{
					FieldDecl: FieldDecl{"Parameters", "interface{}"},
					Binding:   "in",
				},
				{
					FieldDecl: FieldDecl{"Request", "interface{}"},
					Binding:   "s.newRequest()",
				},
			},
			Outputs: []OutputFieldDecl{
				{
					FieldDecl: FieldDecl{"Result", "interface{}"},
					Binding:   "res",
				},
			},
		},
		{
			Phase:     "Build",
			NextInput: "in.Request",

			InputDoc: `// BuildInput provides the input parameters for the BuildMiddleware to consume.
// BuildMiddleware may modify the Request value before forwarding the input
// along to the next BuildHandler.`,
			Inputs: []InputFieldDecl{
				{
					FieldDecl: FieldDecl{"Request", "interface{}"},
					Binding:   "in",
				},
			},
			Outputs: []OutputFieldDecl{
				{
					FieldDecl: FieldDecl{"Result", "interface{}"},
					Binding:   "res",
				},
			},
		},
		{
			Phase:     "Finalize",
			NextInput: "in.Request",

			InputDoc: `// FinalizeInput provides the input parameters for the FinalizeMiddleware to
// consume. FinalizeMiddleware may modify the Request value before forwarding
// the FinalizeInput along to the next next FinalizeHandler.`,
			Inputs: []InputFieldDecl{
				{
					FieldDecl: FieldDecl{"Request", "interface{}"},
					Binding:   "in",
				},
			},
			Outputs: []OutputFieldDecl{
				{
					FieldDecl: FieldDecl{"Result", "interface{}"},
					Binding:   "res",
				},
			},
		},
		{
			Phase:     "Deserialize",
			NextInput: "in.Request",

			InputDoc: `// DeserializeInput provides the input parameters for the DeserializeInput to
// consume. DeserializeMiddleware should not modify the Request, and instead
// forward it along to the next DeserializeHandler.`,
			Inputs: []InputFieldDecl{
				{
					FieldDecl: FieldDecl{"Request", "interface{}"},
					Binding:   "in",
				},
			},
			Outputs: []OutputFieldDecl{
				{
					FieldDecl: FieldDecl{"RawResponse", "interface{}"},
					Binding:   "res",
				},
				{
					FieldDecl: FieldDecl{"Result", "interface{}"},
					Binding:   "nil",
				},
			},
		},
	} {
		step.PhaseLowercase = strings.ToLower(step.Phase)

		f, err := os.Create(step.Filepath())
		if err != nil {
			panic(err)
		}
		defer f.Close()

		if err := tmpl.Execute(f, step); err != nil {
			panic(err)
		}
	}
}
