// Handlers pour la page paramètres : lecture/écriture de la configuration globale.
package api

import (
	"net/http"
	"strconv"

	"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/logbuffer"
	"github.com/go-chi/chi/v5"
)

// SettingsHandler contient les handlers de configuration.
type SettingsHandler struct {
	db          *db.DB
	auditLogger *audit.Logger
	encryptor   *crypto.Encryptor
}

// NewSettingsHandler crée un SettingsHandler.
func NewSettingsHandler(database *db.DB, auditLog *audit.Logger, enc *crypto.Encryptor) *SettingsHandler {
	return &SettingsHandler{db: database, auditLogger: auditLog, encryptor: enc}
}

// paramètres publics (non-sensibles) accessibles par les admins.
var publicSettings = []string{
	"instance_name",
	"public_url",
	"default_lang",
	"proxmox_url",
	"ssh_host",
	"ssh_username",
}

// paramètres sensibles : modifiables en écriture seule, stockés chiffrés.
var encryptedSettings = []string{"ssh_password", "proxmox_token"}

// GetAll retourne tous les paramètres publics de l'application.
// GET /api/settings
func (h *SettingsHandler) GetAll(w http.ResponseWriter, r *http.Request) {
	result := make(map[string]string)
	for _, key := range publicSettings {
		value, _, err := h.db.GetSetting(key)
		if err == nil {
			result[key] = value
		}
	}
	JSONResponse(w, http.StatusOK, result)
}

// UpdateSetting met à jour un paramètre spécifique.
// PUT /api/settings/{key}
// Body: { "value": "..." }
func (h *SettingsHandler) UpdateSetting(w http.ResponseWriter, r *http.Request) {
	claims := GetClaims(r)

	key := chi.URLParam(r, "key")
	if key == "" {
		JSONError(w, "Clé de paramètre manquante", http.StatusBadRequest)
		return
	}

	// Vérifier que la clé est modifiable (publique ou chiffrée)
	isPublic := false
	for _, k := range publicSettings {
		if k == key {
			isPublic = true
			break
		}
	}
	isEncrypted := false
	for _, k := range encryptedSettings {
		if k == key {
			isEncrypted = true
			break
		}
	}
	if !isPublic && !isEncrypted {
		JSONError(w, "Paramètre non modifiable via l'API", http.StatusForbidden)
		return
	}

	var body struct {
		Value string `json:"value"`
	}
	if err := decodeJSON(r, &body); err != nil {
		JSONError(w, "Corps de requête invalide", http.StatusBadRequest)
		return
	}

	// Paramètres chiffrés : valeur vide = ne pas modifier
	if isEncrypted {
		if body.Value == "" {
			JSONResponse(w, http.StatusOK, map[string]string{"message": "Aucun changement"})
			return
		}
		encrypted, err := h.encryptor.Encrypt(body.Value)
		if err != nil {
			JSONError(w, "Erreur chiffrement", http.StatusInternalServerError)
			return
		}
		if err := h.db.SetSetting(key, encrypted, true); err != nil {
			JSONError(w, "Erreur sauvegarde paramètre", http.StatusInternalServerError)
			return
		}
		h.auditLogger.Log(&claims.UserID, claims.Username, "setting_update", key,
			map[string]string{"key": key}, clientIP(r))
		JSONResponse(w, http.StatusOK, map[string]string{"message": "Paramètre mis à jour"})
		return
	}

	if err := h.db.SetSetting(key, body.Value, false); err != nil {
		JSONError(w, "Erreur sauvegarde paramètre", http.StatusInternalServerError)
		return
	}

	h.auditLogger.Log(&claims.UserID, claims.Username, "setting_update", key,
		map[string]string{"key": key}, clientIP(r))

	JSONResponse(w, http.StatusOK, map[string]string{"message": "Paramètre mis à jour"})
}

