// Handlers pour l'API Proxmox : liste LXC/VM, démarrage/arrêt, WebSocket temps réel.
package api

import (
	"log"
	"net/http"
	"strconv"
	"time"

	"git.geronzi.fr/proxmoxPanel/core/backend/internal/audit"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/crypto"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/db"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/proxmox"
	"git.geronzi.fr/proxmoxPanel/core/backend/internal/websocket"
	gorillaws "github.com/gorilla/websocket"

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

var upgrader = gorillaws.Upgrader{
	CheckOrigin: func(r *http.Request) bool { return true },
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

// ProxmoxHandler contient les handlers Proxmox.
type ProxmoxHandler struct {
	db          *db.DB
	hub         *websocket.Hub
	auditLogger *audit.Logger
	encryptor   *crypto.Encryptor
	client      *proxmox.Client // Peut être nil si pas encore configuré
}

// NewProxmoxHandler crée un ProxmoxHandler.
func NewProxmoxHandler(database *db.DB, hub *websocket.Hub, auditLog *audit.Logger, enc *crypto.Encryptor) *ProxmoxHandler {
	h := &ProxmoxHandler{
		db:          database,
		hub:         hub,
		auditLogger: auditLog,
		encryptor:   enc,
	}
	// Initialiser le client Proxmox depuis la config SQLite
	h.initClient()
	return h
}

// initClient recharge le client Proxmox depuis les settings SQLite.
func (h *ProxmoxHandler) initClient() {
	url, _, _ := h.db.GetSetting("proxmox_url")
	encryptedToken, _, _ := h.db.GetSetting("proxmox_token")
	if url == "" || encryptedToken == "" {
		log.Printf("[proxmox/init] Client non initialisé — proxmox_url=%q token_empty=%v", url, encryptedToken == "")
		return
	}
	token, err := h.encryptor.Decrypt(encryptedToken)
	if err != nil {
		log.Printf("[proxmox/init] Impossible de déchiffrer le token : %v", err)
		return
	}
	h.client = proxmox.NewClient(url, token)
}

// GetResources retourne la liste de toutes les ressources Proxmox (LXC + VM + nodes).
// GET /api/proxmox/resources
func (h *ProxmoxHandler) GetResources(w http.ResponseWriter, r *http.Request) {
	// Toujours relire la config (prend en compte les mises à jour du token sans redémarrage)
	h.initClient()
	if h.client == nil {
		JSONError(w, "Proxmox non configuré", http.StatusServiceUnavailable)
		return
	}

	resources, err := h.client.GetResources("")
	if err != nil {
		log.Printf("[proxmox/resources] Erreur API : %v", err)
		JSONError(w, "Erreur API Proxmox : "+err.Error(), http.StatusBadGateway)
		return
	}

	JSONResponse(w, http.StatusOK, resources)
}

// GetLXC retourne uniquement les conteneurs LXC.
// GET /api/proxmox/lxc
func (h *ProxmoxHandler) GetLXC(w http.ResponseWriter, r *http.Request) {
	h.initClient()
	if h.client == nil {
		JSONError(w, "Proxmox non configuré", http.StatusServiceUnavailable)
		return
	}

	lxcs, err := h.client.GetLXCList()
	if err != nil {
		JSONError(w, "Erreur API Proxmox : "+err.Error(), http.StatusBadGateway)
		return
	}

	JSONResponse(w, http.StatusOK, lxcs)
}

// StartLXC démarre un conteneur LXC.
// POST /api/proxmox/lxc/{vmid}/start
func (h *ProxmoxHandler) StartLXC(w http.ResponseWriter, r *http.Request) {
	claims := GetClaims(r)
	vmid, node, err := h.extractVMID(r)
	if err != nil {
		JSONError(w, err.Error(), http.StatusBadRequest)
		return
	}

	if h.client == nil {
		h.initClient()
	}
	if h.client == nil {
		JSONError(w, "Proxmox non configuré", http.StatusServiceUnavailable)
		return
	}

	if err := h.client.StartLXC(node, vmid); err != nil {
		JSONError(w, "Erreur démarrage LXC : "+err.Error(), http.StatusBadGateway)
		return
	}

	h.auditLogger.Log(&claims.UserID, claims.Username, "lxc_start", strconv.Itoa(vmid), nil, clientIP(r))
	JSONResponse(w, http.StatusOK, map[string]string{"message": "LXC démarré"})
}

// StopLXC arrête un conteneur LXC.
// POST /api/proxmox/lxc/{vmid}/stop
func (h *ProxmoxHandler) StopLXC(w http.ResponseWriter, r *http.Request) {
	claims := GetClaims(r)
	vmid, node, err := h.extractVMID(r)
	if err != nil {
		JSONError(w, err.Error(), http.StatusBadRequest)
		return
	}

	if h.client == nil {
		h.initClient()
	}

	if err := h.client.StopLXC(node, vmid); err != nil {
		JSONError(w, "Erreur arrêt LXC : "+err.Error(), http.StatusBadGateway)
		return
	}

	h.auditLogger.Log(&claims.UserID, claims.Username, "lxc_stop", strconv.Itoa(vmid), nil, clientIP(r))
	JSONResponse(w, http.StatusOK, map[string]string{"message": "LXC arrêté"})
}

// WebSocket retourne un WebSocket qui envoie les mises à jour Proxmox en temps réel.
// GET /ws/proxmox
func (h *ProxmoxHandler) WebSocket(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return
	}

	claims := GetClaims(r)
	var userID int64
	if claims != nil {
		userID = claims.UserID
	}

	client := h.hub.NewClient(conn, userID)
	client.Subscribe("proxmox")
}

// StartPolling démarre le polling périodique de l'API Proxmox et publie les updates via WebSocket.
// À appeler au démarrage du serveur.
func (h *ProxmoxHandler) StartPolling() {
	go func() {
		ticker := time.NewTicker(10 * time.Second)
		defer ticker.Stop()

		for range ticker.C {
			if h.client == nil {
				h.initClient()
				if h.client == nil {
					continue
				}
			}

			resources, err := h.client.GetResources("")
			if err != nil {
				continue
			}

			h.hub.Publish("proxmox", "resources_update", resources)
		}
	}()
}

// extractVMID extrait l'ID de VM et le nom du nœud depuis l'URL et les query params.
func (h *ProxmoxHandler) extractVMID(r *http.Request) (int, string, error) {
	vmidStr := chi.URLParam(r, "vmid")
	vmid, err := strconv.Atoi(vmidStr)
	if err != nil {
		return 0, "", &invalidParamError{param: "vmid", value: vmidStr}
	}

	node := r.URL.Query().Get("node")
	if node == "" {
		node = "pve" // Nœud par défaut
	}

	return vmid, node, nil
}

type invalidParamError struct {
	param string
	value string
}

func (e *invalidParamError) Error() string {
	return "Paramètre invalide : " + e.param + " = " + e.value
}
