package main

import (
	"context"
	"log/slog"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"gitea.mrixs.me/minecraft-platform/backend/internal/api"
	"gitea.mrixs.me/minecraft-platform/backend/internal/core"
	"gitea.mrixs.me/minecraft-platform/backend/internal/database"
	"gitea.mrixs.me/minecraft-platform/backend/internal/ws"

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

func main() {
	// --- Инициализация логгера (slog) ---
	logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
	slog.SetDefault(logger)

	slog.Info("Starting backend server initialization...")

	dbPool := database.Connect()
	defer dbPool.Close()

	// --- Инициализация репозиториев ---
	userRepo := &database.UserRepository{DB: dbPool}
	serverRepo := &database.ServerRepository{DB: dbPool}
	modpackRepo := &database.ModpackRepository{DB: dbPool}
	jobRepo := &database.JobRepository{DB: dbPool}

	// --- Инициализация сервисов ---
	userService := &core.UserService{Repo: userRepo}
	authService := &core.AuthService{UserRepo: userRepo}
	serverPoller := &core.ServerPoller{Repo: serverRepo}

	// --- Инициализация WebSocket Hub ---
	hub := ws.NewHub()
	go hub.Run()

	keyPath := os.Getenv("RSA_PRIVATE_KEY_PATH")
	if keyPath == "" {
		slog.Error("RSA_PRIVATE_KEY_PATH environment variable is not set")
		os.Exit(1)
	}
	domain := os.Getenv("APP_DOMAIN")
	if domain == "" {
		slog.Error("APP_DOMAIN environment variable is not set")
		os.Exit(1)
	}
	profileService, err := core.NewProfileService(userRepo, keyPath, domain)
	if err != nil {
		slog.Error("Failed to create profile service", "error", err)
		os.Exit(1)
	}

	modpacksStoragePath := os.Getenv("MODPACKS_STORAGE_PATH")
	if modpacksStoragePath == "" {
		slog.Error("MODPACKS_STORAGE_PATH environment variable is not set")
		os.Exit(1)
	}
	janitorService := core.NewFileJanitorService(modpackRepo, modpacksStoragePath)

	// --- Запуск фоновых задач ---
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go serverPoller.Start(ctx) // Передаем контекст для отмены

	// --- Инициализация хендлеров ---
	userHandler := &api.UserHandler{Service: userService}
	authHandler := &api.AuthHandler{Service: authService, ProfileService: profileService, Domain: domain}
	profileHandler := &api.ProfileHandler{Service: profileService}
	serverHandler := &api.ServerHandler{Repo: serverRepo}
	launcherHandler := &api.LauncherHandler{ModpackRepo: modpackRepo}
	modpackHandler := &api.ModpackHandler{
		ModpackRepo:    modpackRepo,
		JobRepo:        jobRepo,
		JanitorService: janitorService,
		Hub:            hub,
	}
	adminUserHandler := &api.AdminUserHandler{UserRepo: userRepo}

	// --- Настройка роутера ---
	r := chi.NewRouter()
	r.Use(middleware.Logger) // Можно заменить на slog middleware, но пока оставим standard
	r.Use(middleware.Recoverer)

	// Health Check
	r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK"))
	})

	// --- Публичные роуты ---
	r.Route("/api", func(r chi.Router) {
		// Rate limiting: 100 requests per minute for general API
		r.Use(httprate.LimitByIP(100, time.Minute))

		// Auth endpoints: stricter limit (10 requests per minute)
		r.Group(func(r chi.Router) {
			r.Use(httprate.LimitByIP(10, time.Minute))
			r.Post("/register", userHandler.Register)
			r.Post("/login", authHandler.Login)
		})

		r.Get("/servers", serverHandler.GetServers)

		r.Route("/launcher", func(r chi.Router) {
			r.Get("/modpacks/{name}/manifest", launcherHandler.GetModpackManifest)
			r.Get("/modpacks/summary", launcherHandler.GetModpacksSummary)
		})
	})
	r.Route("/authserver", func(r chi.Router) {
		// Stricter rate limit for auth server (10 req/min)
		r.Use(httprate.LimitByIP(10, time.Minute))
		r.Post("/authenticate", authHandler.Authenticate)
	})
	r.Route("/sessionserver/session/minecraft", func(r chi.Router) {
		// Rate limit for session endpoints (60 req/min)
		r.Use(httprate.LimitByIP(60, time.Minute))
		r.Post("/join", authHandler.Join)
		r.Get("/profile/{uuid}", profileHandler.GetProfile)
	})

	// --- Yggdrasil API (for authlib-injector) ---
	r.Route("/api/yggdrasil", func(r chi.Router) {
		r.Get("/", authHandler.GetMetadata)

		r.Route("/authserver", func(r chi.Router) {
			r.Post("/authenticate", authHandler.Authenticate)
			r.Post("/invalidate", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) })
			r.Post("/validate", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) })
			r.Post("/signout", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) })
			r.Post("/refresh", func(w http.ResponseWriter, r *http.Request) {
				// Fallback for refresh - return 400 or implement real refresh
				http.Error(w, "Not implemented", http.StatusForbidden)
			})
		})

		r.Route("/sessionserver/session/minecraft", func(r chi.Router) {
			r.Post("/join", authHandler.Join)
			r.Get("/hasJoined", func(w http.ResponseWriter, r *http.Request) {
				// Legacy hasJoined endpoint if needed, but modern mostly uses join/check
				w.WriteHeader(http.StatusNoContent)
			})
			r.Get("/profile/{uuid}", profileHandler.GetProfile)
		})
	})

	// --- Защищенные роуты ---
	r.Group(func(r chi.Router) {
		r.Use(api.AuthMiddleware)

		r.Route("/api/user", func(r chi.Router) {
			r.Post("/skin", profileHandler.UploadSkin)
			r.Get("/me", userHandler.GetMe)
		})

		r.Route("/api/admin", func(r chi.Router) {
			r.Use(api.AdminMiddleware)

			// WebSocket endpoint for jobs
			r.Get("/ws/jobs", func(w http.ResponseWriter, r *http.Request) {
				ws.ServeWs(hub, w, r)
			})

			r.Route("/modpacks", func(r chi.Router) {
				r.Get("/", modpackHandler.GetModpacks)
				r.Post("/import", modpackHandler.ImportModpack)
				r.Post("/update", modpackHandler.UpdateModpack)
				r.Get("/versions", modpackHandler.GetModpackVersions)
			})

			r.Route("/users", func(r chi.Router) {
				r.Get("/", adminUserHandler.GetAllUsers)
				r.Patch("/{id}/role", adminUserHandler.UpdateUserRole)
			})
		})
	})

	// --- Graceful Shutdown ---
	srv := &http.Server{
		Addr:    ":8080",
		Handler: r,
	}

	go func() {
		slog.Info("Starting backend server on :8080")
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			slog.Error("Failed to start server", "error", err)
			os.Exit(1)
		}
	}()

	// Wait for interrupt signal to gracefully shutdown the server with a timeout.
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	slog.Info("Shutting down server...")

	// The context is used to inform the server it has 5 seconds to finish
	// the request it is currently handling
	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer shutdownCancel()

	if err := srv.Shutdown(shutdownCtx); err != nil {
		slog.Error("Server forced to shutdown", "error", err)
	}

	slog.Info("Server exiting")
}
