package turnstile

import (
	"bytes"
	"context"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"mime/multipart"
	"net/http"
	"time"
)

type service struct {
	timeout time.Duration
	secret  string
	url     string
}

func newService(config Config) Service {
	if config.Timeout == 0 {
		config.Timeout = 10 * time.Second
	}
	return &service{
		secret:  config.Secret,
		timeout: config.Timeout,
		url:     "https://challenges.cloudflare.com/turnstile/v0/siteverify",
	}
}

func (s *service) Verify(ctx context.Context, token string, ip string) (bool, error) {
	return s.verify(ctx, s.secret, token, ip, "")
}

func (s *service) VerifyIdempotent(ctx context.Context, token string, ip string, key string) (bool, error) {
	return s.verify(ctx, s.secret, token, ip, key)
}

func (s *service) RandomUUID() string {
	uuid := make([]byte, 16)
	_, _ = rand.Read(uuid)
	uuid[6] = (uuid[6] & 0x0f) | 0x40
	uuid[8] = (uuid[8] & 0x3f) | 0x80
	return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
}

func (s *service) verify(ctx context.Context, secret string, token string, ip string, key string) (bool, error) {
	_, cancel := context.WithTimeout(ctx, s.timeout)
	defer cancel()
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	_ = writer.WriteField("secret", secret)
	_ = writer.WriteField("response", token)
	_ = writer.WriteField("remoteip", ip)
	if key != "" {
		_ = writer.WriteField("idempotency_key", key)
	}
	_ = writer.Close()
	client := &http.Client{
		Timeout: 5 * time.Second,
	}
	req, _ := http.NewRequest("POST", s.url, body)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	firstResult, err := client.Do(req)
	if err != nil {
		return false, err
	}
	defer firstResult.Body.Close()
	firstOutcome := make(map[string]interface{})
	err = json.NewDecoder(firstResult.Body).Decode(&firstOutcome)
	if err != nil {
		return false, err
	}
	if success, ok := firstOutcome["success"].(bool); ok && success {
		return true, nil
	}
	return false, nil
}
