package api

import (
	"context"
	"fmt"
	"net/http"

	"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
	"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
	s3middleware "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
	"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
	"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/metrics"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"go.uber.org/zap"
)

type (
	// Handler is an S3 API handler interface.
	Handler interface {
		HeadObjectHandler(http.ResponseWriter, *http.Request)
		GetObjectACLHandler(http.ResponseWriter, *http.Request)
		PutObjectACLHandler(http.ResponseWriter, *http.Request)
		GetObjectTaggingHandler(http.ResponseWriter, *http.Request)
		PutObjectTaggingHandler(http.ResponseWriter, *http.Request)
		DeleteObjectTaggingHandler(http.ResponseWriter, *http.Request)
		SelectObjectContentHandler(http.ResponseWriter, *http.Request)
		GetObjectRetentionHandler(http.ResponseWriter, *http.Request)
		GetObjectLegalHoldHandler(http.ResponseWriter, *http.Request)
		GetObjectHandler(http.ResponseWriter, *http.Request)
		GetObjectAttributesHandler(http.ResponseWriter, *http.Request)
		CopyObjectHandler(http.ResponseWriter, *http.Request)
		PutObjectRetentionHandler(http.ResponseWriter, *http.Request)
		PutObjectLegalHoldHandler(http.ResponseWriter, *http.Request)
		PutObjectHandler(http.ResponseWriter, *http.Request)
		DeleteObjectHandler(http.ResponseWriter, *http.Request)
		GetBucketLocationHandler(http.ResponseWriter, *http.Request)
		GetBucketPolicyStatusHandler(http.ResponseWriter, *http.Request)
		GetBucketPolicyHandler(http.ResponseWriter, *http.Request)
		GetBucketLifecycleHandler(http.ResponseWriter, *http.Request)
		GetBucketEncryptionHandler(http.ResponseWriter, *http.Request)
		GetBucketACLHandler(http.ResponseWriter, *http.Request)
		PutBucketACLHandler(http.ResponseWriter, *http.Request)
		GetBucketCorsHandler(http.ResponseWriter, *http.Request)
		PutBucketCorsHandler(http.ResponseWriter, *http.Request)
		DeleteBucketCorsHandler(http.ResponseWriter, *http.Request)
		GetBucketWebsiteHandler(http.ResponseWriter, *http.Request)
		GetBucketAccelerateHandler(http.ResponseWriter, *http.Request)
		GetBucketRequestPaymentHandler(http.ResponseWriter, *http.Request)
		GetBucketLoggingHandler(http.ResponseWriter, *http.Request)
		GetBucketReplicationHandler(http.ResponseWriter, *http.Request)
		GetBucketTaggingHandler(http.ResponseWriter, *http.Request)
		DeleteBucketWebsiteHandler(http.ResponseWriter, *http.Request)
		DeleteBucketTaggingHandler(http.ResponseWriter, *http.Request)
		GetBucketObjectLockConfigHandler(http.ResponseWriter, *http.Request)
		GetBucketVersioningHandler(http.ResponseWriter, *http.Request)
		GetBucketNotificationHandler(http.ResponseWriter, *http.Request)
		ListenBucketNotificationHandler(http.ResponseWriter, *http.Request)
		ListObjectsV2MHandler(http.ResponseWriter, *http.Request)
		ListObjectsV2Handler(http.ResponseWriter, *http.Request)
		ListBucketObjectVersionsHandler(http.ResponseWriter, *http.Request)
		ListObjectsV1Handler(http.ResponseWriter, *http.Request)
		PutBucketLifecycleHandler(http.ResponseWriter, *http.Request)
		PutBucketEncryptionHandler(http.ResponseWriter, *http.Request)
		PutBucketPolicyHandler(http.ResponseWriter, *http.Request)
		PutBucketObjectLockConfigHandler(http.ResponseWriter, *http.Request)
		PutBucketTaggingHandler(http.ResponseWriter, *http.Request)
		PutBucketVersioningHandler(http.ResponseWriter, *http.Request)
		PutBucketNotificationHandler(http.ResponseWriter, *http.Request)
		CreateBucketHandler(http.ResponseWriter, *http.Request)
		HeadBucketHandler(http.ResponseWriter, *http.Request)
		PostObject(http.ResponseWriter, *http.Request)
		DeleteMultipleObjectsHandler(http.ResponseWriter, *http.Request)
		DeleteBucketPolicyHandler(http.ResponseWriter, *http.Request)
		DeleteBucketLifecycleHandler(http.ResponseWriter, *http.Request)
		DeleteBucketEncryptionHandler(http.ResponseWriter, *http.Request)
		DeleteBucketHandler(http.ResponseWriter, *http.Request)
		ListBucketsHandler(http.ResponseWriter, *http.Request)
		Preflight(w http.ResponseWriter, r *http.Request)
		AppendCORSHeaders(w http.ResponseWriter, r *http.Request)
		CreateMultipartUploadHandler(http.ResponseWriter, *http.Request)
		UploadPartHandler(http.ResponseWriter, *http.Request)
		UploadPartCopy(w http.ResponseWriter, r *http.Request)
		CompleteMultipartUploadHandler(http.ResponseWriter, *http.Request)
		AbortMultipartUploadHandler(http.ResponseWriter, *http.Request)
		ListPartsHandler(w http.ResponseWriter, r *http.Request)
		ListMultipartUploadsHandler(http.ResponseWriter, *http.Request)
		PatchObjectHandler(http.ResponseWriter, *http.Request)

		ResolveBucket(ctx context.Context, bucket string) (*data.BucketInfo, error)
		ResolveCID(ctx context.Context, bucket string) (cid.ID, error)
	}
)

