// Package proxmox fournit un client pour l'API REST Proxmox VE.
// Les credentials (token API ou user/password) sont stockés chiffrés en SQLite.
package proxmox

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"
)

// Client est le client HTTP vers l'API Proxmox VE.
type Client struct {
	baseURL    string
	httpClient *http.Client
	token      string // Format: "PVEAPIToken=user@realm!tokenid=secret"
}

// NodeStatus représente l'état d'un nœud Proxmox.
type NodeStatus struct {
	Node   string  `json:"node"`
	Status string  `json:"status"`
	CPU    float64 `json:"cpu"`
	MaxCPU int     `json:"maxcpu"`
	Mem    int64   `json:"mem"`
	MaxMem int64   `json:"maxmem"`
	Uptime int64   `json:"uptime"`
}

// Resource représente un LXC, une VM ou un autre objet Proxmox.
type Resource struct {
	VMID      int     `json:"vmid"`
	Name      string  `json:"name"`
	Node      string  `json:"node"`
	Type      string  `json:"type"` // "lxc" | "qemu" | "storage" | "node"
	Status    string  `json:"status"` // "running" | "stopped"
	CPU       float64 `json:"cpu"`
	MaxCPU    int     `json:"maxcpu"`
	Mem       int64   `json:"mem"`
	MaxMem    int64   `json:"maxmem"`
	Disk      int64   `json:"disk"`
	MaxDisk   int64   `json:"maxdisk"`
	Uptime    int64   `json:"uptime"`
	NetIn     int64   `json:"netin"`
	NetOut    int64   `json:"netout"`
}

// proxmoxResponse est l'enveloppe générique des réponses API Proxmox.
type proxmoxResponse struct {
	Data  json.RawMessage `json:"data"`
	Error string          `json:"errors"`
}

// NewClient crée un client Proxmox avec le token API fourni.
// baseURL : ex "https://10.0.0.1:8006"
// token : ex "PVEAPIToken=enzo@pam!panel=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
func NewClient(baseURL, token string) *Client {
	return &Client{
		baseURL: strings.TrimRight(baseURL, "/"),
		token:   token,
		httpClient: &http.Client{
			Timeout: 15 * time.Second,
			Transport: &http.Transport{
				// Proxmox utilise des certificats auto-signés
				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			},
		},
	}
}

// GetNodes retourne la liste des nœuds Proxmox.
func (c *Client) GetNodes() ([]NodeStatus, error) {
	var nodes []NodeStatus
	if err := c.get("/api2/json/nodes", &nodes); err != nil {
		return nil, err
	}
	return nodes, nil
}

// GetResources retourne tous les LXC et VM de l'ensemble du cluster.
// Le paramètre type filtre les résultats ("lxc", "vm", ou "" pour tout).
func (c *Client) GetResources(resourceType string) ([]Resource, error) {
	path := "/api2/json/cluster/resources"
	if resourceType != "" {
		path += "?type=" + resourceType
	}
	var resources []Resource
	if err := c.get(path, &resources); err != nil {
		return nil, err
	}
	return resources, nil
}

// GetLXCList retourne uniquement les conteneurs LXC.
func (c *Client) GetLXCList() ([]Resource, error) {
	return c.GetResources("lxc")
}

// GetVMList retourne uniquement les machines virtuelles QEMU.
func (c *Client) GetVMList() ([]Resource, error) {
	return c.GetResources("vm")
}

// StartLXC démarre un conteneur LXC.
func (c *Client) StartLXC(node string, vmid int) error {
	_, err := c.post(fmt.Sprintf("/api2/json/nodes/%s/lxc/%d/status/start", node, vmid), nil)
	return err
}

// StopLXC arrête un conteneur LXC.
func (c *Client) StopLXC(node string, vmid int) error {
	_, err := c.post(fmt.Sprintf("/api2/json/nodes/%s/lxc/%d/status/stop", node, vmid), nil)
	return err
}

// StartVM démarre une machine virtuelle.
func (c *Client) StartVM(node string, vmid int) error {
	_, err := c.post(fmt.Sprintf("/api2/json/nodes/%s/qemu/%d/status/start", node, vmid), nil)
	return err
}

// StopVM arrête une machine virtuelle.
func (c *Client) StopVM(node string, vmid int) error {
	_, err := c.post(fmt.Sprintf("/api2/json/nodes/%s/qemu/%d/status/stop", node, vmid), nil)
	return err
}

// TestConnection vérifie que le token API est valide en récupérant la liste des nœuds.
func (c *Client) TestConnection() error {
	_, err := c.GetNodes()
	return err
}

// get effectue une requête GET et décode la réponse dans dest.
func (c *Client) get(path string, dest any) error {
	req, err := http.NewRequest("GET", c.baseURL+path, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Authorization", c.token)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("requête Proxmox : %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == 401 {
		return fmt.Errorf("token Proxmox invalide ou expiré")
	}
	if resp.StatusCode >= 400 {
		return fmt.Errorf("erreur Proxmox API : HTTP %d", resp.StatusCode)
	}

	return c.decodeResponse(resp.Body, dest)
}

// post effectue une requête POST.
func (c *Client) post(path string, body any) (json.RawMessage, error) {
	var reader io.Reader
	if body != nil {
		data, err := json.Marshal(body)
		if err != nil {
			return nil, err
		}
		reader = strings.NewReader(string(data))
	}

	req, err := http.NewRequest("POST", c.baseURL+path, reader)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", c.token)
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("requête Proxmox : %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == 401 {
		return nil, fmt.Errorf("token Proxmox invalide")
	}
	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("erreur Proxmox API : HTTP %d", resp.StatusCode)
	}

	var result json.RawMessage
	c.decodeResponse(resp.Body, &result)
	return result, nil
}

// decodeResponse décode l'enveloppe JSON Proxmox et extrait le champ "data".
func (c *Client) decodeResponse(body io.Reader, dest any) error {
	var wrapper proxmoxResponse
	if err := json.NewDecoder(body).Decode(&wrapper); err != nil {
		return fmt.Errorf("décodage réponse Proxmox : %w", err)
	}
	if wrapper.Error != "" {
		return fmt.Errorf("erreur Proxmox : %s", wrapper.Error)
	}
	if dest == nil || wrapper.Data == nil {
		return nil
	}
	return json.Unmarshal(wrapper.Data, dest)
}
