package antispam

import (
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"log/slog"
	"net/http"
	"net/url"
	"strings"
)

type HCaptcha struct {
	formField          template.HTML
	secretKey          string
	siteverifyEndpoint string
}

func (hcaptcha *HCaptcha) Script() template.HTML {
	return `<script src="https://js.hcaptcha.com/1/api.js" async defer></script>`
}

func (hcaptcha *HCaptcha) FormField() template.HTML {
	return hcaptcha.formField
}

func (hcaptcha *HCaptcha) CheckRequest(r *http.Request) (bool, error) {
	err := r.ParseForm()
	if err != nil {
		return false, fmt.Errorf("error parsing form: %w", err)
	}

	token := r.PostForm.Get("x-captcha-response")
	if token == "" {
		slog.Debug("No hCaptcha response token in request")
		return false, nil
	}

	form := url.Values{}
	form.Set("secret", hcaptcha.secretKey)
	form.Set("response", token)
	res, err := http.PostForm(hcaptcha.siteverifyEndpoint, form)
	if err != nil {
		return false, fmt.Errorf("hCaptcha site verify error: %w", err)
	}

	var data struct {
		Success    bool     `json:"success"`
		Hostname   string   `json:"hostname"`
		ErrorCodes []string `json:"error-codes"`
	}
	err = json.NewDecoder(res.Body).Decode(&data)
	if err != nil {
		return false, fmt.Errorf("hCaptcha site verify: JSON decode error: %w", err)
	}
	if res.StatusCode != http.StatusOK {
		return false, fmt.Errorf("hCaptcha site verify: error codes %s, status %d", strings.Join(data.ErrorCodes, ", "), res.StatusCode)
	}

	result := data.Success && data.Hostname == r.URL.Hostname()
	return result, nil
}

// NewHcaptcha returns an antispam implementation using the hCaptcha service.
func NewHcaptcha(siteKey string, secretKey string) *HCaptcha {
	return newHcaptcha(siteKey, secretKey, "https://hcaptcha.com")
}

func newHcaptcha(siteKey string, secretKey string, endpoint string) *HCaptcha {
	formField := `<div class="h-captcha" data-sitekey="` + template.HTMLEscapeString(siteKey) + `"></div>`

	siteverifyEndpoint, err := url.JoinPath(endpoint, "siteverify")
	if err != nil {
		log.Panicf("error constructing siteverify endpoint: %v", err)
	}

	return &HCaptcha{
		formField:          template.HTML(formField),
		secretKey:          secretKey,
		siteverifyEndpoint: siteverifyEndpoint,
	}
}
