package parser_test

import (
	"testing"

	"github.com/antitbone/ja4/ja4ebpf/internal/parser"
)

func TestDetectH2PrefaceTrue(t *testing.T) {
	preface := []byte("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
	data := append(preface, 0x00, 0x00) // données supplémentaires

	if !parser.DetectH2Preface(data) {
		t.Error("H2Magic non détecté dans un buffer valide")
	}
}

func TestDetectH2PrefaceFalse(t *testing.T) {
	if parser.DetectH2Preface([]byte("GET / HTTP/1.1\r\n")) {
		t.Error("détection faux positif HTTP/1.1 comme HTTP/2")
	}
}

func TestDetectH2PrefaceTooShort(t *testing.T) {
	if parser.DetectH2Preface([]byte("PRI *")) {
		t.Error("détection sur buffer trop court")
	}
}

func TestH2MagicPrefaceLen(t *testing.T) {
	if parser.H2MagicPrefaceLen() != 24 {
		t.Errorf("longueur préambule HTTP/2 attendue 24, obtenue %d", parser.H2MagicPrefaceLen())
	}
}

func TestParseH2ClientPrefaceSettingsEmpty(t *testing.T) {
	// Frame SETTINGS vide (longueur 0, aucun paramètre) sur stream 0
	frame := buildH2Frame(0x4, 0x0, 0, []byte{})
	settings, err := parser.ParseH2ClientPreface(frame)
	if err != nil {
		t.Fatalf("ParseH2ClientPreface: %v", err)
	}
	if settings == nil {
		t.Fatal("settings ne doit pas être nil")
	}
	// Tous les champs doivent être -1 (absent)
	if settings.HeaderTableSize != -1 {
		t.Errorf("HeaderTableSize: attendu -1, obtenu %d", settings.HeaderTableSize)
	}
	if settings.InitialWindowSize != -1 {
		t.Errorf("InitialWindowSize: attendu -1, obtenu %d", settings.InitialWindowSize)
	}
}

func TestParseH2ClientPrefaceSettingsWithValues(t *testing.T) {
	// Frame SETTINGS avec INITIAL_WINDOW_SIZE=65536 et MAX_CONCURRENT_STREAMS=100
	settingsPayload := []byte{
		0x00, 0x04, 0x00, 0x01, 0x00, 0x00, // INITIAL_WINDOW_SIZE = 65536
		0x00, 0x03, 0x00, 0x00, 0x00, 0x64, // MAX_CONCURRENT_STREAMS = 100
	}
	frame := buildH2Frame(0x4, 0x0, 0, settingsPayload)

	settings, err := parser.ParseH2ClientPreface(frame)
	if err != nil {
		t.Fatalf("ParseH2ClientPreface: %v", err)
	}

	if settings.InitialWindowSize != 65536 {
		t.Errorf("InitialWindowSize: attendu 65536, obtenu %d", settings.InitialWindowSize)
	}
	if settings.MaxConcurrentStreams != 100 {
		t.Errorf("MaxConcurrentStreams: attendu 100, obtenu %d", settings.MaxConcurrentStreams)
	}
	// Les paramètres non présents restent à -1
	if settings.HeaderTableSize != -1 {
		t.Errorf("HeaderTableSize non fourni: attendu -1, obtenu %d", settings.HeaderTableSize)
	}
}

func TestParseH2ClientPrefaceWindowUpdate(t *testing.T) {
	// Frame WINDOW_UPDATE sur stream 0 avec incrément = 1073741824
	wuPayload := []byte{0x40, 0x00, 0x00, 0x00} // 0x40000000 = 1073741824
	frame := buildH2Frame(0x8, 0x0, 0, wuPayload)

	settings, err := parser.ParseH2ClientPreface(frame)
	if err != nil {
		t.Fatalf("ParseH2ClientPreface: %v", err)
	}
	if settings.WindowUpdateIncrement != 1073741824 {
		t.Errorf("WindowUpdateIncrement: attendu 1073741824, obtenu %d", settings.WindowUpdateIncrement)
	}
}

func TestParseH2ClientPrefaceCombined(t *testing.T) {
	// SETTINGS + WINDOW_UPDATE combinés (comme envoyé par curl/h2)
	settingsPayload := []byte{
		0x00, 0x01, 0x00, 0x00, 0x10, 0x00, // HEADER_TABLE_SIZE = 4096
		0x00, 0x04, 0x00, 0x00, 0xff, 0xff, // INITIAL_WINDOW_SIZE = 65535
	}
	wuPayload := []byte{0x00, 0x0f, 0x00, 0x01} // WINDOW_UPDATE incr = 983041

	frames := buildH2Frame(0x4, 0x0, 0, settingsPayload)
	frames = append(frames, buildH2Frame(0x8, 0x0, 0, wuPayload)...)

	settings, err := parser.ParseH2ClientPreface(frames)
	if err != nil {
		t.Fatalf("ParseH2ClientPreface: %v", err)
	}
	if settings.HeaderTableSize != 4096 {
		t.Errorf("HeaderTableSize: attendu 4096, obtenu %d", settings.HeaderTableSize)
	}
	if settings.InitialWindowSize != 65535 {
		t.Errorf("InitialWindowSize: attendu 65535, obtenu %d", settings.InitialWindowSize)
	}
	if settings.WindowUpdateIncrement != 983041 {
		t.Errorf("WindowUpdateIncrement: attendu 983041, obtenu %d", settings.WindowUpdateIncrement)
	}
}

func TestParseH2ClientPrefaceEmpty(t *testing.T) {
	// Données vides : doit retourner sans erreur, settings avec valeurs par défaut (-1)
	settings, err := parser.ParseH2ClientPreface([]byte{})
	if err != nil {
		t.Fatalf("ParseH2ClientPreface sur vide: %v", err)
	}
	if settings == nil {
		t.Error("settings ne doit pas être nil même pour données vides")
	}
	if settings.HeaderTableSize != -1 {
		t.Errorf("HeaderTableSize: attendu -1 par défaut, obtenu %d", settings.HeaderTableSize)
	}
}

func TestParseH2ClientPrefaceTruncatedFrame(t *testing.T) {
	// Frame tronquée : en-tête complet mais payload incomplet
	truncated := []byte{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x01} // payload tronqué
	settings, err := parser.ParseH2ClientPreface(truncated)
	if err != nil {
		t.Fatalf("ParseH2ClientPreface sur frame tronquée: %v (doit tolérer)", err)
	}
	// Les paramètres restent à -1 car le payload est incomplet
	_ = settings
}

// ── Helpers ───────────────────────────────────────────────────────────────

// buildH2Frame construit une frame HTTP/2 brute (en-tête 9 octets + payload).
func buildH2Frame(frameType, flags uint8, streamID uint32, payload []byte) []byte {
	l := len(payload)
	frame := []byte{
		byte(l >> 16), byte(l >> 8), byte(l), // longueur sur 3 octets
		frameType, flags,
		byte(streamID >> 24), byte(streamID >> 16), byte(streamID >> 8), byte(streamID),
	}
	return append(frame, payload...)
}
