// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
// 🤖 Github Repository: https://github.com/gofiber/fiber
// 📌 API Documentation: https://docs.gofiber.io

package fiber

import (
	
	
	
	
	
	
	

	

	
)

// Router defines all router handle interface, including app and group router.
type Router interface {
	Use(args ...interface{}) Router

	Get(path string, handlers ...Handler) Router
	Head(path string, handlers ...Handler) Router
	Post(path string, handlers ...Handler) Router
	Put(path string, handlers ...Handler) Router
	Delete(path string, handlers ...Handler) Router
	Connect(path string, handlers ...Handler) Router
	Options(path string, handlers ...Handler) Router
	Trace(path string, handlers ...Handler) Router
	Patch(path string, handlers ...Handler) Router

	Add(method, path string, handlers ...Handler) Router
	Static(prefix, root string, config ...Static) Router
	All(path string, handlers ...Handler) Router

	Group(prefix string, handlers ...Handler) Router

	Route(prefix string, fn func(router Router), name ...string) Router

	Mount(prefix string, fiber *App) Router

	Name(name string) Router
}

// Route is a struct that holds all metadata for each registered handler.
type Route struct {
	// always keep in sync with the copy method "app.copyRoute"
	// Data for routing
	pos         uint32      // Position in stack -> important for the sort of the matched routes
	use         bool        // USE matches path prefixes
	mount       bool        // Indicated a mounted app on a specific route
	star        bool        // Path equals '*'
	root        bool        // Path equals '/'
	path        string      // Prettified path
	routeParser routeParser // Parameter parser
	group       *Group      // Group instance. used for routes in groups

	// Public fields
	Method string `json:"method"` // HTTP method
	Name   string `json:"name"`   // Route's name
	//nolint:revive // Having both a Path (uppercase) and a path (lowercase) is fine
	Path     string    `json:"path"`   // Original registered route path
	Params   []string  `json:"params"` // Case sensitive param keys
	Handlers []Handler `json:"-"`      // Ctx handlers
}

func ( *Route) (,  string,  *[maxParams]string) bool {
	// root detectionPath check
	if .root &&  == "/" {
		return true
		// '*' wildcard matches any detectionPath
	} else if .star {
		if len() > 1 {
			[0] = [1:]
		} else {
			[0] = ""
		}
		return true
	}
	// Does this route have parameters
	if len(.Params) > 0 {
		// Match params
		if  := .routeParser.getMatch(, , , .use);  {
			// Get params from the path detectionPath
			return 
		}
	}
	// Is this route a Middleware?
	if .use {
		// Single slash will match or detectionPath prefix
		if .root || strings.HasPrefix(, .path) {
			return true
		}
		// Check for a simple detectionPath match
	} else if len(.path) == len() && .path ==  {
		return true
	}
	// No match
	return false
}

func ( *App) ( *Ctx) (bool, error) {
	// Get stack length
	,  := .treeStack[.methodINT][.treePath]
	if ! {
		 = .treeStack[.methodINT][""]
	}
	 := len() - 1

	// Loop over the route stack starting from previous index
	for .indexRoute <  {
		// Increment route index
		.indexRoute++

		// Get *Route
		 := [.indexRoute]

		var  bool
		var  error
		// skip for mounted apps
		if .mount {
			continue
		}

		// Check if it matches the request path
		 = .match(.detectionPath, .path, &.values)
		if ! {
			// No match, next route
			continue
		}
		// Pass route reference and param values
		.route = 

		// Non use handler matched
		if !.matched && !.use {
			.matched = true
		}

		// Execute first handler of route
		.indexHandler = 0
		if len(.Handlers) > 0 {
			 = .Handlers[0]()
		}
		return ,  // Stop scanning the stack
	}

	// If c.Next() does not match, return 404
	 := NewError(StatusNotFound, "Cannot "+.method+" "+html.EscapeString(.pathOriginal))
	if !.matched && .methodExist() {
		// If no match, scan stack again if other methods match the request
		// Moved from app.handler because middleware may break the route chain
		 = ErrMethodNotAllowed
	}
	return false, 
}

func ( *App) ( *fasthttp.RequestCtx) { //revive:disable-line:confusing-naming // Having both a Handler() (uppercase) and a handler() (lowercase) is fine. TODO: Use nolint:revive directive instead. See https://github.com/golangci/golangci-lint/issues/3476
	// Acquire Ctx with fasthttp request from pool
	 := .AcquireCtx()
	defer .ReleaseCtx()

	// handle invalid http method directly
	if .methodINT == -1 {
		_ = .Status(StatusBadRequest).SendString("Invalid http method") //nolint:errcheck // It is fine to ignore the error here
		return
	}

	// Find match in stack
	,  := .next()
	if  != nil {
		if  := .app.ErrorHandler(, );  != nil {
			_ = .SendStatus(StatusInternalServerError) //nolint:errcheck // It is fine to ignore the error here
		}
		// TODO: Do we need to return here?
	}
	// Generate ETag if enabled
	if  && .config.ETag {
		setETag(, false)
	}
}

