package router

import (
	"context"
	"time"

	"github.com/atlasos/calypso/internal/audit"
	"github.com/atlasos/calypso/internal/auth"
	"github.com/atlasos/calypso/internal/backup"
	"github.com/atlasos/calypso/internal/bacula"

	"github.com/atlasos/calypso/internal/common/cache"
	"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/iam"
	"github.com/atlasos/calypso/internal/monitoring"
	"github.com/atlasos/calypso/internal/object_storage"
	"github.com/atlasos/calypso/internal/scst"
	"github.com/atlasos/calypso/internal/shares"
	"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()

	// Initialize cache if enabled
	var responseCache *cache.Cache
	if cfg.Server.Cache.Enabled {
		responseCache = cache.NewCache(cfg.Server.Cache.DefaultTTL)
		log.Info("Response caching enabled", "default_ttl", cfg.Server.Cache.DefaultTTL)
	}

	// Middleware
	r.Use(ginLogger(log))
	r.Use(gin.Recovery())
	r.Use(securityHeadersMiddleware(cfg))
	r.Use(rateLimitMiddleware(cfg, log))
	r.Use(corsMiddleware(cfg))

	// Cache control headers (always applied)
	r.Use(cacheControlMiddleware())

	// Response caching middleware (if enabled)
	if cfg.Server.Cache.Enabled {
		cacheConfig := CacheConfig{
			Enabled:    cfg.Server.Cache.Enabled,
			DefaultTTL: cfg.Server.Cache.DefaultTTL,
			MaxAge:     cfg.Server.Cache.MaxAge,
		}
		r.Use(cacheMiddleware(cacheConfig, responseCache))
	}

	// Initialize monitoring services
	eventHub := monitoring.NewEventHub(log)
	alertService := monitoring.NewAlertService(db, log)
	alertService.SetEventHub(eventHub) // Connect alert service to event hub
	metricsService := monitoring.NewMetricsService(db, log)
	healthService := monitoring.NewHealthService(db, log, metricsService)

	// Start event hub in background
	go eventHub.Run()

	// Start metrics broadcaster in background
	go func() {
		ticker := time.NewTicker(30 * time.Second) // Broadcast metrics every 30 seconds
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				if metrics, err := metricsService.CollectMetrics(context.Background()); err == nil {
					eventHub.BroadcastMetrics(metrics)
				}
			}
		}
	}()

	// Initialize and start alert rule engine
	alertRuleEngine := monitoring.NewAlertRuleEngine(db, log, alertService)

	// Register default alert rules
	alertRuleEngine.RegisterRule(monitoring.NewAlertRule(
		"storage-capacity-warning",
		"Storage Capacity Warning",
		monitoring.AlertSourceStorage,
		&monitoring.StorageCapacityCondition{ThresholdPercent: 80.0},
		monitoring.AlertSeverityWarning,
		true,
		"Alert when storage repositories exceed 80% capacity",
	))
	alertRuleEngine.RegisterRule(monitoring.NewAlertRule(
		"storage-capacity-critical",
		"Storage Capacity Critical",
		monitoring.AlertSourceStorage,
		&monitoring.StorageCapacityCondition{ThresholdPercent: 95.0},
		monitoring.AlertSeverityCritical,
		true,
		"Alert when storage repositories exceed 95% capacity",
	))
	alertRuleEngine.RegisterRule(monitoring.NewAlertRule(
		"task-failure",
		"Task Failure",
		monitoring.AlertSourceTask,
		&monitoring.TaskFailureCondition{LookbackMinutes: 60},
		monitoring.AlertSeverityWarning,
		true,
		"Alert when tasks fail within the last hour",
	))

	// Start alert rule engine in background
	ctx := context.Background()
	go alertRuleEngine.Start(ctx)

	// Health check (no auth required) - enhanced
	r.GET("/api/v1/health", func(c *gin.Context) {
		health := healthService.CheckHealth(c.Request.Context())
		statusCode := 200
		if health.Status == "unhealthy" {
			statusCode = 503
		} else if health.Status == "degraded" {
			statusCode = 200 // Still 200 but with degraded status
		}
		c.JSON(statusCode, health)
	})

	// 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)
			// Pass cache to storage handler for cache invalidation
			if responseCache != nil {
				storageHandler.SetCache(responseCache)
			}

			// Start disk monitor service in background (syncs disks every 5 minutes)
			diskMonitor := storage.NewDiskMonitor(db, log, 5*time.Minute)
			go diskMonitor.Start(context.Background())

			// Start ZFS pool monitor service in background (syncs pools every 2 minutes)
			zfsPoolMonitor := storage.NewZFSPoolMonitor(db, log, 2*time.Minute)
			go zfsPoolMonitor.Start(context.Background())

			// Start snapshot schedule worker in background (checks schedules every 1 minute)
			snapshotService := storage.NewSnapshotService(db, log)
			snapshotScheduleWorker := storage.NewSnapshotScheduleWorker(db, log, snapshotService, 1*time.Minute)
			go snapshotScheduleWorker.Start(context.Background())

			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", requirePermission("storage", "write"), storageHandler.CreateRepository)
				storageGroup.DELETE("/repositories/:id", requirePermission("storage", "write"), storageHandler.DeleteRepository)
				// ZFS Pools
				storageGroup.GET("/zfs/pools", storageHandler.ListZFSPools)
				storageGroup.GET("/zfs/pools/:id", storageHandler.GetZFSPool)
				storageGroup.POST("/zfs/pools", requirePermission("storage", "write"), storageHandler.CreateZPool)
				storageGroup.DELETE("/zfs/pools/:id", requirePermission("storage", "write"), storageHandler.DeleteZFSPool)
				storageGroup.POST("/zfs/pools/:id/spare", requirePermission("storage", "write"), storageHandler.AddSpareDisk)
				// ZFS Datasets
				storageGroup.GET("/zfs/pools/:id/datasets", storageHandler.ListZFSDatasets)
				storageGroup.POST("/zfs/pools/:id/datasets", requirePermission("storage", "write"), storageHandler.CreateZFSDataset)
				storageGroup.DELETE("/zfs/pools/:id/datasets/:dataset", requirePermission("storage", "write"), storageHandler.DeleteZFSDataset)
				// ZFS Snapshots
				storageGroup.GET("/zfs/snapshots", storageHandler.ListSnapshots)
				storageGroup.POST("/zfs/snapshots", requirePermission("storage", "write"), storageHandler.CreateSnapshot)
				storageGroup.DELETE("/zfs/snapshots/:name", requirePermission("storage", "write"), storageHandler.DeleteSnapshot)
				storageGroup.POST("/zfs/snapshots/:name/rollback", requirePermission("storage", "write"), storageHandler.RollbackSnapshot)
				storageGroup.POST("/zfs/snapshots/:name/clone", requirePermission("storage", "write"), storageHandler.CloneSnapshot)
				// Snapshot Schedules
				storageGroup.GET("/zfs/snapshot-schedules", storageHandler.ListSnapshotSchedules)
				storageGroup.GET("/zfs/snapshot-schedules/:id", storageHandler.GetSnapshotSchedule)
				storageGroup.POST("/zfs/snapshot-schedules", requirePermission("storage", "write"), storageHandler.CreateSnapshotSchedule)
				storageGroup.PUT("/zfs/snapshot-schedules/:id", requirePermission("storage", "write"), storageHandler.UpdateSnapshotSchedule)
				storageGroup.DELETE("/zfs/snapshot-schedules/:id", requirePermission("storage", "write"), storageHandler.DeleteSnapshotSchedule)
				storageGroup.POST("/zfs/snapshot-schedules/:id/toggle", requirePermission("storage", "write"), storageHandler.ToggleSnapshotSchedule)

				// Replication Tasks
				storageGroup.GET("/zfs/replication-tasks", storageHandler.ListReplicationTasks)
				storageGroup.GET("/zfs/replication-tasks/:id", storageHandler.GetReplicationTask)
				storageGroup.POST("/zfs/replication-tasks", requirePermission("storage", "write"), storageHandler.CreateReplicationTask)
				storageGroup.PUT("/zfs/replication-tasks/:id", requirePermission("storage", "write"), storageHandler.UpdateReplicationTask)
				storageGroup.DELETE("/zfs/replication-tasks/:id", requirePermission("storage", "write"), storageHandler.DeleteReplicationTask)
				// ZFS ARC Stats
				storageGroup.GET("/zfs/arc/stats", storageHandler.GetARCStats)
			}

			// Shares (CIFS/NFS)
			sharesHandler := shares.NewHandler(db, log)
			sharesGroup := protected.Group("/shares")
			sharesGroup.Use(requirePermission("storage", "read"))
			{
				sharesGroup.GET("", sharesHandler.ListShares)
				sharesGroup.GET("/:id", sharesHandler.GetShare)
				sharesGroup.POST("", requirePermission("storage", "write"), sharesHandler.CreateShare)
				sharesGroup.PUT("/:id", requirePermission("storage", "write"), sharesHandler.UpdateShare)
				sharesGroup.DELETE("/:id", requirePermission("storage", "write"), sharesHandler.DeleteShare)
			}

			// Object Storage (MinIO)
			// Initialize MinIO service if configured
			if cfg.ObjectStorage.Endpoint != "" {
				objectStorageService, err := object_storage.NewService(
					cfg.ObjectStorage.Endpoint,
					cfg.ObjectStorage.AccessKey,
					cfg.ObjectStorage.SecretKey,
					log,
				)
				if err != nil {
					log.Error("Failed to initialize MinIO service", "error", err)
				} else {
					objectStorageHandler := object_storage.NewHandler(objectStorageService, db, log)
					objectStorageGroup := protected.Group("/object-storage")
					objectStorageGroup.Use(requirePermission("storage", "read"))
					{
						// Setup endpoints
						objectStorageGroup.GET("/setup/datasets", objectStorageHandler.GetAvailableDatasets)
						objectStorageGroup.GET("/setup/current", objectStorageHandler.GetCurrentSetup)
						objectStorageGroup.POST("/setup", requirePermission("storage", "write"), objectStorageHandler.SetupObjectStorage)
						objectStorageGroup.PUT("/setup", requirePermission("storage", "write"), objectStorageHandler.UpdateObjectStorage)

						// Bucket endpoints
						// IMPORTANT: More specific routes must come BEFORE less specific ones
						objectStorageGroup.GET("/buckets", objectStorageHandler.ListBuckets)
						objectStorageGroup.GET("/buckets/:name/objects", objectStorageHandler.ListObjects)
						objectStorageGroup.GET("/buckets/:name", objectStorageHandler.GetBucket)
						objectStorageGroup.POST("/buckets", requirePermission("storage", "write"), objectStorageHandler.CreateBucket)
						objectStorageGroup.DELETE("/buckets/:name", requirePermission("storage", "write"), objectStorageHandler.DeleteBucket)
						// User management routes
						objectStorageGroup.GET("/users", objectStorageHandler.ListUsers)
						objectStorageGroup.POST("/users", requirePermission("storage", "write"), objectStorageHandler.CreateUser)
						objectStorageGroup.DELETE("/users/:access_key", requirePermission("storage", "write"), objectStorageHandler.DeleteUser)
						// Service account (access key) management routes
						objectStorageGroup.GET("/service-accounts", objectStorageHandler.ListServiceAccounts)
						objectStorageGroup.POST("/service-accounts", requirePermission("storage", "write"), objectStorageHandler.CreateServiceAccount)
						objectStorageGroup.DELETE("/service-accounts/:access_key", requirePermission("storage", "write"), objectStorageHandler.DeleteServiceAccount)
					}
				}
			}

			// 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", requirePermission("iscsi", "write"), scstHandler.AddLUN)
				scstGroup.DELETE("/targets/:id/luns/:lunId", requirePermission("iscsi", "write"), scstHandler.RemoveLUN)
				scstGroup.POST("/targets/:id/initiators", scstHandler.AddInitiator)
				scstGroup.POST("/targets/:id/enable", scstHandler.EnableTarget)
				scstGroup.POST("/targets/:id/disable", scstHandler.DisableTarget)
				scstGroup.DELETE("/targets/:id", requirePermission("iscsi", "write"), scstHandler.DeleteTarget)
				scstGroup.GET("/initiators", scstHandler.ListAllInitiators)
				scstGroup.GET("/initiators/:id", scstHandler.GetInitiator)
				scstGroup.DELETE("/initiators/:id", scstHandler.RemoveInitiator)
				scstGroup.GET("/extents", scstHandler.ListExtents)
				scstGroup.POST("/extents", scstHandler.CreateExtent)
				scstGroup.DELETE("/extents/:device", scstHandler.DeleteExtent)
				scstGroup.POST("/config/apply", scstHandler.ApplyConfig)
				scstGroup.GET("/handlers", scstHandler.ListHandlers)
				scstGroup.GET("/portals", scstHandler.ListPortals)
				scstGroup.GET("/portals/:id", scstHandler.GetPortal)
				scstGroup.POST("/portals", scstHandler.CreatePortal)
				scstGroup.PUT("/portals/:id", scstHandler.UpdatePortal)
				scstGroup.DELETE("/portals/:id", scstHandler.DeletePortal)
				// Initiator Groups routes
				scstGroup.GET("/initiator-groups", scstHandler.ListAllInitiatorGroups)
				scstGroup.GET("/initiator-groups/:id", scstHandler.GetInitiatorGroup)
				scstGroup.POST("/initiator-groups", requirePermission("iscsi", "write"), scstHandler.CreateInitiatorGroup)
				scstGroup.PUT("/initiator-groups/:id", requirePermission("iscsi", "write"), scstHandler.UpdateInitiatorGroup)
				scstGroup.DELETE("/initiator-groups/:id", requirePermission("iscsi", "write"), scstHandler.DeleteInitiatorGroup)
				scstGroup.POST("/initiator-groups/:id/initiators", requirePermission("iscsi", "write"), scstHandler.AddInitiatorToGroup)
				// Config file management
				scstGroup.GET("/config/file", requirePermission("iscsi", "read"), scstHandler.GetConfigFile)
				scstGroup.PUT("/config/file", requirePermission("iscsi", "write"), scstHandler.UpdateConfigFile)
			}

			// 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)

			// Start MHVTL monitor service in background (syncs every 5 minutes)
			mhvtlMonitor := tape_vtl.NewMHVTLMonitor(db, log, "/etc/mhvtl", 5*time.Minute)
			go mhvtlMonitor.Start(context.Background())

			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
			systemService := system.NewService(log)
			systemHandler := system.NewHandler(log, tasks.NewEngine(db, log))
			// Set service in handler (if handler needs direct access)
			// Note: Handler already has service via NewHandler, but we need to ensure it's the same instance

			// Start network monitoring with RRD
			if err := systemService.StartNetworkMonitoring(context.Background()); err != nil {
				log.Warn("Failed to start network monitoring", "error", err)
			} else {
				log.Info("Network monitoring started with RRD")
			}

			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.GET("/logs", systemHandler.GetSystemLogs)
				systemGroup.GET("/network/throughput", systemHandler.GetNetworkThroughput)
				systemGroup.POST("/support-bundle", systemHandler.GenerateSupportBundle)
				systemGroup.GET("/interfaces", systemHandler.ListNetworkInterfaces)
				systemGroup.GET("/management-ip", systemHandler.GetManagementIPAddress)
				systemGroup.PUT("/interfaces/:name", systemHandler.UpdateNetworkInterface)
				systemGroup.GET("/ntp", systemHandler.GetNTPSettings)
				systemGroup.POST("/ntp", systemHandler.SaveNTPSettings)
				systemGroup.POST("/execute", requirePermission("system", "write"), systemHandler.ExecuteCommand)
			}

			// IAM routes - GetUser can be accessed by user viewing own profile or admin
			iamHandler := iam.NewHandler(db, cfg, log)
			protected.GET("/iam/users/:id", iamHandler.GetUser)

			// IAM admin routes
			iamGroup := protected.Group("/iam")
			iamGroup.Use(requireRole("admin"))
			{
				iamGroup.GET("/users", iamHandler.ListUsers)
				iamGroup.POST("/users", iamHandler.CreateUser)
				iamGroup.PUT("/users/:id", iamHandler.UpdateUser)
				iamGroup.DELETE("/users/:id", iamHandler.DeleteUser)
				// Roles routes
				iamGroup.GET("/roles", iamHandler.ListRoles)
				iamGroup.GET("/roles/:id", iamHandler.GetRole)
				iamGroup.POST("/roles", iamHandler.CreateRole)
				iamGroup.PUT("/roles/:id", iamHandler.UpdateRole)
				iamGroup.DELETE("/roles/:id", iamHandler.DeleteRole)
				iamGroup.GET("/roles/:id/permissions", iamHandler.GetRolePermissions)
				iamGroup.POST("/roles/:id/permissions", iamHandler.AssignPermissionToRole)
				iamGroup.DELETE("/roles/:id/permissions", iamHandler.RemovePermissionFromRole)

				// Permissions routes
				iamGroup.GET("/permissions", iamHandler.ListPermissions)

				// User role/group assignment
				iamGroup.POST("/users/:id/roles", iamHandler.AssignRoleToUser)
				iamGroup.DELETE("/users/:id/roles", iamHandler.RemoveRoleFromUser)
				iamGroup.POST("/users/:id/groups", iamHandler.AssignGroupToUser)
				iamGroup.DELETE("/users/:id/groups", iamHandler.RemoveGroupFromUser)

				// Groups routes
				iamGroup.GET("/groups", iamHandler.ListGroups)
				iamGroup.GET("/groups/:id", iamHandler.GetGroup)
				iamGroup.POST("/groups", iamHandler.CreateGroup)
				iamGroup.PUT("/groups/:id", iamHandler.UpdateGroup)
				iamGroup.DELETE("/groups/:id", iamHandler.DeleteGroup)
				iamGroup.POST("/groups/:id/users", iamHandler.AddUserToGroup)
				iamGroup.DELETE("/groups/:id/users/:user_id", iamHandler.RemoveUserFromGroup)
			}

			// Backup Jobs
			backupService := backup.NewService(db, log)
			// Set up direct connection to Bacula database
			// Try common Bacula database names
			baculaDBName := "bacula" // Default
			if err := backupService.SetBaculaDatabase(cfg.Database, baculaDBName); err != nil {
				log.Warn("Failed to connect to Bacula database, trying 'bareos'", "error", err)
				// Try 'bareos' as alternative
				if err := backupService.SetBaculaDatabase(cfg.Database, "bareos"); err != nil {
					log.Error("Failed to connect to Bacula database", "error", err, "tried", []string{"bacula", "bareos"})
					// Continue anyway - will fallback to bconsole
				}
			}
			backupHandler := backup.NewHandler(backupService, log)
			backupGroup := protected.Group("/backup")
			backupGroup.Use(requirePermission("backup", "read"))
			{
				backupGroup.GET("/dashboard/stats", backupHandler.GetDashboardStats)
				backupGroup.GET("/jobs", backupHandler.ListJobs)
				backupGroup.GET("/jobs/:id", backupHandler.GetJob)
				backupGroup.POST("/jobs", requirePermission("backup", "write"), backupHandler.CreateJob)
				backupGroup.GET("/clients", backupHandler.ListClients)
				backupGroup.GET("/storage/pools", backupHandler.ListStoragePools)
				backupGroup.POST("/storage/pools", requirePermission("backup", "write"), backupHandler.CreateStoragePool)
				backupGroup.DELETE("/storage/pools/:id", requirePermission("backup", "write"), backupHandler.DeleteStoragePool)
				backupGroup.GET("/storage/volumes", backupHandler.ListStorageVolumes)
				backupGroup.POST("/storage/volumes", requirePermission("backup", "write"), backupHandler.CreateStorageVolume)
				backupGroup.PUT("/storage/volumes/:id", requirePermission("backup", "write"), backupHandler.UpdateStorageVolume)
				backupGroup.DELETE("/storage/volumes/:id", requirePermission("backup", "write"), backupHandler.DeleteStorageVolume)
				backupGroup.GET("/media", backupHandler.ListMedia)
				backupGroup.GET("/storage/daemons", backupHandler.ListStorageDaemons)
				backupGroup.POST("/console/execute", requirePermission("backup", "write"), backupHandler.ExecuteBconsoleCommand)
			}

			baculaHandler := bacula.NewHandler(db, log)
			baculaGroup := protected.Group("/bacula/clients")
			baculaGroup.Use(requireRole("bacula-admin"))
			{
				baculaGroup.POST("/register", baculaHandler.Register)
				baculaGroup.POST("/:id/capabilities", baculaHandler.UpdateCapabilities)
				baculaGroup.GET("/:id/pending-update", baculaHandler.GetPendingUpdate)
				baculaGroup.POST("/:id/ping", baculaHandler.Ping)
				baculaGroup.GET("", baculaHandler.ListClients)
				baculaGroup.GET("/:id", baculaHandler.GetClient)
			}

			// Monitoring
			monitoringHandler := monitoring.NewHandler(db, log, alertService, metricsService, eventHub)
			monitoringGroup := protected.Group("/monitoring")
			monitoringGroup.Use(requirePermission("monitoring", "read"))
			{
				// Alerts
				monitoringGroup.GET("/alerts", monitoringHandler.ListAlerts)
				monitoringGroup.GET("/alerts/:id", monitoringHandler.GetAlert)
				monitoringGroup.POST("/alerts/:id/acknowledge", monitoringHandler.AcknowledgeAlert)
				monitoringGroup.POST("/alerts/:id/resolve", monitoringHandler.ResolveAlert)

				// Metrics
				monitoringGroup.GET("/metrics", monitoringHandler.GetMetrics)

				// WebSocket (no permission check needed, handled by auth middleware)
				monitoringGroup.GET("/events", monitoringHandler.WebSocketHandler)
			}
		}
	}

	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(),
		)
	}
}
