package handler

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
)

// securityHeaders sets conservative response headers on every response: prevent
// MIME sniffing of served file content, forbid framing, and suppress the
// Referer header on outbound navigations.
func securityHeaders() gin.HandlerFunc {
	return func(c *gin.Context) {
		h := c.Writer.Header()
		h.Set("X-Content-Type-Options", "nosniff")
		h.Set("X-Frame-Options", "DENY")
		h.Set("Referrer-Policy", "no-referrer")
		c.Next()
	}
}

// NewRouter builds and returns a configured Gin engine.
func NewRouter(
	auth *AuthMiddleware,
	authHandler *AuthHandler,
	fileHandler *FileHandler,
	tagHandler *TagHandler,
	categoryHandler *CategoryHandler,
	poolHandler *PoolHandler,
	userHandler *UserHandler,
	aclHandler *ACLHandler,
	auditHandler *AuditHandler,
) *gin.Engine {
	r := gin.New()
	r.Use(gin.Logger(), gin.Recovery(), securityHeaders())

	// Health check — no auth required.
	r.GET("/health", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"status": "ok"})
	})

	v1 := r.Group("/api/v1")

	// -------------------------------------------------------------------------
	// Auth
	// -------------------------------------------------------------------------
	authGroup := v1.Group("/auth")
	{
		// Throttle credential endpoints per client IP to slow brute force.
		authLimiter := newRateLimiter(10, time.Minute).Middleware()
		authGroup.POST("/login", authLimiter, authHandler.Login)
		authGroup.POST("/refresh", authLimiter, authHandler.Refresh)

		protected := authGroup.Group("", auth.Handle())
		{
			protected.POST("/logout", authHandler.Logout)
			protected.GET("/sessions", authHandler.ListSessions)
			protected.DELETE("/sessions/:id", authHandler.TerminateSession)
		}
	}

	// -------------------------------------------------------------------------
	// Files (all require auth)
	// -------------------------------------------------------------------------
	files := v1.Group("/files", auth.Handle())
	{
		files.GET("", fileHandler.List)
		files.POST("", fileHandler.Upload)

		// Bulk + import routes registered before /:id to prevent param collision.
		files.POST("/bulk/tags", fileHandler.BulkSetTags)
		files.POST("/bulk/delete", fileHandler.BulkDelete)
		files.POST("/bulk/common-tags", fileHandler.CommonTags)
		files.POST("/import", fileHandler.Import)

		// Per-file routes.
		files.GET("/:id", fileHandler.GetMeta)
		files.PATCH("/:id", fileHandler.UpdateMeta)
		files.DELETE("/:id", fileHandler.SoftDelete)

		files.GET("/:id/content", fileHandler.GetContent)
		files.PUT("/:id/content", fileHandler.ReplaceContent)
		files.GET("/:id/thumbnail", fileHandler.GetThumbnail)
		files.GET("/:id/preview", fileHandler.GetPreview)
		files.POST("/:id/restore", fileHandler.Restore)
		files.DELETE("/:id/permanent", fileHandler.PermanentDelete)

		// File–tag relations — served by TagHandler for auto-rule support.
		files.GET("/:id/tags", tagHandler.FileListTags)
		files.PUT("/:id/tags", tagHandler.FileSetTags)
		files.PUT("/:id/tags/:tag_id", tagHandler.FileAddTag)
		files.DELETE("/:id/tags/:tag_id", tagHandler.FileRemoveTag)
	}

	// -------------------------------------------------------------------------
	// Tags (all require auth)
	// -------------------------------------------------------------------------
	tags := v1.Group("/tags", auth.Handle())
	{
		tags.GET("", tagHandler.List)
		tags.POST("", tagHandler.Create)

		tags.GET("/:tag_id", tagHandler.Get)
		tags.PATCH("/:tag_id", tagHandler.Update)
		tags.DELETE("/:tag_id", tagHandler.Delete)

		tags.GET("/:tag_id/files", tagHandler.ListFiles)

		tags.GET("/:tag_id/rules", tagHandler.ListRules)
		tags.POST("/:tag_id/rules", tagHandler.CreateRule)
		tags.PATCH("/:tag_id/rules/:then_tag_id", tagHandler.PatchRule)
		tags.DELETE("/:tag_id/rules/:then_tag_id", tagHandler.DeleteRule)
	}

	// -------------------------------------------------------------------------
	// Categories (all require auth)
	// -------------------------------------------------------------------------
	categories := v1.Group("/categories", auth.Handle())
	{
		categories.GET("", categoryHandler.List)
		categories.POST("", categoryHandler.Create)

		categories.GET("/:category_id", categoryHandler.Get)
		categories.PATCH("/:category_id", categoryHandler.Update)
		categories.DELETE("/:category_id", categoryHandler.Delete)

		categories.GET("/:category_id/tags", categoryHandler.ListTags)
	}

	// -------------------------------------------------------------------------
	// Pools (all require auth)
	// -------------------------------------------------------------------------
	pools := v1.Group("/pools", auth.Handle())
	{
		pools.GET("", poolHandler.List)
		pools.POST("", poolHandler.Create)

		pools.GET("/:pool_id", poolHandler.Get)
		pools.PATCH("/:pool_id", poolHandler.Update)
		pools.DELETE("/:pool_id", poolHandler.Delete)

		// Sub-routes registered before /:pool_id/files to avoid param conflicts.
		pools.POST("/:pool_id/files/remove", poolHandler.RemoveFiles)
		pools.PUT("/:pool_id/files/reorder", poolHandler.Reorder)

		pools.GET("/:pool_id/files", poolHandler.ListFiles)
		pools.POST("/:pool_id/files", poolHandler.AddFiles)
	}

	// -------------------------------------------------------------------------
	// Users (auth required; admin checks enforced in handler)
	// -------------------------------------------------------------------------
	users := v1.Group("/users", auth.Handle())
	{
		// /users/me must be registered before /:user_id to avoid param capture.
		users.GET("/me", userHandler.GetMe)
		users.PATCH("/me", userHandler.UpdateMe)

		users.GET("", userHandler.List)
		users.POST("", userHandler.Create)

		users.GET("/:user_id", userHandler.Get)
		users.PATCH("/:user_id", userHandler.UpdateAdmin)
		users.DELETE("/:user_id", userHandler.Delete)
	}

	// -------------------------------------------------------------------------
	// ACL (auth required)
	// -------------------------------------------------------------------------
	acl := v1.Group("/acl", auth.Handle())
	{
		acl.GET("/:object_type/:object_id", aclHandler.GetPermissions)
		acl.PUT("/:object_type/:object_id", aclHandler.SetPermissions)
	}

	// -------------------------------------------------------------------------
	// Audit (auth required; admin check enforced in handler)
	// -------------------------------------------------------------------------
	v1.GET("/audit", auth.Handle(), auditHandler.List)

	return r
}