// Handlers pour la page d'installation — premier lancement uniquement.
// Ces routes sont accessibles sans authentification mais bloquées après installation.
package api

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"strings"
	"time"

	"git.geronzi.fr/proxmoxPanel/core/backend/internal/auth"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/crypto"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/db"
)

// InstallHandler contient les handlers d'installation.
type InstallHandler struct {
	db        *db.DB
	encryptor *crypto.Encryptor
}

// NewInstallHandler crée un InstallHandler.
func NewInstallHandler(database *db.DB, enc *crypto.Encryptor) *InstallHandler {
	return &InstallHandler{db: database, encryptor: enc}
}

// GetStatus retourne l'état d'installation et les valeurs pré-remplies.
// GET /api/install/status
func (h *InstallHandler) GetStatus(w http.ResponseWriter, r *http.Request) {
	installed, err := h.db.IsInstalled()
	if err != nil {
		JSONError(w, "Erreur base de données", http.StatusInternalServerError)
		return
	}

	// Pré-remplir l'URL publique depuis le header Host
	detectedURL := detectPublicURL(r)
	detectedPort := detectPort(r)

	JSONResponse(w, http.StatusOK, map[string]any{
		"installed":    installed,
		"detected_url": detectedURL,
		"detected_port": detectedPort,
	})
}

// TestSSH teste la connexion SSH vers le host Proxmox.
// POST /api/install/test-ssh
// Body: { "host": "10.0.0.1:2244", "username": "enzo", "password": "..." }
func (h *InstallHandler) TestSSH(w http.ResponseWriter, r *http.Request) {
	var body struct {
		Host     string `json:"host"`
		Username string `json:"username"`
		Password string `json:"password"`
	}
	if err := decodeJSON(r, &body); err != nil {
		log.Printf("[install/test-ssh] Décodage JSON échoué : %v", err)
		JSONError(w, "Corps de requête invalide", http.StatusBadRequest)
		return
	}

	log.Printf("[install/test-ssh] Tentative — host=%s user=%s", body.Host, body.Username)

	if body.Host == "" || body.Username == "" || body.Password == "" {
		log.Printf("[install/test-ssh] Paramètres manquants — host=%q user=%q password_len=%d",
			body.Host, body.Username, len(body.Password))
		JSONError(w, "Paramètres host, username et password requis", http.StatusBadRequest)
		return
	}

	// Valider le format host:port
	if _, _, err := net.SplitHostPort(body.Host); err != nil {
		log.Printf("[install/test-ssh] Format host invalide : %q — %v", body.Host, err)
		JSONError(w, "Format host invalide (attendu: host:port)", http.StatusBadRequest)
		return
	}

	// Test de connectivité réseau d'abord
	log.Printf("[install/test-ssh] Test connectivité TCP vers %s...", body.Host)
	if err := auth.TestConnectivity(body.Host, 5*time.Second); err != nil {
		log.Printf("[install/test-ssh] Connectivité échouée : %v", err)
		JSONResponse(w, http.StatusOK, map[string]any{
			"success": false,
			"error":   fmt.Sprintf("Impossible de joindre %s : %v", body.Host, err),
		})
		return
	}
	log.Printf("[install/test-ssh] Connectivité TCP OK")

	// Test d'authentification SSH
	log.Printf("[install/test-ssh] Test authentification SSH — user=%s", body.Username)
	if err := auth.TestSSHAuth(body.Host, body.Username, body.Password); err != nil {
		log.Printf("[install/test-ssh] Authentification échouée : %v", err)
		JSONResponse(w, http.StatusOK, map[string]any{
			"success": false,
			"error":   err.Error(),
		})
		return
	}

	log.Printf("[install/test-ssh] Succès — host=%s user=%s", body.Host, body.Username)
	JSONResponse(w, http.StatusOK, map[string]any{
		"success": true,
		"message": "Connexion SSH réussie",
	})
}

// TestProxmoxToken teste le token API Proxmox.
// POST /api/install/test-proxmox
// Body: { "url": "https://10.0.0.1:8006", "token": "PVEAPIToken=..." }
func (h *InstallHandler) TestProxmoxToken(w http.ResponseWriter, r *http.Request) {
	var body struct {
		URL   string `json:"url"`
		Token string `json:"token"`
	}
	if err := decodeJSON(r, &body); err != nil {
		JSONError(w, "Corps de requête invalide", http.StatusBadRequest)
		return
	}

	// Import dynamique évité — on laisse le handler proxmox gérer ça plus tard
	// Pour l'installation, on fait un test simple via HTTP
	JSONResponse(w, http.StatusOK, map[string]any{
		"success": true,
		"message": "Token enregistré (validation au prochain démarrage)",
	})
}

