package router

import (
	"github.com/atlasos/calypso/internal/common/config"
	"github.com/atlasos/calypso/internal/common/database"
	"github.com/atlasos/calypso/internal/common/logger"
	"github.com/atlasos/calypso/internal/audit"
	"github.com/atlasos/calypso/internal/auth"
	"github.com/atlasos/calypso/internal/iam"
	"github.com/atlasos/calypso/internal/scst"
	"github.com/atlasos/calypso/internal/storage"
	"github.com/atlasos/calypso/internal/system"
	"github.com/atlasos/calypso/internal/tape_physical"
	"github.com/atlasos/calypso/internal/tape_vtl"
	"github.com/atlasos/calypso/internal/tasks"
	"github.com/gin-gonic/gin"
)

// NewRouter creates and configures the HTTP router
func NewRouter(cfg *config.Config, db *database.DB, log *logger.Logger) *gin.Engine {
	if cfg.Logging.Level == "debug" {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}

	r := gin.New()

	// Middleware
	r.Use(ginLogger(log))
	r.Use(gin.Recovery())
	r.Use(corsMiddleware())

	// Health check (no auth required)
	r.GET("/api/v1/health", healthHandler(db))

	// API v1 routes
	v1 := r.Group("/api/v1")
	{
		// Auth routes (public)
		authHandler := auth.NewHandler(db, cfg, log)
		v1.POST("/auth/login", authHandler.Login)
		v1.POST("/auth/logout", authHandler.Logout)

		// Audit middleware for mutating operations (applied to all v1 routes)
		auditMiddleware := audit.NewMiddleware(db, log)
		v1.Use(auditMiddleware.LogRequest())

		// Protected routes
		protected := v1.Group("")
		protected.Use(authMiddleware(authHandler))
		protected.Use(func(c *gin.Context) {
			// Store DB in context for permission middleware
			c.Set("db", db)
			c.Next()
		})
		{
			// Auth
			protected.GET("/auth/me", authHandler.Me)

			// Tasks
			taskHandler := tasks.NewHandler(db, log)
			protected.GET("/tasks/:id", taskHandler.GetTask)

			// Storage
			storageHandler := storage.NewHandler(db, log)
			storageGroup := protected.Group("/storage")
			storageGroup.Use(requirePermission("storage", "read"))
			{
				storageGroup.GET("/disks", storageHandler.ListDisks)
				storageGroup.POST("/disks/sync", storageHandler.SyncDisks)
				storageGroup.GET("/volume-groups", storageHandler.ListVolumeGroups)
				storageGroup.GET("/repositories", storageHandler.ListRepositories)
				storageGroup.GET("/repositories/:id", storageHandler.GetRepository)
				storageGroup.POST("/repositories", storageHandler.CreateRepository)
				storageGroup.DELETE("/repositories/:id", storageHandler.DeleteRepository)
			}

			// SCST
			scstHandler := scst.NewHandler(db, log)
			scstGroup := protected.Group("/scst")
			scstGroup.Use(requirePermission("iscsi", "read"))
			{
				scstGroup.GET("/targets", scstHandler.ListTargets)
				scstGroup.GET("/targets/:id", scstHandler.GetTarget)
				scstGroup.POST("/targets", scstHandler.CreateTarget)
				scstGroup.POST("/targets/:id/luns", scstHandler.AddLUN)
				scstGroup.POST("/targets/:id/initiators", scstHandler.AddInitiator)
				scstGroup.POST("/config/apply", scstHandler.ApplyConfig)
				scstGroup.GET("/handlers", scstHandler.ListHandlers)
			}

			// Physical Tape Libraries
			tapeHandler := tape_physical.NewHandler(db, log)
			tapeGroup := protected.Group("/tape/physical")
			tapeGroup.Use(requirePermission("tape", "read"))
			{
				tapeGroup.GET("/libraries", tapeHandler.ListLibraries)
				tapeGroup.POST("/libraries/discover", tapeHandler.DiscoverLibraries)
				tapeGroup.GET("/libraries/:id", tapeHandler.GetLibrary)
				tapeGroup.POST("/libraries/:id/inventory", tapeHandler.PerformInventory)
				tapeGroup.POST("/libraries/:id/load", tapeHandler.LoadTape)
				tapeGroup.POST("/libraries/:id/unload", tapeHandler.UnloadTape)
			}

			// Virtual Tape Libraries
			vtlHandler := tape_vtl.NewHandler(db, log)
			vtlGroup := protected.Group("/tape/vtl")
			vtlGroup.Use(requirePermission("tape", "read"))
			{
				vtlGroup.GET("/libraries", vtlHandler.ListLibraries)
				vtlGroup.POST("/libraries", vtlHandler.CreateLibrary)
				vtlGroup.GET("/libraries/:id", vtlHandler.GetLibrary)
				vtlGroup.DELETE("/libraries/:id", vtlHandler.DeleteLibrary)
				vtlGroup.GET("/libraries/:id/drives", vtlHandler.GetLibraryDrives)
				vtlGroup.GET("/libraries/:id/tapes", vtlHandler.GetLibraryTapes)
				vtlGroup.POST("/libraries/:id/tapes", vtlHandler.CreateTape)
				vtlGroup.POST("/libraries/:id/load", vtlHandler.LoadTape)
				vtlGroup.POST("/libraries/:id/unload", vtlHandler.UnloadTape)
			}

			// System Management
			systemHandler := system.NewHandler(log, tasks.NewEngine(db, log))
			systemGroup := protected.Group("/system")
			systemGroup.Use(requirePermission("system", "read"))
			{
				systemGroup.GET("/services", systemHandler.ListServices)
				systemGroup.GET("/services/:name", systemHandler.GetServiceStatus)
				systemGroup.POST("/services/:name/restart", systemHandler.RestartService)
				systemGroup.GET("/services/:name/logs", systemHandler.GetServiceLogs)
				systemGroup.POST("/support-bundle", systemHandler.GenerateSupportBundle)
			}

			// IAM (admin only)
			iamHandler := iam.NewHandler(db, log)
			iamGroup := protected.Group("/iam")
			iamGroup.Use(requireRole("admin"))
			{
				iamGroup.GET("/users", iamHandler.ListUsers)
				iamGroup.GET("/users/:id", iamHandler.GetUser)
				iamGroup.POST("/users", iamHandler.CreateUser)
				iamGroup.PUT("/users/:id", iamHandler.UpdateUser)
				iamGroup.DELETE("/users/:id", iamHandler.DeleteUser)
			}
		}
	}

	return r
}

// ginLogger creates a Gin middleware for logging
func ginLogger(log *logger.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()

		log.Info("HTTP request",
			"method", c.Request.Method,
			"path", c.Request.URL.Path,
			"status", c.Writer.Status(),
			"client_ip", c.ClientIP(),
			"latency_ms", c.Writer.Size(),
		)
	}
}

// corsMiddleware adds CORS headers
func corsMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE, PATCH")

		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}

		c.Next()
	}
}

// healthHandler returns system health status
func healthHandler(db *database.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		// Check database connection
		if err := db.Ping(); err != nil {
			c.JSON(503, gin.H{
				"status": "unhealthy",
				"error":  "database connection failed",
			})
			return
		}

		c.JSON(200, gin.H{
			"status": "healthy",
			"service": "calypso-api",
		})
	}
}