// GetModules retourne la liste de tous les modules et leur état.
// GET /api/modules
func (h *SettingsHandler) GetModules(w http.ResponseWriter, r *http.Request) {
	rows, err := h.db.Query(`
		SELECT id, name, description, version, is_core, is_enabled, installed_at
		FROM modules ORDER BY is_core DESC, name ASC
	`)
	if err != nil {
		JSONError(w, "Erreur lecture modules", http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	type module struct {
		ID          string  `json:"id"`
		Name        string  `json:"name"`
		Description string  `json:"description"`
		Version     string  `json:"version"`
		IsCore      bool    `json:"is_core"`
		IsEnabled   bool    `json:"is_enabled"`
		InstalledAt *string `json:"installed_at,omitempty"`
	}

	var modules []module
	for rows.Next() {
		var m module
		var isCore, isEnabled int
		var installedAt *string
		rows.Scan(&m.ID, &m.Name, &m.Description, &m.Version, &isCore, &isEnabled, &installedAt)
		m.IsCore = isCore == 1
		m.IsEnabled = isEnabled == 1
		m.InstalledAt = installedAt
		modules = append(modules, m)
	}

	JSONResponse(w, http.StatusOK, modules)
}

// EnableModule active un module.
// POST /api/modules/{id}/enable
func (h *SettingsHandler) EnableModule(w http.ResponseWriter, r *http.Request) {
	claims := GetClaims(r)
	id := chi.URLParam(r, "id")

	result, err := h.db.Exec(`UPDATE modules SET is_enabled = 1 WHERE id = ?`, id)
	if err != nil {
		JSONError(w, "Erreur activation module", http.StatusInternalServerError)
		return
	}
	n, _ := result.RowsAffected()
	if n == 0 {
		JSONError(w, "Module introuvable", http.StatusNotFound)
		return
	}

	h.auditLogger.Log(&claims.UserID, claims.Username, "module_enable", id, nil, clientIP(r))
	JSONResponse(w, http.StatusOK, map[string]string{"message": "Module activé (redémarrage requis pour prendre effet)"})
}

// DisableModule désactive un module (ne peut pas désactiver les modules CORE).
// POST /api/modules/{id}/disable
func (h *SettingsHandler) DisableModule(w http.ResponseWriter, r *http.Request) {
	claims := GetClaims(r)
	id := chi.URLParam(r, "id")

	// Vérifier que ce n'est pas un module CORE
	var isCore int
	if err := h.db.QueryRow(`SELECT is_core FROM modules WHERE id = ?`, id).Scan(&isCore); err != nil {
		JSONError(w, "Module introuvable", http.StatusNotFound)
		return
	}
	if isCore == 1 {
		JSONError(w, "Les modules CORE ne peuvent pas être désactivés", http.StatusForbidden)
		return
	}

	h.db.Exec(`UPDATE modules SET is_enabled = 0 WHERE id = ?`, id)
	h.auditLogger.Log(&claims.UserID, claims.Username, "module_disable", id, nil, clientIP(r))
	JSONResponse(w, http.StatusOK, map[string]string{"message": "Module désactivé"})
}

// GetLogs retourne les dernières lignes du log applicatif (tampon mémoire).
// GET /api/settings/logs?lines=200
func (h *SettingsHandler) GetLogs(w http.ResponseWriter, r *http.Request) {
	n := 200
	if s := r.URL.Query().Get("lines"); s != "" {
		if v, err := strconv.Atoi(s); err == nil && v > 0 {
			n = v
		}
	}
	lines := logbuffer.Global.Lines(n)
	if lines == nil {
		lines = []string{}
	}
	JSONResponse(w, http.StatusOK, lines)
}

// GetAuditLog retourne le journal d'audit paginé.
// GET /api/settings/audit
func (h *SettingsHandler) GetAuditLog(w http.ResponseWriter, r *http.Request) {
	rows, err := h.db.Query(`
		SELECT id, username, action, resource, details, ip, created_at
		FROM audit_log ORDER BY created_at DESC LIMIT 100
	`)
	if err != nil {
		JSONError(w, "Erreur lecture audit", http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	type entry struct {
		ID        int64   `json:"id"`
		Username  string  `json:"username"`
		Action    string  `json:"action"`
		Resource  *string `json:"resource,omitempty"`
		Details   *string `json:"details,omitempty"`
		IP        *string `json:"ip,omitempty"`
		CreatedAt string  `json:"created_at"`
	}

	var entries []entry
	for rows.Next() {
		var e entry
		var resource, details, ip *string
		rows.Scan(&e.ID, &e.Username, &e.Action, &resource, &details, &ip, &e.CreatedAt)
		e.Resource = resource
		e.Details = details
		e.IP = ip
		entries = append(entries, e)
	}

	JSONResponse(w, http.StatusOK, entries)
}
