package api

import (
	"net/http"

	"github.com/TopherMayor/unified-media-manager/internal/config"
	"github.com/TopherMayor/unified-media-manager/internal/db"
	"github.com/TopherMayor/unified-media-manager/internal/service"
	"github.com/TopherMayor/unified-media-manager/internal/worker"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

type Services struct {
	DB             *db.DB
	Media          *service.MediaService
	Queue          *service.QueueService
	Indexer        *service.IndexerService
	Blocklist      *service.BlocklistService
	Dashboard      *service.DashboardService
	Quality        *service.QualityService
	DownloadClient *service.DownloadClientService
	Search         *service.SearchService
	Import         *service.ImportService
	Metadata       *service.MetadataService
	Subtitle       *service.SubtitleService
	RootFolder     *service.RootFolderService
	Tag            *service.TagService
	Scheduler      *worker.Scheduler
	User           *service.UserService
	Activity       *service.ActivityService
	Safety         *service.SafetyService
	Request        *service.RequestService
	Notification   *service.NotificationService
	Discover       *service.DiscoverService
	MediaDetail    *service.MediaDetailService
	Calendar       *service.CalendarService
}

func NewRouter(cfg *config.Config, svc *Services) *echo.Echo {
	e := echo.New()
	e.HideBanner = true

	e.Use(middleware.Logger())
	e.Use(middleware.Recover())
	e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
		AllowOrigins: []string{cfg.FrontendURL},
		AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
	}))
	e.Use(cacheControlMiddleware())

	e.GET("/health/live", healthLive)
	e.GET("/health/ready", healthReady(svc.DB, cfg))

	g := e.Group("/api")

	g.GET("/media", listMedia(svc.Media))
	g.GET("/media/:type/:id", getMedia(svc.Media))
	g.GET("/media/:type/:id/detail", getFullMediaDetail(svc.MediaDetail))
	g.POST("/media", createMedia(svc.Media))
	g.PUT("/media/:type/:id", updateMedia(svc.Media))
	g.DELETE("/media/:type/:id", deleteMedia(svc.Media))

	g.GET("/search", searchMedia(svc.Media))
	g.GET("/search/missing", searchMissing(svc.Media))
	g.GET("/search/upgrades", searchUpgrades(svc.Media))

	g.GET("/queue", listQueue(svc.Queue))
	g.DELETE("/queue/:id", deleteQueueItem(svc.Queue))
	g.DELETE("/queue/batch", batchDeleteQueue(svc.Queue))
	g.POST("/queue/clear", clearQueue(svc.Queue))
	g.POST("/queue/:id/retry", retryQueueItem(svc.Queue))
	g.POST("/queue/retry-failed", retryFailedQueue(svc.Queue))

	g.GET("/blocklist", listBlocklist(svc.Blocklist))
	g.DELETE("/blocklist/:id", deleteBlocklistItem(svc.Blocklist))
	g.DELETE("/blocklist", clearBlocklist(svc.Blocklist))
	g.DELETE("/blocklist/expired", clearExpiredBlocklist(svc.Blocklist))
	g.POST("/blocklist", addBlocklistItem(svc.Blocklist))

	g.GET("/indexers", listIndexers(svc.Indexer))
	g.POST("/indexers", createIndexer(svc.Indexer))
	g.POST("/indexers/validate-cardigann", validateCardigannDefinition())
	g.PUT("/indexers/:id", updateIndexer(svc.Indexer))
	g.DELETE("/indexers/:id", deleteIndexer(svc.Indexer))
	g.POST("/indexers/:id/test", testIndexer(svc.Indexer))
	g.GET("/indexers/:id/stats", indexerStats(svc.Indexer))

	g.GET("/dashboard", dashboard(svc.Dashboard))

	g.GET("/activity", listActivity(svc.Activity))

	g.GET("/quality-profiles", listQualityProfiles(svc.Quality))
	g.POST("/quality-profiles", createQualityProfile(svc.Quality))
	g.PUT("/quality-profiles/:id", updateQualityProfile(svc.Quality))
	g.DELETE("/quality-profiles/:id", deleteQualityProfile(svc.Quality))

	g.GET("/download-clients", listDownloadClients(svc.DownloadClient))
	g.POST("/download-clients", createDownloadClient(svc.DownloadClient))
	g.PUT("/download-clients/:id", updateDownloadClient(svc.DownloadClient))
	g.DELETE("/download-clients/:id", deleteDownloadClient(svc.DownloadClient))
	g.POST("/download-clients/:id/test", testDownloadClient(svc.DownloadClient))

	g.GET("/releases/search", searchReleases(svc.Search))
	g.POST("/releases/grab", grabRelease(svc.Search, svc.DownloadClient, svc.Queue, svc.Safety, svc.Activity))

	g.POST("/imports/trigger", triggerImport(svc.Import))
	g.GET("/imports/history", listImportHistory(svc.Import, svc.DB))

	g.POST("/media/:type/:id/refresh-metadata", refreshMetadata(svc.Metadata))
	g.POST("/media/refresh-all", refreshAllMetadata(svc.Metadata))
	g.GET("/images/:type/:filename", serveImage(cfg.ImageDir))

	g.GET("/media/:type/:id/subtitles/search", searchSubtitles(svc.Subtitle, svc.Media))
	g.POST("/media/:type/:id/subtitles/download", downloadSubtitle(svc.Subtitle, svc.Media))
	g.POST("/media/:type/:id/subtitles/extract", extractSubtitles(svc.Subtitle, svc.Media))

	g.GET("/root-folders", listRootFolders(svc.RootFolder))
	g.POST("/root-folders", createRootFolder(svc.RootFolder))
	g.DELETE("/root-folders/:id", deleteRootFolder(svc.RootFolder))

	g.GET("/tags", listTags(svc.Tag))
	g.POST("/tags", createTag(svc.Tag))
	g.DELETE("/tags/:id", deleteTag(svc.Tag))

	if svc.Scheduler != nil {
		g.GET("/workers", listWorkers(svc.Scheduler))
		g.GET("/workers/:name/history", workerHistory(svc.Scheduler))
		g.PUT("/workers/:name", updateWorker(svc.Scheduler))
		g.POST("/workers/:name/trigger", triggerWorker(svc.Scheduler))
	}

	// Notification routes
	g.GET("/notifications/channels", listNotificationChannels(svc.Notification))
	g.POST("/notifications/channels", createNotificationChannel(svc.Notification))
	g.PUT("/notifications/channels/:id", updateNotificationChannel(svc.Notification))
	g.DELETE("/notifications/channels/:id", deleteNotificationChannel(svc.Notification))
	g.POST("/notifications/channels/:id/test", testNotificationChannel(svc.Notification))
	g.GET("/notifications/queue", listNotificationQueue(svc.Notification))

	// Discover routes
	if svc.Discover != nil {
		g.GET("/discover/trending", listTrending(svc.Discover))
		g.GET("/discover/popular", listPopular(svc.Discover))
		g.POST("/discover/add", addFromDiscover(svc.Discover))
	}

	// Calendar route
	g.GET("/calendar", listCalendarEvents(svc.Calendar))

	// Request routes — protected by API key auth
	apiKeyAuth := newAPIKeyAuth(svc.User)
	g.GET("/requests", listRequests(svc.Request, svc.User), apiKeyAuth)
	g.POST("/requests", createRequest(svc.Request, svc.User), apiKeyAuth)
	g.GET("/requests/stats", requestStats(svc.Request, svc.User), apiKeyAuth)
	g.PUT("/requests/:id/approve", approveRequest(svc.Request, svc.User), apiKeyAuth)
	g.PUT("/requests/:id/reject", rejectRequest(svc.Request, svc.User), apiKeyAuth)
	g.DELETE("/requests/:id", withdrawRequest(svc.Request, svc.User), apiKeyAuth)

	return e
}