type Settings interface {
	s3middleware.RequestSettings
	s3middleware.PolicySettings
	s3middleware.MetricsSettings
	s3middleware.VHSSettings
	s3middleware.LogHTTPSettings
}

type FrostFSID interface {
	s3middleware.FrostFSIDValidator
	s3middleware.FrostFSIDInformer
}

type Config struct {
	Throttle middleware.ThrottleOpts
	Handler  Handler
	Center   s3middleware.Center
	Log      *zap.Logger
	Metrics  *metrics.AppMetrics

	MiddlewareSettings Settings

	FrostfsID FrostFSID

	FrostFSIDValidation bool

	PolicyChecker engine.ChainRouter

	XMLDecoder s3middleware.XMLDecoder
	Tagging    s3middleware.ResourceTagging
}

func NewRouter(cfg Config) *chi.Mux {
	api := chi.NewRouter()

	api.Use(
		s3middleware.LogHTTP(cfg.Log, cfg.MiddlewareSettings),
		s3middleware.Request(cfg.Log, cfg.MiddlewareSettings),
		middleware.ThrottleWithOpts(cfg.Throttle),
		middleware.Recoverer,
		s3middleware.Metrics(cfg.Log, cfg.Handler.ResolveCID, cfg.Metrics, cfg.MiddlewareSettings),
		s3middleware.LogSuccessResponse(cfg.Log),
		s3middleware.Auth(cfg.Center, cfg.Log),
	)

	if cfg.FrostFSIDValidation {
		api.Use(s3middleware.FrostfsIDValidation(cfg.FrostfsID, cfg.Log))
	}

	api.Use(s3middleware.PrepareAddressStyle(cfg.MiddlewareSettings, cfg.Log))
	api.Use(s3middleware.PolicyCheck(s3middleware.PolicyConfig{
		Storage:        cfg.PolicyChecker,
		FrostfsID:      cfg.FrostfsID,
		Settings:       cfg.MiddlewareSettings,
		Log:            cfg.Log,
		BucketResolver: cfg.Handler.ResolveBucket,
		Decoder:        cfg.XMLDecoder,
		Tagging:        cfg.Tagging,
	}))

	defaultRouter := chi.NewRouter()
	defaultRouter.Mount("/{bucket}", bucketRouter(cfg.Handler))
	defaultRouter.Get("/", named(s3middleware.ListBucketsOperation, cfg.Handler.ListBucketsHandler))
	attachErrorHandler(defaultRouter)

	vhsRouter := bucketRouter(cfg.Handler)
	router := newGlobalRouter(defaultRouter, vhsRouter)

	api.Mount("/", router)

	attachErrorHandler(api)

	return api
}

type globalRouter struct {
	pathStyleRouter chi.Router
	vhsRouter       chi.Router
}

