// Handler pour le terminal SSH interactif via WebSocket + PTY.
// Utilise golang.org/x/crypto/ssh pour la connexion et gorilla/websocket pour le transport.
package api

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"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"
	gorillaws "github.com/gorilla/websocket"
	gossh "golang.org/x/crypto/ssh"
)

// TerminalHandler gère les sessions de terminal SSH interactif.
type TerminalHandler struct {
	db          *db.DB
	auditLogger *audit.Logger
	encryptor   *crypto.Encryptor
}

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

// terminalCmd représente un message de contrôle envoyé via WebSocket.
type terminalCmd struct {
	Type string `json:"type"` // "resize" | "data"
	Cols int    `json:"cols,omitempty"`
	Rows int    `json:"rows,omitempty"`
}

// WebSocket ouvre un terminal SSH interactif via WebSocket.
// GET /ws/terminal
// Query params: host (optionnel, défaut = ssh_host depuis config)
func (h *TerminalHandler) WebSocket(w http.ResponseWriter, r *http.Request) {
	claims := GetClaims(r)

	// Connexion WebSocket
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return
	}
	defer conn.Close()

	// Récupérer les params SSH
	sshHost := r.URL.Query().Get("host")
	if sshHost == "" {
		sshHost, _, _ = h.db.GetSetting("ssh_host")
	}
	sshUser, _, _ := h.db.GetSetting("ssh_username")
	encryptedPass, _, _ := h.db.GetSetting("ssh_password")
	sshPass, _ := h.encryptor.Decrypt(encryptedPass)

	if sshHost == "" {
		log.Printf("[terminal] SSH non configuré — user=%s", claims.Username)
		conn.WriteMessage(gorillaws.TextMessage, []byte("\r\nErreur : SSH non configuré\r\n"))
		return
	}

	log.Printf("[terminal] Ouverture session — user=%s ssh_host=%s", claims.Username, sshHost)
	h.auditLogger.Log(&claims.UserID, claims.Username, "terminal_open", sshHost, nil, clientIP(r))

	// Établir la connexion SSH
	sshConfig := &gossh.ClientConfig{
		User: sshUser,
		Auth: []gossh.AuthMethod{
			gossh.Password(sshPass),
		},
		Timeout:         15 * time.Second,
		HostKeyCallback: gossh.InsecureIgnoreHostKey(),
	}

	sshClient, err := gossh.Dial("tcp", sshHost, sshConfig)
	if err != nil {
		log.Printf("[terminal] Échec connexion SSH %s@%s : %v", sshUser, sshHost, err)
		conn.WriteMessage(gorillaws.TextMessage, []byte(fmt.Sprintf("\r\nErreur SSH : %v\r\n", err)))
		return
	}
	log.Printf("[terminal] Connecté — %s@%s", sshUser, sshHost)
	defer sshClient.Close()

	// Créer une session SSH avec pseudo-terminal
	session, err := sshClient.NewSession()
	if err != nil {
		conn.WriteMessage(gorillaws.TextMessage, []byte(fmt.Sprintf("\r\nErreur session SSH : %v\r\n", err)))
		return
	}
	defer session.Close()

	// Configurer le PTY (terminal 80x24 par défaut)
	modes := gossh.TerminalModes{
		gossh.ECHO:          1,
		gossh.TTY_OP_ISPEED: 14400,
		gossh.TTY_OP_OSPEED: 14400,
	}
	if err := session.RequestPty("xterm-256color", 24, 80, modes); err != nil {
		conn.WriteMessage(gorillaws.TextMessage, []byte(fmt.Sprintf("\r\nErreur PTY : %v\r\n", err)))
		return
	}

	// Pipes stdin/stdout entre WebSocket et SSH
	stdinPipe, err := session.StdinPipe()
	if err != nil {
		return
	}
	stdoutPipe, err := session.StdoutPipe()
	if err != nil {
		return
	}

	if err := session.Shell(); err != nil {
		conn.WriteMessage(gorillaws.TextMessage, []byte(fmt.Sprintf("\r\nErreur shell : %v\r\n", err)))
		return
	}

	// Goroutine : SSH stdout → WebSocket
	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := stdoutPipe.Read(buf)
			if err != nil {
				break
			}
			conn.WriteMessage(gorillaws.BinaryMessage, buf[:n])
		}
		conn.Close()
	}()

	// Boucle principale : WebSocket → SSH stdin
	for {
		_, msg, err := conn.ReadMessage()
		if err != nil {
			break
		}

		// Détecter les messages de contrôle JSON (ex: resize)
		if len(msg) > 0 && msg[0] == '{' {
			var cmd terminalCmd
			if json.Unmarshal(msg, &cmd) == nil && cmd.Type == "resize" && cmd.Cols > 0 && cmd.Rows > 0 {
				session.WindowChange(cmd.Rows, cmd.Cols)
				continue
			}
		}

		// Données brutes → stdin SSH
		stdinPipe.Write(msg)
	}

	h.auditLogger.Log(&claims.UserID, claims.Username, "terminal_close", sshHost, nil, clientIP(r))
}
