// Package dispatcher fournit le routeur Magic Bytes qui unifie les événements
// issus des RingBuffers SSL (trafic chiffré déchiffré par uprobe) et HTTP plain
// (trafic clair capturé par TC ingress), et les route vers le bon parser L7.
package dispatcher

import (
	"bytes"
)

// --- Marqueurs Magic Bytes -----------------------------------------------

// h2MagicBytes est la préface HTTP/2 client sous forme de []byte.
var h2MagicBytes = []byte("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")

// Préfixes de méthodes HTTP/1.x courants (liste non exhaustive).
var http1Prefixes = [][]byte{
	[]byte("GET "),
	[]byte("POST "),
	[]byte("PUT "),
	[]byte("DELETE "),
	[]byte("HEAD "),
	[]byte("OPTIONS "),
	[]byte("PATCH "),
	[]byte("CONNECT "),
	[]byte("TRACE "),
}

// Protocol identifie le protocole applicatif détecté à partir des premiers octets.
type Protocol uint8

const (
	ProtoUnknown Protocol = iota
	ProtoHTTP1            // HTTP/1.0 ou HTTP/1.1
	ProtoHTTP2            // HTTP/2 (préface "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
)

// RawEvent représente un buffer brut reçu depuis un RingBuffer eBPF
// (SSL_read déchiffré OU payload TCP HTTP en clair).
type RawEvent struct {
	SrcIP     uint32
	SrcPort   uint16
	PID       uint32 // 0 pour les événements TC (HTTP plain, pas de PID)
	FD        uint32 // 0 pour les événements TC
	Data      []byte
	EOF       bool   // connexion terminée (uprobe SSL uniquement)
	Cleartext bool   // true = provient de rb_http_plain (TC), false = uprobe SSL
}

// Classify inspecte les premiers octets du buffer et retourne le Protocol détecté.
// La détection est purement basée sur les octets de début (pas de parsing complet).
func Classify(data []byte) Protocol {
	if len(data) == 0 {
		return ProtoUnknown
	}

	// Détection HTTP/2 : préface exacte de 24 octets
	if bytes.HasPrefix(data, h2MagicBytes) {
		return ProtoHTTP2
	}

	// Détection HTTP/1.x : commence par une méthode connue suivie d'un espace
	for _, pfx := range http1Prefixes {
		if bytes.HasPrefix(data, pfx) {
			return ProtoHTTP1
		}
	}

	// Détection HTTP/2 partiel : peut arriver si le magic est fragmenté
	// sur plusieurs lectures. Dans ce cas on laisse l'appelant accumuler.
	n := minInt(len(data), len(h2MagicBytes))
	if bytes.HasPrefix(h2MagicBytes, data[:n]) && len(data) < len(h2MagicBytes) {
		return ProtoHTTP2 // préface partielle — traiter comme H2 en cours
	}

	return ProtoUnknown
}

// minInt retourne le minimum de deux entiers (helper interne).
func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}