type paginatedResponse struct {
	Data       interface{} `json:"data"`
	Total      int         `json:"total"`
	Page       int         `json:"page"`
	PageSize   int         `json:"page_size"`
	TotalPages int         `json:"total_pages"`
}

func newAPIKeyAuth(userSvc *service.UserService) echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			key := c.Request().Header.Get("X-API-Key")
			if key == "" {
				key = c.QueryParam("api_key")
			}
			if key == "" {
				return c.JSON(http.StatusUnauthorized, map[string]string{"error": "API key required"})
			}
			user, err := userSvc.GetUserByAPIKey(c.Request().Context(), key)
			if err != nil {
				return c.JSON(http.StatusUnauthorized, map[string]string{"error": "invalid API key"})
			}
			c.Set("user", user)
			return next(c)
		}
	}
}

func cacheControlMiddleware() echo.MiddlewareFunc {
	shortCache := map[string]bool{
		"/api/quality-profiles": true,
		"/api/download-clients": true,
		"/api/root-folders":     true,
		"/api/tags":             true,
		"/api/indexers":         true,
		"/api/workers":          true,
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			err := next(c)
			if err != nil {
				return err
			}
			path := c.Request().URL.Path
			if c.Request().Method == http.MethodGet {
				if shortCache[path] {
					c.Response().Header().Set("Cache-Control", "max-age=60")
				} else if path == "/api/dashboard" {
					c.Response().Header().Set("Cache-Control", "max-age=30")
				} else if path == "/api/calendar" || path == "/api/activity" {
					c.Response().Header().Set("Cache-Control", "max-age=15")
				}
			}
			return nil
		}
	}
}