func newGlobalRouter(pathStyleRouter, vhsRouter chi.Router) *globalRouter {
	return &globalRouter{
		pathStyleRouter: pathStyleRouter,
		vhsRouter:       vhsRouter,
	}
}

func (g *globalRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	router := g.pathStyleRouter
	if reqInfo := s3middleware.GetReqInfo(r.Context()); reqInfo.RequestVHSEnabled {
		router = g.vhsRouter
	}

	router.ServeHTTP(w, r)
}

func named(name string, handlerFunc http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		reqInfo := s3middleware.GetReqInfo(r.Context())
		reqInfo.API = name
		handlerFunc.ServeHTTP(w, r)
	}
}

// If none of the http routes match respond with appropriate errors.
func errorResponseHandler(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	reqInfo := s3middleware.GetReqInfo(ctx)

	desc := fmt.Sprintf("Unknown API request at %s", r.URL.Path)
	_, wrErr := s3middleware.WriteErrorResponse(w, reqInfo, errors.Error{
		Code:           "UnknownAPIRequest",
		Description:    desc,
		HTTPStatusCode: http.StatusBadRequest,
	})

	if log := s3middleware.GetReqLog(ctx); log != nil {
		fields := []zap.Field{
			zap.String("method", reqInfo.API),
			zap.String("http method", r.Method),
			zap.String("url", r.RequestURI),
		}

		if wrErr != nil {
			fields = append(fields, zap.NamedError("write_response_error", wrErr))
		}

		log.Error(logs.RequestUnmatched, fields...)
	}
}

func notSupportedHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		reqInfo := s3middleware.GetReqInfo(ctx)

		_, wrErr := s3middleware.WriteErrorResponse(w, reqInfo, errors.GetAPIError(errors.ErrNotSupported))

		if log := s3middleware.GetReqLog(ctx); log != nil {
			fields := []zap.Field{
				zap.String("http method", r.Method),
				zap.String("url", r.RequestURI),
			}

			if wrErr != nil {
				fields = append(fields, zap.NamedError("write_response_error", wrErr))
			}

			log.Error(logs.NotSupported, fields...)
		}
	}
}

// attachErrorHandler set NotFoundHandler and MethodNotAllowedHandler for chi.Router.
func attachErrorHandler(api *chi.Mux) {
	errorHandler := http.HandlerFunc(errorResponseHandler)

	// If none of the routes match, add default error handler routes
	api.NotFound(named("NotFound", errorHandler))
	api.MethodNotAllowed(named("MethodNotAllowed", errorHandler))
}

