// ProxmoxPanel — CORE Backend
// Point d'entrée du serveur Go. Initialise la base de données, les services,
// enregistre les modules actifs et démarre le serveur HTTP sur :3001.
package main

import (
	"io"
	"log"
	"net/http"
	"os"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/api"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/audit"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/auth"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/crypto"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/db"
	dockerclient "git.geronzi.fr/proxmoxPanel/core/backend/internal/docker"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/logbuffer"
	sshpool "git.geronzi.fr/proxmoxPanel/core/backend/internal/ssh"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/websocket"
	"git.geronzi.fr/proxmoxPanel/core/backend/modules"

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

func main() {
	// Brancher le buffer de logs (stderr + mémoire) avant tout autre log
	log.SetOutput(io.MultiWriter(os.Stderr, logbuffer.Global))

	// ── Gestion du remplacement de container (rebuild module) ──────────────
	// Si REPLACING_CONTAINER est défini, ce container est un successeur issu d'un rebuild.
	// Il arrête l'ancien container et se renomme avant de continuer le démarrage normal.
	docker := dockerclient.New()
	if docker.Available() {
		docker.HandleReplacement()
	}

	// Répertoire de données persistantes (volume Docker)
	dataDir := getEnv("DATA_DIR", "/app/data")

	log.Printf("ProxmoxPanel CORE — démarrage (data: %s)", dataDir)

	// ── Initialisation de la base de données ───────────────────────────────
	database, err := db.Open(dataDir)
	if err != nil {
		log.Fatalf("Impossible d'ouvrir la base de données : %v", err)
	}
	log.Println("Base de données SQLite initialisée")

	// ── Services de base ───────────────────────────────────────────────────
	encryptor, err := crypto.NewEncryptor(dataDir)
	if err != nil {
		log.Fatalf("Impossible d'initialiser le chiffrement : %v", err)
	}
	log.Println("Chiffrement AES-256-GCM initialisé")

	jwtManager, err := auth.NewJWTManager(dataDir)
	if err != nil {
		log.Fatalf("Impossible d'initialiser JWT : %v", err)
	}
	log.Println("Clés JWT RS256 prêtes")

	// SSH host depuis la configuration (peut être vide si pas encore installé)
	sshHost, _, _ := database.GetSetting("ssh_host")
	var sshAuthenticator *auth.SSHAuthenticator
	if sshHost != "" {
		sshAuthenticator = auth.NewSSHAuthenticator(sshHost)
	} else {
		sshAuthenticator = auth.NewSSHAuthenticator("") // Sera mis à jour après installation
	}

	sshPool := sshpool.NewPool()
	defer sshPool.Close()

	hub := websocket.NewHub()
	auditLogger := audit.New(database.DB)

	// ── Chargement des modules actifs ──────────────────────────────────────
	loader := modules.NewLoader(database, sshPool, encryptor)
	RegisterModules(loader) // généré par cmd/gen-modules selon les modules compilés
	if err := loader.LoadActive(); err != nil {
		log.Fatalf("Erreur chargement modules : %v", err)
	}

	// ── Handlers HTTP ──────────────────────────────────────────────────────
	installHandler := api.NewInstallHandler(database, encryptor)
	authHandler := api.NewAuthHandler(database, jwtManager, sshAuthenticator, auditLogger)
	proxmoxHandler := api.NewProxmoxHandler(database, hub, auditLogger, encryptor)
	updatesHandler := api.NewUpdatesHandler(database, sshPool, hub, auditLogger, encryptor)
	settingsHandler := api.NewSettingsHandler(database, auditLogger, encryptor, docker)
	terminalHandler := api.NewTerminalHandler(database, auditLogger, encryptor)

	// Démarrer le polling Proxmox en arrière-plan
	proxmoxHandler.StartPolling()

	// ── Router Chi ─────────────────────────────────────────────────────────
	r := chi.NewRouter()

	// Middlewares globaux
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)
	r.Use(middleware.RequestID)
	r.Use(api.SecurityHeaders)
	r.Use(middleware.Compress(5)) // Compression gzip

	// Limiter global (100 req/min par IP)
	globalLimiter := api.NewRateLimiter(100, 60*1000000000) // 60 secondes
	r.Use(api.RateLimit(globalLimiter))

	// ── Routes publiques (sans authentification) ───────────────────────────
	r.Get("/api/health", func(w http.ResponseWriter, r *http.Request) {
		api.JSONResponse(w, http.StatusOK, map[string]string{"status": "ok"})
	})

	// Routes d'installation (accessibles seulement si non-installé)
	r.Group(func(r chi.Router) {
		r.Use(requireNotInstalled(database))
		r.Get("/api/install/status", installHandler.GetStatus)
		r.Post("/api/install/test-ssh", installHandler.TestSSH)
		r.Post("/api/install/test-proxmox", installHandler.TestProxmoxToken)
		r.Post("/api/install/configure", installHandler.Configure)
	})

	// Status d'installation accessible toujours (pour la redirection frontend)
	r.Get("/api/install/check", func(w http.ResponseWriter, r *http.Request) {
		installed, _ := database.IsInstalled()
		api.JSONResponse(w, http.StatusOK, map[string]bool{"installed": installed})
	})

	// Routes d'authentification
	r.Post("/api/auth/login", authHandler.Login)
	r.Post("/api/auth/refresh", authHandler.Refresh)

	// ── Routes protégées (JWT requis) ──────────────────────────────────────
	r.Group(func(r chi.Router) {
		r.Use(api.RequireAuth(jwtManager))

		r.Post("/api/auth/logout", authHandler.Logout)
		r.Get("/api/auth/me", authHandler.Me)
		r.Patch("/api/auth/preferences", authHandler.UpdatePreferences)
		r.Get("/api/auth/sessions", authHandler.GetSessions)
		r.Delete("/api/auth/sessions/{id}", authHandler.RevokeSession)

		// Proxmox
		r.Get("/api/proxmox/resources", proxmoxHandler.GetResources)
		r.Get("/api/proxmox/lxc", proxmoxHandler.GetLXC)

		// Actions Proxmox — admin uniquement
		r.Group(func(r chi.Router) {
			r.Use(api.RequireAdmin)
			r.Post("/api/proxmox/lxc/{vmid}/start", proxmoxHandler.StartLXC)
			r.Post("/api/proxmox/lxc/{vmid}/stop", proxmoxHandler.StopLXC)
		})

		// Mises à jour — admin uniquement
		r.Group(func(r chi.Router) {
			r.Use(api.RequireAdmin)
			r.Post("/api/updates/run", updatesHandler.RunUpdate)
			r.Get("/api/updates/targets", updatesHandler.GetTargets)
			r.Get("/api/updates/packages", updatesHandler.GetPackages)
		})
		r.Get("/api/updates/history", updatesHandler.GetHistory)

		// Paramètres — admin uniquement
		r.Group(func(r chi.Router) {
			r.Use(api.RequireAdmin)
			r.Get("/api/settings", settingsHandler.GetAll)
			r.Put("/api/settings/{key}", settingsHandler.UpdateSetting)
			r.Get("/api/settings/audit", settingsHandler.GetAuditLog)
			r.Get("/api/settings/logs", settingsHandler.GetLogs)
		})

		// Modules
		r.Get("/api/modules", settingsHandler.GetModules)
		r.Group(func(r chi.Router) {
			r.Use(api.RequireAdmin)
			r.Post("/api/modules/{id}/enable", settingsHandler.EnableModule)
			r.Post("/api/modules/{id}/disable", settingsHandler.DisableModule)
		})

		// Registry store — admin uniquement
		r.Group(func(r chi.Router) {
			r.Use(api.RequireAdmin)
			r.Get("/api/registry/modules", settingsHandler.GetRegistryModules)
			r.Post("/api/registry/modules/{id}/install", settingsHandler.InstallRegistryModule)
		})

		// Réparation DB — admin uniquement
		r.Group(func(r chi.Router) {
			r.Use(api.RequireAdmin)
			r.Get("/api/repair/modules", settingsHandler.GetRepairStatus)
			r.Delete("/api/repair/modules/{id}", settingsHandler.ResetModule)
		})

		// WebSocket — les routes WS extraient le token via query param
		r.Get("/ws/proxmox", proxmoxHandler.WebSocket)
		r.Get("/ws/updates/{jobId}", updatesHandler.WebSocketUpdate)
		r.Get("/ws/terminal", terminalHandler.WebSocket)
	})

	// Routes enregistrées par les modules actifs
	for _, route := range loader.Registry().GetRoutes() {
		routeCopy := route // Capturer la variable pour la closure
		switch {
		case routeCopy.Public:
			r.MethodFunc(routeCopy.Method, routeCopy.Path, routeCopy.Handler)
		case routeCopy.RequireAdmin:
			r.With(api.RequireAuth(jwtManager), api.RequireAdmin).MethodFunc(routeCopy.Method, routeCopy.Path, routeCopy.Handler)
		default:
			r.With(api.RequireAuth(jwtManager)).MethodFunc(routeCopy.Method, routeCopy.Path, routeCopy.Handler)
		}
	}

	// Servir les assets frontend (en production, c'est Nginx qui s'en charge)
	if _, err := os.Stat("./static"); err == nil {
		fs := http.FileServer(http.Dir("./static"))
		r.Handle("/*", fs)
	}

	// ── Démarrage du serveur ───────────────────────────────────────────────
	addr := getEnv("LISTEN_ADDR", ":3001")
	log.Printf("Serveur démarré sur %s", addr)
	if err := http.ListenAndServe(addr, r); err != nil {
		log.Fatalf("Serveur arrêté : %v", err)
	}
}

// requireNotInstalled est un middleware qui bloque les routes d'installation si déjà installé.
func requireNotInstalled(database *db.DB) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// La route /api/install/status reste accessible pour le check
			installed, _ := database.IsInstalled()
			if installed {
				api.JSONError(w, "Application déjà installée", http.StatusForbidden)
				return
			}
			next.ServeHTTP(w, r)
		})
	}
}

// getEnv lit une variable d'environnement avec une valeur par défaut.
func getEnv(key, defaultValue string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return defaultValue
}