func ( *App) ( string,  *Route) *Route {
	 := getGroupPath(, .Path)
	 := 
	// Case-sensitive routing, all to lowercase
	if !.config.CaseSensitive {
		 = utils.ToLower()
	}
	// Strict routing, remove trailing slashes
	if !.config.StrictRouting && len() > 1 {
		 = utils.TrimRight(, '/')
	}

	.Path = 
	.path = RemoveEscapeChar()
	.routeParser = parseRoute()
	.root = false
	.star = false

	return 
}

func (*App) ( *Route) *Route {
	return &Route{
		// Router booleans
		use:   .use,
		mount: .mount,
		star:  .star,
		root:  .root,

		// Path data
		path:        .path,
		routeParser: .routeParser,
		Params:      .Params,

		// misc
		pos: .pos,

		// Public data
		Path:     .Path,
		Method:   .Method,
		Handlers: .Handlers,
	}
}

func ( *App) (,  string,  *Group,  ...Handler) {
	// Uppercase HTTP methods
	 = utils.ToUpper()
	// Check if the HTTP method is valid unless it's USE
	if  != methodUse && .methodInt() == -1 {
		panic(fmt.Sprintf("add: invalid http method %s\n", ))
	}
	// is mounted app
	 :=  != nil && .app != 
	// A route requires atleast one ctx handler
	if len() == 0 && ! {
		panic(fmt.Sprintf("missing handler in route: %s\n", ))
	}
	// Cannot have an empty path
	if  == "" {
		 = "/"
	}
	// Path always start with a '/'
	if [0] != '/' {
		 = "/" + 
	}
	// Create a stripped path in-case sensitive / trailing slashes
	 := 
	// Case-sensitive routing, all to lowercase
	if !.config.CaseSensitive {
		 = utils.ToLower()
	}
	// Strict routing, remove trailing slashes
	if !.config.StrictRouting && len() > 1 {
		 = utils.TrimRight(, '/')
	}
	// Is layer a middleware?
	 :=  == methodUse
	// Is path a direct wildcard?
	 :=  == "/*"
	// Is path a root slash?
	 :=  == "/"
	// Parse path parameters
	 := parseRoute()
	 := parseRoute()

	// Create route metadata without pointer
	 := Route{
		// Router booleans
		use:   ,
		mount: ,
		star:  ,
		root:  ,

		// Path data
		path:        RemoveEscapeChar(),
		routeParser: ,
		Params:      .params,

		// Group data
		group: ,

		// Public data
		Path:     ,
		Method:   ,
		Handlers: ,
	}
	// Increment global handler count
	atomic.AddUint32(&.handlersCount, uint32(len()))

	// Middleware route matches all HTTP methods
	if  {
		// Add route to all HTTP methods stack
		for ,  := range .config.RequestMethods {
			// Create a route copy to avoid duplicates during compression
			 := 
			.addRoute(, &, )
		}
	} else {
		// Add route to stack
		.addRoute(, &, )
	}
}

