package webhooks

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"net/http"
	"strings"
)

// SignatureHeaders is the ordered list of headers HMAC validation consults.
// The first non-empty value wins. Conventions covered:
//   - X-Hub-Signature-256 (GitHub-style)
//   - X-Webhook-Signature-256
//   - X-Signature-256
// All expect the value `sha256=<hex>` (case-insensitive prefix).
var SignatureHeaders = []string{
	"X-Hub-Signature-256",
	"X-Webhook-Signature-256",
	"X-Signature-256",
}

// TokenHeaders is the ordered list of headers plain-token validation
// consults. Used by integrations that don't sign bodies (autobrr by default,
// most static-header webhook macros).
var TokenHeaders = []string{
	"X-Webhook-Token",
	"Authorization",
}

// Validate accepts the request as authentic if either the signature matches
// HMAC-SHA256(body, secret) or one of the token headers carries the secret
// verbatim. Constant-time comparison throughout.
//
// Returns:
//   - true, "" on success
//   - false, "<reason>" when neither check passes
func Validate(r *http.Request, body []byte, secret []byte) (bool, string) {
	if len(secret) == 0 {
		return false, "no secret"
	}

	// 1. HMAC signature.
	for _, h := range SignatureHeaders {
		v := r.Header.Get(h)
		if v == "" {
			continue
		}
		want := computeHex(secret, body)
		got := strings.TrimSpace(v)
		got = strings.TrimPrefix(got, "sha256=")
		got = strings.TrimPrefix(got, "SHA256=")
		if hmac.Equal([]byte(want), []byte(got)) {
			return true, ""
		}
	}

	// 2. Plain token (autobrr-style).
	for _, h := range TokenHeaders {
		v := r.Header.Get(h)
		if v == "" {
			continue
		}
		// Authorization header may be "Bearer <token>".
		v = strings.TrimSpace(v)
		v = strings.TrimPrefix(v, "Bearer ")
		v = strings.TrimPrefix(v, "bearer ")
		if hmac.Equal([]byte(v), secret) {
			return true, ""
		}
	}

	return false, "no valid signature or token in any of the expected headers"
}

func computeHex(secret, body []byte) string {
	m := hmac.New(sha256.New, secret)
	m.Write(body)
	return hex.EncodeToString(m.Sum(nil))
}