func bucketRouter(h Handler) chi.Router {
	bktRouter := chi.NewRouter()
	bktRouter.Use(
		s3middleware.WrapHandler(h.AppendCORSHeaders),
	)

	bktRouter.Mount("/", objectRouter(h))

	bktRouter.Options("/", named(s3middleware.OptionsBucketOperation, h.Preflight))

	bktRouter.Head("/", named(s3middleware.HeadBucketOperation, h.HeadBucketHandler))

	// GET method handlers
	bktRouter.Group(func(r chi.Router) {
		r.Method(http.MethodGet, "/", NewHandlerFilter().
			Add(NewFilter().
				Queries(s3middleware.UploadsQuery).
				Handler(named(s3middleware.ListMultipartUploadsOperation, h.ListMultipartUploadsHandler))).
			Add(NewFilter().
				Queries(s3middleware.LocationQuery).
				Handler(named(s3middleware.GetBucketLocationOperation, h.GetBucketLocationHandler))).
			Add(NewFilter().
				Queries(s3middleware.PolicyStatusQuery).
				Handler(named(s3middleware.GetBucketPolicyStatusOperation, h.GetBucketPolicyStatusHandler))).
			Add(NewFilter().
				Queries(s3middleware.PolicyQuery).
				Handler(named(s3middleware.GetBucketPolicyOperation, h.GetBucketPolicyHandler))).
			Add(NewFilter().
				Queries(s3middleware.LifecycleQuery).
				Handler(named(s3middleware.GetBucketLifecycleOperation, h.GetBucketLifecycleHandler))).
			Add(NewFilter().
				Queries(s3middleware.EncryptionQuery).
				Handler(named(s3middleware.GetBucketEncryptionOperation, h.GetBucketEncryptionHandler))).
			Add(NewFilter().
				Queries(s3middleware.CorsQuery).
				Handler(named(s3middleware.GetBucketCorsOperation, h.GetBucketCorsHandler))).
			Add(NewFilter().
				Queries(s3middleware.ACLQuery).
				Handler(named(s3middleware.GetBucketACLOperation, h.GetBucketACLHandler))).
			Add(NewFilter().
				Queries(s3middleware.WebsiteQuery).
				Handler(named(s3middleware.GetBucketWebsiteOperation, h.GetBucketWebsiteHandler))).
			Add(NewFilter().
				Queries(s3middleware.AccelerateQuery).
				Handler(named(s3middleware.GetBucketAccelerateOperation, h.GetBucketAccelerateHandler))).
			Add(NewFilter().
				Queries(s3middleware.RequestPaymentQuery).
				Handler(named(s3middleware.GetBucketRequestPaymentOperation, h.GetBucketRequestPaymentHandler))).
			Add(NewFilter().
				Queries(s3middleware.LoggingQuery).
				Handler(named(s3middleware.GetBucketLoggingOperation, h.GetBucketLoggingHandler))).
			Add(NewFilter().
				Queries(s3middleware.ReplicationQuery).
				Handler(named(s3middleware.GetBucketReplicationOperation, h.GetBucketReplicationHandler))).
			Add(NewFilter().
				Queries(s3middleware.TaggingQuery).
				Handler(named(s3middleware.GetBucketTaggingOperation, h.GetBucketTaggingHandler))).
			Add(NewFilter().
				Queries(s3middleware.ObjectLockQuery).
				Handler(named(s3middleware.GetBucketObjectLockConfigOperation, h.GetBucketObjectLockConfigHandler))).
			Add(NewFilter().
				Queries(s3middleware.VersioningQuery).
				Handler(named(s3middleware.GetBucketVersioningOperation, h.GetBucketVersioningHandler))).
			Add(NewFilter().
				Queries(s3middleware.NotificationQuery).
				Handler(named(s3middleware.GetBucketNotificationOperation, h.GetBucketNotificationHandler))).
			Add(NewFilter().
				Queries(s3middleware.EventsQuery).
				Handler(named(s3middleware.ListenBucketNotificationOperation, h.ListenBucketNotificationHandler))).
			Add(NewFilter().
				QueriesMatch(s3middleware.ListTypeQuery, "2", s3middleware.MetadataQuery, "true").
				Handler(named(s3middleware.ListObjectsV2MOperation, h.ListObjectsV2MHandler))).
			Add(NewFilter().
				QueriesMatch(s3middleware.ListTypeQuery, "2").
				Handler(named(s3middleware.ListObjectsV2Operation, h.ListObjectsV2Handler))).
			Add(NewFilter().
				Queries(s3middleware.VersionsQuery).
				Handler(named(s3middleware.ListBucketObjectVersionsOperation, h.ListBucketObjectVersionsHandler))).
			Add(NewFilter().
				AllowedQueries(s3middleware.QueryDelimiter, s3middleware.QueryMaxKeys, s3middleware.QueryPrefix,
					s3middleware.QueryMarker, s3middleware.QueryEncodingType).
				Handler(named(s3middleware.ListObjectsV1Operation, h.ListObjectsV1Handler))).
			Add(NewFilter().
				NoQueries().
				Handler(listWrapper(h))).
			DefaultHandler(notSupportedHandler()))
	})

	// PUT method handlers
	bktRouter.Group(func(r chi.Router) {
		r.Method(http.MethodPut, "/", NewHandlerFilter().
			Add(NewFilter().
				Queries(s3middleware.CorsQuery).
				Handler(named(s3middleware.PutBucketCorsOperation, h.PutBucketCorsHandler))).
			Add(NewFilter().
				Queries(s3middleware.ACLQuery).
				Handler(named(s3middleware.PutBucketACLOperation, h.PutBucketACLHandler))).
			Add(NewFilter().
				Queries(s3middleware.LifecycleQuery).
				Handler(named(s3middleware.PutBucketLifecycleOperation, h.PutBucketLifecycleHandler))).
			Add(NewFilter().
				Queries(s3middleware.EncryptionQuery).
				Handler(named(s3middleware.PutBucketEncryptionOperation, h.PutBucketEncryptionHandler))).
			Add(NewFilter().
				Queries(s3middleware.PolicyQuery).
				Handler(named(s3middleware.PutBucketPolicyOperation, h.PutBucketPolicyHandler))).
			Add(NewFilter().
				Queries(s3middleware.ObjectLockQuery).
				Handler(named(s3middleware.PutBucketObjectLockConfigOperation, h.PutBucketObjectLockConfigHandler))).
			Add(NewFilter().
				Queries(s3middleware.TaggingQuery).
				Handler(named(s3middleware.PutBucketTaggingOperation, h.PutBucketTaggingHandler))).
			Add(NewFilter().
				Queries(s3middleware.VersioningQuery).
				Handler(named(s3middleware.PutBucketVersioningOperation, h.PutBucketVersioningHandler))).
			Add(NewFilter().
				Queries(s3middleware.NotificationQuery).
				Handler(named(s3middleware.PutBucketNotificationOperation, h.PutBucketNotificationHandler))).
			Add(NewFilter().
				NoQueries().
				Handler(named(s3middleware.CreateBucketOperation, h.CreateBucketHandler))).
			DefaultHandler(notSupportedHandler()))
	})

	// POST method handlers
	bktRouter.Group(func(r chi.Router) {
		r.Method(http.MethodPost, "/", NewHandlerFilter().
			Add(NewFilter().
				Queries(s3middleware.DeleteQuery).
				Handler(named(s3middleware.DeleteMultipleObjectsOperation, h.DeleteMultipleObjectsHandler))).
			// todo consider add filter to match header for defaultHandler:  hdrContentType, "multipart/form-data*"
			DefaultHandler(named(s3middleware.PostObjectOperation, h.PostObject)))
	})

	// DELETE method handlers
	bktRouter.Group(func(r chi.Router) {
		r.Method(http.MethodDelete, "/", NewHandlerFilter().
			Add(NewFilter().
				Queries(s3middleware.CorsQuery).
				Handler(named(s3middleware.DeleteBucketCorsOperation, h.DeleteBucketCorsHandler))).
			Add(NewFilter().
				Queries(s3middleware.WebsiteQuery).
				Handler(named(s3middleware.DeleteBucketWebsiteOperation, h.DeleteBucketWebsiteHandler))).
			Add(NewFilter().
				Queries(s3middleware.TaggingQuery).
				Handler(named(s3middleware.DeleteBucketTaggingOperation, h.DeleteBucketTaggingHandler))).
			Add(NewFilter().
				Queries(s3middleware.PolicyQuery).
				Handler(named(s3middleware.DeleteBucketPolicyOperation, h.DeleteBucketPolicyHandler))).
			Add(NewFilter().
				Queries(s3middleware.LifecycleQuery).
				Handler(named(s3middleware.DeleteBucketLifecycleOperation, h.DeleteBucketLifecycleHandler))).
			Add(NewFilter().
				Queries(s3middleware.EncryptionQuery).
				Handler(named(s3middleware.DeleteBucketEncryptionOperation, h.DeleteBucketEncryptionHandler))).
			Add(NewFilter().
				NoQueries().
				Handler(named(s3middleware.DeleteBucketOperation, h.DeleteBucketHandler))).
			DefaultHandler(notSupportedHandler()))
	})

	attachErrorHandler(bktRouter)

	return bktRouter
}

