// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

// https://github.com/go-gitea/gitea/pull/37492#issuecomment-4355482585
// End users need it "since it is the only one open-source option in Gitea besides the image captcha"

package mcaptcha

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"strings"

	"gitea.dev/modules/json"
	"gitea.dev/modules/setting"
	"gitea.dev/modules/util"
)

func Verify(ctx context.Context, token string) (bool, error) {
	c := &client{
		ServerURL: setting.Service.McaptchaURL,
		SiteKey:   setting.Service.McaptchaSitekey,
		Secret:    setting.Service.McaptchaSecret,
		Token:     token,
	}
	return c.Verify(ctx)
}

type client struct {
	ServerURL string

	Secret  string
	SiteKey string
	Token   string
}

func (c *client) Verify(ctx context.Context) (bool, error) {
	verifyURL := strings.TrimSuffix(c.ServerURL, "/") + "/api/v1/pow/siteverify"
	reqParams := map[string]string{"secret": c.Secret, "key": c.SiteKey, "token": c.Token}
	reqBody, _ := json.Marshal(reqParams)
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, verifyURL, bytes.NewReader(reqBody))
	if err != nil {
		return false, fmt.Errorf("unable to create verify request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return false, fmt.Errorf("unable to complete verify request: %w", err)
	}
	defer res.Body.Close()

	respContent, err := io.ReadAll(io.LimitReader(res.Body, 16*1024))
	if err != nil {
		return false, fmt.Errorf("unable to read response body: %w", err)
	}
	if res.StatusCode != http.StatusOK {
		return false, fmt.Errorf("unexpected response status %d: %q", res.StatusCode, util.TruncateRunes(util.UnsafeBytesToString(respContent), 100))
	}

	var resp struct {
		Valid bool `json:"valid"`
	}
	err = json.Unmarshal(respContent, &resp)
	if err != nil {
		return false, fmt.Errorf("unable to decode response: %w", err)
	}
	return resp.Valid, nil
}