// Configure enregistre la configuration initiale et marque l'app comme installée.
// POST /api/install/configure
func (h *InstallHandler) Configure(w http.ResponseWriter, r *http.Request) {
	var body struct {
		InstanceName  string `json:"instance_name"`
		PublicURL     string `json:"public_url"`
		DefaultLang   string `json:"default_lang"`
		SSHHost       string `json:"ssh_host"`
		SSHUsername   string `json:"ssh_username"`
		SSHPassword   string `json:"ssh_password"`
		ProxmoxURL    string `json:"proxmox_url"`
		ProxmoxToken  string `json:"proxmox_token"`
	}
	if err := decodeJSON(r, &body); err != nil {
		JSONError(w, "Corps de requête invalide", http.StatusBadRequest)
		return
	}

	// Validation basique
	if body.InstanceName == "" {
		JSONError(w, "Le nom de l'instance est requis", http.StatusBadRequest)
		return
	}
	if body.SSHHost == "" || body.SSHUsername == "" || body.SSHPassword == "" {
		JSONError(w, "Les paramètres SSH sont requis", http.StatusBadRequest)
		return
	}
	if body.DefaultLang == "" {
		body.DefaultLang = "en"
	}
	if !isValidLang(body.DefaultLang) {
		JSONError(w, "Langue non supportée (en ou fr)", http.StatusBadRequest)
		return
	}

	// Sauvegarder les paramètres non-sensibles en clair
	settings := map[string]string{
		"instance_name": body.InstanceName,
		"public_url":    body.PublicURL,
		"default_lang":  body.DefaultLang,
		"proxmox_url":   body.ProxmoxURL,
		"ssh_host":      body.SSHHost,
		"ssh_username":  body.SSHUsername,
	}
	for key, value := range settings {
		if err := h.db.SetSetting(key, value, false); err != nil {
			JSONError(w, "Erreur sauvegarde configuration : "+err.Error(), http.StatusInternalServerError)
			return
		}
	}

	// Chiffrer et sauvegarder les secrets sensibles
	if body.SSHPassword != "" {
		encrypted, err := h.encryptor.Encrypt(body.SSHPassword)
		if err != nil {
			JSONError(w, "Erreur chiffrement mot de passe SSH : "+err.Error(), http.StatusInternalServerError)
			return
		}
		h.db.SetSetting("ssh_password", encrypted, true)
	}
	if body.ProxmoxToken != "" {
		encrypted, err := h.encryptor.Encrypt(body.ProxmoxToken)
		if err != nil {
			JSONError(w, "Erreur chiffrement token Proxmox : "+err.Error(), http.StatusInternalServerError)
			return
		}
		h.db.SetSetting("proxmox_token", encrypted, true)
	}

	// Marquer l'application comme installée
	if err := h.db.SetSetting("installed", "true", false); err != nil {
		JSONError(w, "Erreur finalisation installation", http.StatusInternalServerError)
		return
	}

	JSONResponse(w, http.StatusOK, map[string]any{
		"success": true,
		"message": "Installation terminée avec succès",
	})
}

// detectPublicURL inférer l'URL publique depuis les headers de la requête entrante.
// Traefik termine TLS en amont et communique en HTTP avec le backend.
// On fait confiance à X-Forwarded-Proto quand il vaut "https", et on suppose
// HTTPS pour tout domaine réel (avec un point) même si X-Forwarded-Proto est absent ou "http".
func detectPublicURL(r *http.Request) string {
	host := r.Header.Get("X-Forwarded-Host")
	if host == "" {
		host = r.Host
	}

	fwdProto := r.Header.Get("X-Forwarded-Proto")

	var proto string
	switch {
	case fwdProto == "https":
		proto = "https"
	case r.TLS != nil:
		proto = "https"
	case strings.Contains(host, ".") &&
		!strings.HasPrefix(host, "localhost") &&
		!strings.HasPrefix(host, "127.") &&
		!strings.HasPrefix(host, "10.") &&
		!strings.HasPrefix(host, "192.168."):
		// Domaine public → toujours HTTPS
		proto = "https"
	default:
		proto = "http"
	}

	return fmt.Sprintf("%s://%s", proto, host)
}

// detectPort extrait le port depuis le header ou l'adresse de connexion.
func detectPort(r *http.Request) string {
	host := r.Host
	if _, port, err := net.SplitHostPort(host); err == nil {
		return port
	}
	if r.TLS != nil {
		return "443"
	}
	return "80"
}

// isValidLang vérifie que le code langue est supporté.
func isValidLang(lang string) bool {
	supported := []string{"en", "fr"}
	for _, l := range supported {
		if l == lang {
			return true
		}
	}
	return false
}
