// Package parser fournit les parseurs pour les protocoles HTTP/1.x, HTTP/2 et TLS.
package parser

import (
	"bytes"
	"strings"
)

// HTTP1Request représente une requête HTTP/1.x parsée depuis le flux déchiffré.
type HTTP1Request struct {
	Method    string   // méthode HTTP (GET, POST, …)
	Path      string   // chemin (sans query string)
	Query     string   // query string (sans le '?')
	Protocol  string   // "HTTP/1.0" ou "HTTP/1.1"
	Headers   []string // noms des en-têtes dans l'ordre exact d'arrivée
	HeaderSig string   // signature : noms joints par ";"
}

// HTTP1Response représente le début d'une réponse HTTP/1.x (status line).
type HTTP1Response struct {
	StatusCode int
}

// knownMethods est la liste des méthodes HTTP/1.x reconnues.
var knownMethods = []string{
	"GET", "POST", "PUT", "DELETE", "HEAD",
	"OPTIONS", "PATCH", "CONNECT", "TRACE",
}

// IsHTTP1Request retourne true si les premiers octets ressemblent à une
// requête HTTP/1.x (commence par une méthode reconnue suivi d'un espace).
func IsHTTP1Request(data []byte) bool {
	for _, m := range knownMethods {
		if bytes.HasPrefix(data, []byte(m+" ")) {
			return true
		}
	}
	return false
}

// IsHTTP1Response retourne true si les premiers octets ressemblent à une
// réponse HTTP/1.x ("HTTP/1.").
func IsHTTP1Response(data []byte) bool {
	return bytes.HasPrefix(data, []byte("HTTP/1."))
}

// ParseHTTP1Request extrait les champs d'une requête HTTP/1.x depuis un buffer brut.
// Retourne nil sans erreur si le buffer ne contient pas de requête complète.
func ParseHTTP1Request(data []byte) *HTTP1Request {
	// Localiser la fin de la request-line + headers (double CRLF)
	headerEnd := bytes.Index(data, []byte("\r\n\r\n"))
	if headerEnd < 0 {
		headerEnd = len(data)
	}
	text := string(data[:headerEnd])
	lines := strings.Split(text, "\r\n")
	if len(lines) == 0 {
		return nil
	}

	// Parser la request-line : "METHOD path HTTP/x.y"
	requestLine := lines[0]
	parts := strings.SplitN(requestLine, " ", 3)
	if len(parts) < 2 {
		return nil
	}
	method := parts[0]
	rawPath := parts[1]
	protocol := "HTTP/1.1"
	if len(parts) == 3 {
		protocol = parts[2]
	}

	// Valider la méthode
	validMethod := false
	for _, m := range knownMethods {
		if method == m {
			validMethod = true
			break
		}
	}
	if !validMethod {
		return nil
	}

	// Séparer path et query string
	path := rawPath
	query := ""
	if idx := strings.Index(rawPath, "?"); idx >= 0 {
		path = rawPath[:idx]
		query = rawPath[idx+1:]
	}

	// Extraire les noms d'en-têtes dans l'ordre
	headers := make([]string, 0, len(lines)-1)
	for _, line := range lines[1:] {
		if line == "" {
			break
		}
		if colon := strings.Index(line, ":"); colon > 0 {
			name := strings.TrimSpace(line[:colon])
			if name != "" {
				headers = append(headers, name)
			}
		}
	}

	sig := strings.Join(headers, ";")

	return &HTTP1Request{
		Method:    method,
		Path:      path,
		Query:     query,
		Protocol:  protocol,
		Headers:   headers,
		HeaderSig: sig,
	}
}

// ParseHTTP1Response extrait le code de statut d'une réponse HTTP/1.x.
// Retourne nil si le buffer n'est pas une réponse HTTP/1.x reconnaissable.
func ParseHTTP1Response(data []byte) *HTTP1Response {
	if !IsHTTP1Response(data) {
		return nil
	}
	// Status-line : "HTTP/1.1 200 OK\r\n..."
	line := data
	if idx := bytes.IndexByte(data, '\n'); idx >= 0 {
		line = data[:idx]
	}
	parts := strings.SplitN(strings.TrimRight(string(line), "\r\n"), " ", 3)
	if len(parts) < 2 {
		return nil
	}
	code := 0
	for _, c := range parts[1] {
		if c < '0' || c > '9' {
			break
		}
		code = code*10 + int(c-'0')
	}
	if code < 100 || code > 599 {
		return nil
	}
	return &HTTP1Response{StatusCode: code}
}
