package identity

import (
	"bytes"
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"net/url"
	"time"

	"qycw/go-server/internal/config"
)

func verifyCaptcha(ctx context.Context, cfg config.Bootstrap, logger *slog.Logger, payload *GeetestPayload) error {
	if cfg.GeetestCaptchaID == "" {
		return nil
	}
	if payload == nil || payload.LotNumber == "" || payload.CaptchaOutput == "" || payload.PassToken == "" || payload.GenTime == "" {
		return BadRequest("请先完成人机验证")
	}
	sign := hmac.New(sha256.New, []byte(cfg.GeetestCaptchaKey))
	sign.Write([]byte(payload.LotNumber))
	form := url.Values{}
	form.Set("lot_number", payload.LotNumber)
	form.Set("captcha_output", payload.CaptchaOutput)
	form.Set("pass_token", payload.PassToken)
	form.Set("gen_time", payload.GenTime)
	form.Set("sign_token", hex.EncodeToString(sign.Sum(nil)))

	requestCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
	apiURL := fmt.Sprintf("%s/validate?captcha_id=%s", cfg.GeetestAPIServer, url.QueryEscape(cfg.GeetestCaptchaID))
	req, err := http.NewRequestWithContext(requestCtx, http.MethodPost, apiURL, bytes.NewBufferString(form.Encode()))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return handleCaptchaUnavailable(cfg, logger, fmt.Sprintf("geetest request failed: %v", err))
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		_, _ = io.Copy(io.Discard, resp.Body)
		return handleCaptchaUnavailable(cfg, logger, fmt.Sprintf("geetest non-200: %d", resp.StatusCode))
	}
	var body struct {
		Result string `json:"result"`
		Reason string `json:"reason"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
		return handleCaptchaUnavailable(cfg, logger, fmt.Sprintf("geetest decode failed: %v", err))
	}
	if body.Result != "success" {
		reason := body.Reason
		if reason == "" {
			reason = "人机验证未通过"
		}
		return BadRequest("人机验证失败：" + reason)
	}
	return nil
}

func handleCaptchaUnavailable(cfg config.Bootstrap, logger *slog.Logger, message string) error {
	if cfg.GeetestFailOpen {
		logger.Warn(message, "fail_open", true)
		return nil
	}
	logger.Warn(message, "fail_open", false)
	return BadRequest("人机验证服务暂时不可用，请稍后重试")
}
