// Package api provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/discord-gophers/goapi-gen version v0.3.0 DO NOT EDIT.
package api

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"net/http"
	"net/url"
	"path"
	"strings"

	"github.com/discord-gophers/goapi-gen/runtime"
	"github.com/getkin/kin-openapi/openapi3"
	"github.com/go-chi/chi/v5"
	"github.com/go-chi/render"
)

// Error defines model for Error.
type Error struct {
	Code    int32  `json:"code"`
	Message string `json:"message"`
}

// ConnectParams defines parameters for Connect.
type ConnectParams struct {
	// The temporary oauth grant code
	Code *string `json:"code,omitempty"`

	// The error message
	Error *string `json:"error,omitempty"`
}

// Response is a common response struct for all the API calls.
// A Response object may be instantiated via functions for specific operation responses.
// It may also be instantiated directly, for the purpose of responding with a single status code.
type Response struct {
	body        interface{}
	Code        int
	contentType string
}

// Render implements the render.Renderer interface. It sets the Content-Type header
// and status code based on the response definition.
func (resp *Response) Render(w http.ResponseWriter, r *http.Request) error {
	w.Header().Set("Content-Type", resp.contentType)
	render.Status(r, resp.Code)
	return nil
}

// Status is a builder method to override the default status code for a response.
func (resp *Response) Status(code int) *Response {
	resp.Code = code
	return resp
}

// ContentType is a builder method to override the default content type for a response.
func (resp *Response) ContentType(contentType string) *Response {
	resp.contentType = contentType
	return resp
}

// MarshalJSON implements the json.Marshaler interface.
// This is used to only marshal the body of the response.
func (resp *Response) MarshalJSON() ([]byte, error) {
	return json.Marshal(resp.body)
}

// MarshalXML implements the xml.Marshaler interface.
// This is used to only marshal the body of the response.
func (resp *Response) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	return e.Encode(resp.body)
}

// ImportSTIXJSON500Response is a constructor method for a ImportSTIX response.
// A *Response is returned with the configured status code and content type from the spec.
func ImportSTIXJSON500Response(body Error) *Response {
	return &Response{
		body:        body,
		Code:        500,
		contentType: "application/json",
	}
}

// ConnectJSON500Response is a constructor method for a Connect response.
// A *Response is returned with the configured status code and content type from the spec.
func ConnectJSON500Response(body Error) *Response {
	return &Response{
		body:        body,
		Code:        500,
		contentType: "application/json",
	}
}

// ServerInterface represents all server handlers.
type ServerInterface interface {
	// Get homepage
	// (GET /)
	GetHomePage(w http.ResponseWriter, r *http.Request) *Response

	// (GET /api/events)
	GetEvents(w http.ResponseWriter, r *http.Request) *Response
	// Import STIX data into Notion
	// (GET /api/import)
	ImportSTIX(w http.ResponseWriter, r *http.Request) *Response
	// Connect the Notion STIX Integration
	// (GET /auth/notion/callback)
	Connect(w http.ResponseWriter, r *http.Request, params ConnectParams) *Response
}

// ServerInterfaceWrapper converts contexts to parameters.
type ServerInterfaceWrapper struct {
	Handler          ServerInterface
	ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
}

// GetHomePage operation middleware
func (siw *ServerInterfaceWrapper) GetHomePage(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	var handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		resp := siw.Handler.GetHomePage(w, r)
		if resp != nil {
			if resp.body != nil {
				render.Render(w, r, resp)
			} else {
				w.WriteHeader(resp.Code)
			}
		}
	})

	handler(w, r.WithContext(ctx))
}

// GetEvents operation middleware
func (siw *ServerInterfaceWrapper) GetEvents(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	var handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		resp := siw.Handler.GetEvents(w, r)
		if resp != nil {
			if resp.body != nil {
				render.Render(w, r, resp)
			} else {
				w.WriteHeader(resp.Code)
			}
		}
	})

	handler(w, r.WithContext(ctx))
}

// ImportSTIX operation middleware
func (siw *ServerInterfaceWrapper) ImportSTIX(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	var handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		resp := siw.Handler.ImportSTIX(w, r)
		if resp != nil {
			if resp.body != nil {
				render.Render(w, r, resp)
			} else {
				w.WriteHeader(resp.Code)
			}
		}
	})

	handler(w, r.WithContext(ctx))
}

