// Package correlation gère l'état des sessions TCP et leur corrélation
// entre les couches réseau (L3/L4), TLS (L5) et applicative (L7).
package correlation

import (
	"sync"
	"time"
)

// SessionKey identifie une connexion TCP de façon unique.
type SessionKey struct {
	SrcIP   [4]byte // adresse IP source en représentation binaire
	SrcPort uint16  // port source
}

// L3L4 contient les caractéristiques réseau et transport de la connexion.
type L3L4 struct {
	TTL           uint8     // TTL IP observé dans le SYN
	DFBit         bool      // bit Don't Fragment actif
	IPID          uint16    // champ identification IP
	WindowSize    uint16    // taille de fenêtre TCP initiale
	WindowScale   uint8     // facteur d'échelle de fenêtre (0xFF = absent)
	MSS           uint16    // Maximum Segment Size (0 = absent)
	TCPOptionsRaw []byte    // options TCP brutes (max 40 octets)
	SYNTimestamp  time.Time // horodatage du paquet SYN
}

// TLSInfo contient les données extraites du ClientHello TLS.
type TLSInfo struct {
	ClientHelloRaw   []byte    // payload ClientHello brut
	JA4Hash          string    // empreinte JA4 calculée
	SNI              string    // Server Name Indication
	ALPN             []string  // protocoles Application-Layer Protocol Negotiation
	TLSVersion       uint16    // version TLS la plus haute annoncée
	CipherSuites     []uint16  // suites de chiffrement proposées
	Extensions       []uint16  // identifiants des extensions TLS
	Timestamp        time.Time // horodatage du ClientHello
}

// HTTP2Settings contient les paramètres SETTINGS et WINDOW_UPDATE du client HTTP/2.
type HTTP2Settings struct {
	HeaderTableSize        int32    // SETTINGS_HEADER_TABLE_SIZE (-1 si absent)
	EnablePush             int32    // SETTINGS_ENABLE_PUSH
	MaxConcurrentStreams   int32    // SETTINGS_MAX_CONCURRENT_STREAMS
	InitialWindowSize      int32    // SETTINGS_INITIAL_WINDOW_SIZE
	MaxFrameSize           int32    // SETTINGS_MAX_FRAME_SIZE
	MaxHeaderListSize      int32    // SETTINGS_MAX_HEADER_LIST_SIZE
	UnknownSettings        int32    // paramètre 0x7 (JA4H2)
	WindowUpdateIncrement  uint32   // valeur WINDOW_UPDATE sur stream 0
	PseudoHeaderOrder      []string // ordre des pseudo-headers [:method, :authority, ...]
}

// HTTPRequest représente une requête HTTP observée dans la session.
type HTTPRequest struct {
	Method         string         // méthode HTTP (GET, POST, etc.)
	Path           string         // chemin de la requête
	QueryString    string         // paramètres de requête
	StatusCode     int            // code de statut de la réponse
	ResponseSize   int64          // taille de la réponse en octets
	DurationMS     float64        // durée de traitement en millisecondes
	HeaderOrder    []string       // ordre exact des en-têtes HTTP bruts
	HeaderOrderSig string         // signature de l'ordre des en-têtes (hash)
	HTTP2Settings  *HTTP2Settings // non nil uniquement pour HTTP/2
	Timestamp      time.Time      // horodatage de la requête
}

// SessionState représente l'état complet d'une connexion TCP corrélée.
// La structure est thread-safe via un mutex interne.
type SessionState struct {
	Key           SessionKey    // identifiant de la session
	L3L4          *L3L4         // données réseau/transport (peut être nil si L7-only)
	TLS           *TLSInfo      // données TLS (peut être nil si HTTP plain)
	Requests      []HTTPRequest // requêtes HTTP observées
	MaxKeepAlives int           // nombre maximum de requêtes keep-alive

	FirstSeen    time.Time // horodatage de création de la session
	LastActivity time.Time // horodatage de la dernière activité

	mu sync.Mutex // protection des modifications concurrentes
}

// IsExpired indique si la session n'a reçu aucune activité depuis timeout.
func (s *SessionState) IsExpired(timeout time.Duration) bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	return time.Since(s.LastActivity) > timeout
}

// AddRequest ajoute une requête HTTP à la session et met à jour LastActivity.
func (s *SessionState) AddRequest(req HTTPRequest) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.Requests = append(s.Requests, req)
	s.LastActivity = time.Now()
	if len(s.Requests) > s.MaxKeepAlives && s.MaxKeepAlives > 0 {
		s.MaxKeepAlives = len(s.Requests)
	}
}

// IsSlowloris détecte si la session présente un profil d'attaque Slowloris :
// première activité il y a plus de threshold sans aucune requête complète.
func (s *SessionState) IsSlowloris(threshold time.Duration) bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	if len(s.Requests) > 0 {
		return false
	}
	return time.Since(s.FirstSeen) > threshold
}
