package httpserver

import (
	"log/slog"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"

	"update_server/internal/apikeys"
	authservice "update_server/internal/auth"
	"update_server/internal/config"
	"update_server/internal/db"
	"update_server/internal/releases"
)

type handler struct {
	config            config.Config
	store             *db.Store
	logger            *slog.Logger
	renderer          *Renderer
	auth              *authservice.Service
	apiKeys           *apikeys.Service
	releases          *releases.Service
	loginRateLimiter  *rateLimitStore
	clientRateLimiter *rateLimitStore
}

func NewRouter(cfg config.Config, logger *slog.Logger, renderer *Renderer, store *db.Store, auth *authservice.Service, apiKeyService *apikeys.Service, releaseService *releases.Service) http.Handler {
	h := &handler{
		config:            cfg,
		store:             store,
		logger:            logger,
		renderer:          renderer,
		auth:              auth,
		apiKeys:           apiKeyService,
		releases:          releaseService,
		loginRateLimiter:  newRateLimitStore(cfg.LoginRateLimitPerMinute, cfg.LoginRateLimitBurst),
		clientRateLimiter: newRateLimitStore(cfg.ClientRateLimitPerMinute, cfg.ClientRateLimitBurst),
	}

	router := chi.NewRouter()
	router.Use(middleware.RequestID)
	if cfg.TrustProxyHeaders {
		router.Use(middleware.RealIP)
	}
	router.Use(requestLogger(logger))
	router.Use(middleware.Recoverer)
	router.Use(middleware.StripSlashes)
	router.Use(securityHeaders(cfg))

	router.Get("/healthz", h.health)
	router.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir(cfg.StaticDir))))
	router.Get("/", h.home)

	router.Route("/admin", func(r chi.Router) {
		r.Use(adminResponseHeaders)
		r.Use(h.adminCSRF)

		r.Get("/login", h.adminLoginForm)
		r.With(h.loginRateLimit).Post("/login", h.adminLogin)

		r.Group(func(r chi.Router) {
			r.Use(h.requireAuthenticatedSession)
			r.Use(h.requireRole(db.UserRoleAdmin))

			r.Get("/", h.adminHome)
			r.Post("/logout", h.adminLogout)
			r.Get("/projects", h.adminProjects)
			r.Get("/projects/new", h.adminProjectNew)
			r.Post("/projects", h.adminProjectCreate)
			r.Get("/projects/{projectID}", h.adminProjectDetail)
			r.Post("/projects/{projectID}", h.adminProjectUpdate)
			r.Post("/projects/{projectID}/archive", h.adminProjectArchive)
			r.Post("/projects/{projectID}/tags", h.adminProjectAttachTag)
			r.Post("/projects/{projectID}/tags/{tagID}/detach", h.adminProjectDetachTag)
			r.Post("/projects/{projectID}/releases", h.adminProjectUploadRelease)
			r.Get("/tags", h.adminTags)
			r.Get("/tags/new", h.adminTagNew)
			r.Post("/tags", h.adminTagCreate)
			r.Get("/tags/{tagID}", h.adminTagDetail)
			r.Post("/tags/{tagID}", h.adminTagUpdate)
			r.Post("/tags/{tagID}/delete", h.adminTagDelete)
			r.Get("/api-keys", h.adminAPIKeys)
			r.Get("/api-keys/new", h.adminAPIKeyNew)
			r.Post("/api-keys", h.adminAPIKeyCreate)
			r.Get("/api-keys/{apiKeyID}", h.adminAPIKeyDetail)
			r.Post("/api-keys/{apiKeyID}", h.adminAPIKeyUpdate)
			r.Post("/api-keys/{apiKeyID}/activate", h.adminAPIKeyToggleActive)
		})
	})

	router.Route("/api", func(r chi.Router) {
		r.Route("/v1", func(r chi.Router) {
			r.Use(apiResponseHeaders)
			r.Get("/", h.apiIndex)

			r.Group(func(r chi.Router) {
				r.Use(protectedAPIResponseHeaders)
				r.Use(h.clientAPIRateLimit)
				r.Use(h.requireAPIKey)
				r.Use(h.requireAPIKeyPermission(apikeys.PermissionDownload))

				r.Get("/projects", h.apiAccessibleProjects)
				r.Get("/projects/{projectSlug}/releases/latest", h.apiLatestRelease)
				r.Get("/releases/{releaseID}", h.apiReleaseMetadata)
				r.Get("/releases/{releaseID}/download", h.apiReleaseDownload)
			})
		})
	})

	return router
}