// Connect operation middleware
func (siw *ServerInterfaceWrapper) Connect(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	// Parameter object where we will unmarshal all parameters from the context
	var params ConnectParams

	// ------------- Optional query parameter "code" -------------

	if err := runtime.BindQueryParameter("form", true, false, "code", r.URL.Query(), &params.Code); err != nil {
		err = fmt.Errorf("invalid format for parameter code: %w", err)
		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{err, "code"})
		return
	}

	// ------------- Optional query parameter "error" -------------

	if err := runtime.BindQueryParameter("form", true, false, "error", r.URL.Query(), &params.Error); err != nil {
		err = fmt.Errorf("invalid format for parameter error: %w", err)
		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{err, "error"})
		return
	}

	var handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		resp := siw.Handler.Connect(w, r, params)
		if resp != nil {
			if resp.body != nil {
				render.Render(w, r, resp)
			} else {
				w.WriteHeader(resp.Code)
			}
		}
	})

	handler(w, r.WithContext(ctx))
}

type UnescapedCookieParamError struct {
	err       error
	paramName string
}

// Error implements error.
func (err UnescapedCookieParamError) Error() string {
	return fmt.Sprintf("error unescaping cookie parameter %s: %v", err.paramName, err.err)
}

func (err UnescapedCookieParamError) Unwrap() error { return err.err }

type UnmarshalingParamError struct {
	err       error
	paramName string
}

// Error implements error.
func (err UnmarshalingParamError) Error() string {
	return fmt.Sprintf("error unmarshaling parameter %s as JSON: %v", err.paramName, err.err)
}

func (err UnmarshalingParamError) Unwrap() error { return err.err }

type RequiredParamError struct {
	err       error
	paramName string
}

// Error implements error.
func (err RequiredParamError) Error() string {
	if err.err == nil {
		return fmt.Sprintf("query parameter %s is required, but not found", err.paramName)
	} else {
		return fmt.Sprintf("query parameter %s is required, but errored: %s", err.paramName, err.err)
	}
}

func (err RequiredParamError) Unwrap() error { return err.err }

type RequiredHeaderError struct {
	paramName string
}

// Error implements error.
func (err RequiredHeaderError) Error() string {
	return fmt.Sprintf("header parameter %s is required, but not found", err.paramName)
}

type InvalidParamFormatError struct {
	err       error
	paramName string
}

// Error implements error.
func (err InvalidParamFormatError) Error() string {
	return fmt.Sprintf("invalid format for parameter %s: %v", err.paramName, err.err)
}

func (err InvalidParamFormatError) Unwrap() error { return err.err }

type TooManyValuesForParamError struct {
	NumValues int
	paramName string
}

// Error implements error.
func (err TooManyValuesForParamError) Error() string {
	return fmt.Sprintf("expected one value for %s, got %d", err.paramName, err.NumValues)
}

// ParameterName is an interface that is implemented by error types that are
// relevant to a specific parameter.
type ParameterError interface {
	error
	// ParamName is the name of the parameter that the error is referring to.
	ParamName() string
}

func (err UnescapedCookieParamError) ParamName() string  { return err.paramName }
func (err UnmarshalingParamError) ParamName() string     { return err.paramName }
func (err RequiredParamError) ParamName() string         { return err.paramName }
func (err RequiredHeaderError) ParamName() string        { return err.paramName }
func (err InvalidParamFormatError) ParamName() string    { return err.paramName }
func (err TooManyValuesForParamError) ParamName() string { return err.paramName }

type ServerOptions struct {
	BaseURL          string
	BaseRouter       chi.Router
	ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
}

type ServerOption func(*ServerOptions)

// Handler creates http.Handler with routing matching OpenAPI spec.
func Handler(si ServerInterface, opts ...ServerOption) http.Handler {
	options := &ServerOptions{
		BaseURL:    "/",
		BaseRouter: chi.NewRouter(),
		ErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
			http.Error(w, err.Error(), http.StatusBadRequest)
		},
	}

	for _, f := range opts {
		f(options)
	}

	r := options.BaseRouter
	wrapper := ServerInterfaceWrapper{
		Handler:          si,
		ErrorHandlerFunc: options.ErrorHandlerFunc,
	}

	r.Route(options.BaseURL, func(r chi.Router) {
		r.Get("/", wrapper.GetHomePage)
		r.Get("/api/events", wrapper.GetEvents)
		r.Get("/api/import", wrapper.ImportSTIX)
		r.Get("/auth/notion/callback", wrapper.Connect)
	})
	return r
}

func WithRouter(r chi.Router) ServerOption {
	return func(s *ServerOptions) {
		s.BaseRouter = r
	}
}

func WithServerBaseURL(url string) ServerOption {
	return func(s *ServerOptions) {
		s.BaseURL = url
	}
}

func WithErrorHandler(handler func(w http.ResponseWriter, r *http.Request, err error)) ServerOption {
	return func(s *ServerOptions) {
		s.ErrorHandlerFunc = handler
	}
}

// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{

	"H4sIAAAAAAAC/9RUTa/bNhD8K8S2R0VyPnrRrSjS1ijaBsgDWqAwCpraJzGRSGa5erZh6L8XS0m2Etnp",
	"u+ZiyORwdrgz3DMY3wXv0HGE8gzRNNjp9PmWyJN84FF3oUX5NL5CKN9s3mTQYYy6RijhD8/qZ9+7CoYM",
	"AvmAxBbjFX+GR0+dZijBOn79CjLgU8DxL9ZIcvDCd543I5N1NQxDBoSfektYQfnPyHnF7y5kfv8BDcMg",
	"B6x79KMAx9qwfDrdTWqtd+r9w/ZvtZXypGUBMuiphRIa5hDLojgcDrlL2Dx6EVhhNGRDApfw0NiobFTc",
	"oLpDqX58t83VlpVuW3+I6uR7xV7ZCYFKcUOoOa20ra3RGVSVZi0rfqLNIYPWGnQRF7f4ffuwUuwDuuh7",
	"Mph7qovpUCwEO2TAlttlA9gev2jAE1Icb/cy3+QbOSScOlgo4XX+Mt9ABkFzk7wt5KfG1NvPe/Me6QlT",
	"Zw64j5ZRNb7DIG4lxrHgtoISfkH+1Xf4btwjjMGLZuF8tdnMDqJLVRiPXDTctdek3orLyqs/f0shin3X",
	"aTqNVZeSWNdRkvUX7mEnyEIHW+DT/CimS66Uvx0RKfRVbzCRJJHp7IvIhLqThN662OcaA/maMEbVh0qy",
	"MVUfhose2wVPfLfp27Q9hvDLDK3aPoIFC88RJw3M4IeVITqE1ppEW3yIAl368j3hI5TwXXEdMcU0X4px",
	"uNzwShJJTrcqhYjUDFza9z9Xne0cYbOjPTfF+J4Lo9t2r83Hu738yTuHhr/6uNkrbYw4dvI9zbCDp48x",
	"aLNO+sSZXhDpDhlJVJ5XcwUVoyjXdFJeZKuatGM1zT0rqE890gmyeRpMW/efRHarDEpr1TxHs+uch/Fi",
	"/1boLFZ3aqbTXy26+waj9QznFwmbPd2lajHR3jJ1wbSa2mMmX0S2x7wPOWnbHvQp1yGAdHAqdRn8MqPE",
	"zunvrGCxNMV+2A3/BQAA///jjjtJ3QcAAA==",
}

// GetSwagger returns the content of the embedded swagger specification file
// or error if failed to decode
func decodeSpec() ([]byte, error) {
	zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
	if err != nil {
		return nil, fmt.Errorf("error base64 decoding spec: %s", err)
	}
	zr, err := gzip.NewReader(bytes.NewReader(zipped))
	if err != nil {
		return nil, fmt.Errorf("error decompressing spec: %s", err)
	}
	var buf bytes.Buffer
	_, err = buf.ReadFrom(zr)
	if err != nil {
		return nil, fmt.Errorf("error decompressing spec: %s", err)
	}

	return buf.Bytes(), nil
}

var rawSpec = decodeSpecCached()

// a naive cached of a decoded swagger spec
func decodeSpecCached() func() ([]byte, error) {
	data, err := decodeSpec()
	return func() ([]byte, error) {
		return data, err
	}
}

// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
	var res = make(map[string]func() ([]byte, error))
	if len(pathToFile) > 0 {
		res[pathToFile] = rawSpec
	}

	return res
}

// GetSwagger returns the Swagger specification corresponding to the generated code
// in this file. The external references of Swagger specification are resolved.
// The logic of resolving external references is tightly connected to "import-mapping" feature.
// Externally referenced files must be embedded in the corresponding golang packages.
// Urls can be supported but this task was out of the scope.
func GetSwagger() (swagger *openapi3.T, err error) {
	var resolvePath = PathToRawSpec("")

	loader := openapi3.NewLoader()
	loader.IsExternalRefsAllowed = true
	loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
		var pathToFile = url.String()
		pathToFile = path.Clean(pathToFile)
		getSpec, ok := resolvePath[pathToFile]
		if !ok {
			err1 := fmt.Errorf("path not found: %s", pathToFile)
			return nil, err1
		}
		return getSpec()
	}
	var specData []byte
	specData, err = rawSpec()
	if err != nil {
		return
	}
	swagger, err = loader.LoadFromData(specData)
	if err != nil {
		return
	}
	return
}
