// Package antispam defines HTML templates and the corresponding server-side
// logic for integrating captcha providers.
package antispam

import (
	"context"
	"html/template"
	"log/slog"
	"net/http"
	"os"
)

type Antispam interface {
	// Script contains HTML `<script>` tag(s) that need to be injected into the
	// form content or document head.
	Script() template.HTML
	// FormField is the template that needs to be injected into the `<form>`.
	FormField() template.HTML
	// CheckRequest uses the captcha provider to determine if request should be
	// allowed to proceed.
	//
	// Implementations using HTTP forms may require r.ParseForm() to have been
	// called.
	CheckRequest(r *http.Request) (bool, error)
}

type antispamKey struct{}

// GetAntispam returns the active antispam implementation from request context.
func GetAntispam(r *http.Request) Antispam {
	if antispam, ok := r.Context().Value(antispamKey{}).(Antispam); ok {
		return antispam
	}
	return NewNoop()
}

// WithAntispam chooses a suitable antispam implementation sets up request
// context accordingly.
func WithAntispam(next http.Handler) http.HandlerFunc {
	hCaptchaSiteKey := os.Getenv("HCAPTCHA_SITE_KEY")
	hCaptchaSecretKey := os.Getenv("HCAPTCHA_SECRET_KEY")
	if hCaptchaSiteKey != "" && hCaptchaSecretKey != "" {
		slog.Info("antispam: using hCaptcha implementation")
		return withAntispam(next, NewHcaptcha(hCaptchaSiteKey, hCaptchaSecretKey))
	}

	slog.Info("antispam: falling back to noop implementation")
	return next.ServeHTTP
}

func withAntispam(next http.Handler, impl Antispam) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithValue(r.Context(), antispamKey{}, impl)
		next.ServeHTTP(w, r.WithContext(ctx))
	}
}