func ( *App) (,  string,  ...Static) {
	// For security, we want to restrict to the current work directory.
	if  == "" {
		 = "."
	}
	// Cannot have an empty prefix
	if  == "" {
		 = "/"
	}
	// Prefix always start with a '/' or '*'
	if [0] != '/' {
		 = "/" + 
	}
	// in case-sensitive routing, all to lowercase
	if !.config.CaseSensitive {
		 = utils.ToLower()
	}
	// Strip trailing slashes from the root path
	if len() > 0 && [len()-1] == '/' {
		 = [:len()-1]
	}
	// Is prefix a direct wildcard?
	 :=  == "/*"
	// Is prefix a root slash?
	 :=  == "/"
	// Is prefix a partial wildcard?
	if strings.Contains(, "*") {
		// /john* -> /john
		 = true
		 = strings.Split(, "*")[0]
		// Fix this later
	}
	 := len()
	if  > 1 && [-1:] == "/" {
		// /john/ -> /john
		--
		 = [:]
	}
	const  = 10 * time.Second
	// Fileserver settings
	 := &fasthttp.FS{
		Root:                 ,
		AllowEmptyRoot:       true,
		GenerateIndexPages:   false,
		AcceptByteRange:      false,
		Compress:             false,
		CompressedFileSuffix: .config.CompressedFileSuffix,
		CacheDuration:        ,
		IndexNames:           []string{"index.html"},
		PathRewrite: func( *fasthttp.RequestCtx) []byte {
			 := .Path()
			if len() >=  {
				if  && .getString([0:]) ==  {
					 = append([0:0], '/')
				} else {
					 = [:]
					if len() == 0 || [len()-1] != '/' {
						 = append(, '/')
					}
				}
			}
			if len() > 0 && [0] != '/' {
				 = append([]byte("/"), ...)
			}
			return 
		},
		PathNotFound: func( *fasthttp.RequestCtx) {
			.Response.SetStatusCode(StatusNotFound)
		},
	}

	// Set config if provided
	var  string
	var  Handler
	if len() > 0 {
		 := [0].MaxAge
		if  > 0 {
			 = "public, max-age=" + strconv.Itoa()
		}
		.CacheDuration = [0].CacheDuration
		.Compress = [0].Compress
		.AcceptByteRange = [0].ByteRange
		.GenerateIndexPages = [0].Browse
		if [0].Index != "" {
			.IndexNames = []string{[0].Index}
		}
		 = [0].ModifyResponse
	}
	 := .NewRequestHandler()
	 := func( *Ctx) error {
		// Don't execute middleware if Next returns true
		if len() != 0 && [0].Next != nil && [0].Next() {
			return .Next()
		}
		// Serve file
		(.fasthttp)
		// Sets the response Content-Disposition header to attachment if the Download option is true
		if len() > 0 && [0].Download {
			.Attachment()
		}
		// Return request if found and not forbidden
		 := .fasthttp.Response.StatusCode()
		if  != StatusNotFound &&  != StatusForbidden {
			if len() > 0 {
				.fasthttp.Response.Header.Set(HeaderCacheControl, )
			}
			if  != nil {
				return ()
			}
			return nil
		}
		// Reset response to default
		.fasthttp.SetContentType("") // Issue #420
		.fasthttp.Response.SetStatusCode(StatusOK)
		.fasthttp.Response.SetBodyString("")
		// Next middleware
		return .Next()
	}

	// Create route metadata without pointer
	 := Route{
		// Router booleans
		use:  true,
		root: ,
		path: ,
		// Public data
		Method:   MethodGet,
		Path:     ,
		Handlers: []Handler{},
	}
	// Increment global handler count
	atomic.AddUint32(&.handlersCount, 1)
	// Add route to stack
	.addRoute(MethodGet, &)
	// Add HEAD route
	.addRoute(MethodHead, &)
}

func ( *App) ( string,  *Route,  ...bool) {
	// Check mounted routes
	var  bool
	if len() > 0 {
		 = [0]
	}

	// Get unique HTTP method identifier
	 := .methodInt()

	// prevent identically route registration
	 := len(.stack[])
	if  > 0 && .stack[][-1].Path == .Path && .use == .stack[][-1].use && !.mount && !.stack[][-1].mount {
		 := .stack[][-1]
		.Handlers = append(.Handlers, .Handlers...)
	} else {
		// Increment global route position
		.pos = atomic.AddUint32(&.routesCount, 1)
		.Method = 
		// Add route to the stack
		.stack[] = append(.stack[], )
		.routesRefreshed = true
	}

	// Execute onRoute hooks & change latestRoute if not adding mounted route
	if ! {
		.mutex.Lock()
		.latestRoute = 
		if  := .hooks.executeOnRouteHooks(*);  != nil {
			panic()
		}
		.mutex.Unlock()
	}
}

// buildTree build the prefix tree from the previously registered routes
func ( *App) () *App {
	if !.routesRefreshed {
		return 
	}

	// loop all the methods and stacks and create the prefix tree
	for  := range .config.RequestMethods {
		 := make(map[string][]*Route)
		for ,  := range .stack[] {
			 := ""
			if len(.routeParser.segs) > 0 && len(.routeParser.segs[0].Const) >= 3 {
				 = .routeParser.segs[0].Const[:3]
			}
			// create tree stack
			[] = append([], )
		}
		.treeStack[] = 
	}

	// loop the methods and tree stacks and add global stack and sort everything
	for  := range .config.RequestMethods {
		 := .treeStack[]
		for  := range  {
			if  != "" {
				// merge global tree routes in current tree stack
				[] = uniqueRouteStack(append([], [""]...))
			}
			// sort tree slices with the positions
			 := []
			sort.Slice(, func(,  int) bool { return [].pos < [].pos })
		}
	}
	.routesRefreshed = false

	return 
}