func listWrapper(h Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if reqInfo := s3middleware.GetReqInfo(r.Context()); reqInfo.BucketName == "" {
			reqInfo.API = s3middleware.ListBucketsOperation
			h.ListBucketsHandler(w, r)
		} else {
			reqInfo.API = s3middleware.ListObjectsV1Operation
			h.ListObjectsV1Handler(w, r)
		}
	}
}

func objectRouter(h Handler) chi.Router {
	objRouter := chi.NewRouter()

	objRouter.Options("/*", named(s3middleware.OptionsObjectOperation, h.Preflight))

	objRouter.Head("/*", named(s3middleware.HeadObjectOperation, h.HeadObjectHandler))

	objRouter.Patch("/*", named(s3middleware.PatchObjectOperation, h.PatchObjectHandler))

	// GET method handlers
	objRouter.Group(func(r chi.Router) {
		r.Method(http.MethodGet, "/*", NewHandlerFilter().
			Add(NewFilter().
				Queries(s3middleware.UploadIDQuery).
				Handler(named(s3middleware.ListPartsOperation, h.ListPartsHandler))).
			Add(NewFilter().
				Queries(s3middleware.ACLQuery).
				Handler(named(s3middleware.GetObjectACLOperation, h.GetObjectACLHandler))).
			Add(NewFilter().
				Queries(s3middleware.TaggingQuery).
				Handler(named(s3middleware.GetObjectTaggingOperation, h.GetObjectTaggingHandler))).
			Add(NewFilter().
				Queries(s3middleware.RetentionQuery).
				Handler(named(s3middleware.GetObjectRetentionOperation, h.GetObjectRetentionHandler))).
			Add(NewFilter().
				Queries(s3middleware.LegalHoldQuery).
				Handler(named(s3middleware.GetObjectLegalHoldOperation, h.GetObjectLegalHoldHandler))).
			Add(NewFilter().
				Queries(s3middleware.AttributesQuery).
				Handler(named(s3middleware.GetObjectAttributesOperation, h.GetObjectAttributesHandler))).
			DefaultHandler(named(s3middleware.GetObjectOperation, h.GetObjectHandler)))
	})

	// PUT method handlers
	objRouter.Group(func(r chi.Router) {
		r.Method(http.MethodPut, "/*", NewHandlerFilter().
			Add(NewFilter().
				Headers(AmzCopySource).
				Queries(s3middleware.PartNumberQuery, s3middleware.UploadIDQuery).
				Handler(named(s3middleware.UploadPartCopyOperation, h.UploadPartCopy))).
			Add(NewFilter().
				Queries(s3middleware.PartNumberQuery, s3middleware.UploadIDQuery).
				Handler(named(s3middleware.UploadPartOperation, h.UploadPartHandler))).
			Add(NewFilter().
				Queries(s3middleware.ACLQuery).
				Handler(named(s3middleware.PutObjectACLOperation, h.PutObjectACLHandler))).
			Add(NewFilter().
				Queries(s3middleware.TaggingQuery).
				Handler(named(s3middleware.PutObjectTaggingOperation, h.PutObjectTaggingHandler))).
			Add(NewFilter().
				Headers(AmzCopySource).
				Handler(named(s3middleware.CopyObjectOperation, h.CopyObjectHandler))).
			Add(NewFilter().
				Queries(s3middleware.RetentionQuery).
				Handler(named(s3middleware.PutObjectRetentionOperation, h.PutObjectRetentionHandler))).
			Add(NewFilter().
				Queries(s3middleware.LegalHoldQuery).
				Handler(named(s3middleware.PutObjectLegalHoldOperation, h.PutObjectLegalHoldHandler))).
			DefaultHandler(named(s3middleware.PutObjectOperation, h.PutObjectHandler)))
	})

	// POST method handlers
	objRouter.Group(func(r chi.Router) {
		r.Method(http.MethodPost, "/*", NewHandlerFilter().
			Add(NewFilter().
				Queries(s3middleware.UploadIDQuery).
				Handler(named(s3middleware.CompleteMultipartUploadOperation, h.CompleteMultipartUploadHandler))).
			Add(NewFilter().
				Queries(s3middleware.UploadsQuery).
				Handler(named(s3middleware.CreateMultipartUploadOperation, h.CreateMultipartUploadHandler))).
			DefaultHandler(named(s3middleware.SelectObjectContentOperation, h.SelectObjectContentHandler)))
	})

	// DELETE method handlers
	objRouter.Group(func(r chi.Router) {
		r.Method(http.MethodDelete, "/*", NewHandlerFilter().
			Add(NewFilter().
				Queries(s3middleware.UploadIDQuery).
				Handler(named(s3middleware.AbortMultipartUploadOperation, h.AbortMultipartUploadHandler))).
			Add(NewFilter().
				Queries(s3middleware.TaggingQuery).
				Handler(named(s3middleware.DeleteObjectTaggingOperation, h.DeleteObjectTaggingHandler))).
			DefaultHandler(named(s3middleware.DeleteObjectOperation, h.DeleteObjectHandler)))
	})

	attachErrorHandler(objRouter)

	return objRouter
}