package routes

import (
	"github.com/MishraShardendu22/github-backup/src/backend/github-backup-api/config"
	"github.com/MishraShardendu22/github-backup/src/backend/github-backup-api/handlers"
	"github.com/MishraShardendu22/github-backup/src/backend/github-backup-api/livefeed"
	"github.com/MishraShardendu22/github-backup/src/backend/github-backup-api/middleware"
	"github.com/MishraShardendu22/github-backup/src/backend/github-backup-api/websocket"
	"github.com/gofiber/fiber/v2"
	ws "github.com/gofiber/websocket/v2"
)

// Setup registers every route. feed serves the live log streams.
//
// /health, /healthz, /ready and /readyz are always public. When
// cfg.APIRequireAuth is set, /api/*, the Prometheus /metrics endpoint and
// /ws/live require INTERNAL_SECRET (X-Internal-Secret or Authorization:
// Bearer); otherwise they stay open, as before. Requests that carry the
// secret are recognised either way (see middleware.IdentifyInternalCaller).
func Setup(app *fiber.App, cfg *config.Config, feed *livefeed.Feed) {
	app.Use(middleware.IdentifyInternalCaller(cfg.InternalSecret))

	protect := func(c *fiber.Ctx) error { return c.Next() }
	if cfg.APIRequireAuth {
		protect = middleware.RequireInternalCaller()
	}

	api := app.Group("/api")
	api.Use(middleware.RateLimit(middleware.RateLimits{
		Max:             config.RateLimitMax,
		ForwardedMax:    config.RateLimitForwardedMax,
		ForwardedHeader: cfg.TrustProxyHeader,
		Window:          config.RateLimitWindow,
	}))
	api.Use(protect)

	// backup handler
	api.Get("/backups", handlers.GetBackupRuns)
	api.Get("/backups/latest", handlers.GetLatestBackup)
	api.Get("/backups/:id", handlers.GetBackupRun)

	// dashboard handler
	api.Get("/dashboard/stats", handlers.GetDashboardStats)

	// metrics handler
	api.Get("/metrics", handlers.GetMetrics)
	api.Get("/logs", handlers.GetLogs)
	api.Get("/logs/stream", handlers.StreamLogs(feed, handlers.LogStreamOptions{
		PingInterval:     config.SSEPingInterval,
		MaxDuration:      config.SSEMaxDuration,
		RetryMillis:      config.SSERetryMillis,
		BacklogLimit:     config.SSEBacklogLimit,
		WriteWait:        config.SSEWriteWait,
		SubscribeTimeout: config.SSESubscribeTimeout,
	}))

	// repos handler
	api.Get("/repos", handlers.GetRepos)

	// fix handler
	api.Get("/backup-fixes", handlers.GetBackupFixes)
	api.Get("/backup-fixes/:id", handlers.GetBackupFix)
	api.Get("/backup-runs/:id/fixes", handlers.GetBackupRunFixes)

	// analytics handler
	api.Get("/analytics/history", handlers.GetAnalyticsRuns)
	api.Get("/analytics/latest", handlers.GetAnalyticsForLatestRun)
	api.Get("/analytics/:id", handlers.GetAnalyticsForSpecificRun)
	api.Get("/analytics/run/:id", handlers.GetAnalyticsForSpecificRun)

	// Health (public) and Prometheus metrics
	app.Get("/health", handlers.LivenessCheck)
	app.Get("/healthz", handlers.LivenessCheck)
	app.Get("/ready", handlers.ReadinessCheck)
	app.Get("/readyz", handlers.ReadinessCheck)
	app.Get("/metrics", protect, handlers.GetSystemMetrics)

	// checks the /ws routes whether the incoming request is asking for a WebSocket upgrade.
	app.Use("/ws", protect, websocket.CheckOrigin(cfg.WSAllowedOrigins), func(c *fiber.Ctx) error {
		//if yes, it's asking for upgrade then go to the next function
		if ws.IsWebSocketUpgrade(c) {
			return c.Next()
		}
		return fiber.ErrUpgradeRequired
	})

	app.Get("/ws/live", ws.New(websocket.Handler(feed, websocket.Options{
		PingInterval:     config.WSPingInterval,
		PongWait:         config.WSPongWait,
		WriteWait:        config.WSWriteWait,
		SubscribeTimeout: config.WSSubscribeTimeout,
	})))
}

/*
	app.Use("/ws", ...) matches paths that start with /ws

	Match
	- /ws
	- /ws/
	- /ws/live
	- /ws/chat

	No Match
	- /abc/ws/live
	- /api/ws/live
	- /foo/bar/ws

*/
