package middleware

import (
	"bytes"
	"encoding/json"
	model "github.com/cloudreve/Cloudreve/v3/models"
	"github.com/cloudreve/Cloudreve/v3/pkg/recaptcha"
	"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
	"github.com/cloudreve/Cloudreve/v3/pkg/util"
	"github.com/gin-gonic/gin"
	"github.com/mojocn/base64Captcha"
	captcha "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/captcha/v20190722"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	"io"
	"io/ioutil"
	"strconv"
	"time"
)

type req struct {
	CaptchaCode string `json:"captchaCode"`
	Ticket      string `json:"ticket"`
	Randstr     string `json:"randstr"`
}

const (
	captchaNotMatch = "CAPTCHA not match."
	captchaRefresh  = "Verification failed, please refresh the page and retry."
)

// CaptchaRequired 验证请求签名
func CaptchaRequired(configName string) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 相关设定
		options := model.GetSettingByNames(configName,
			"captcha_type",
			"captcha_ReCaptchaSecret",
			"captcha_TCaptcha_SecretId",
			"captcha_TCaptcha_SecretKey",
			"captcha_TCaptcha_CaptchaAppId",
			"captcha_TCaptcha_AppSecretKey")
		// 检查验证码
		isCaptchaRequired := model.IsTrueVal(options[configName])

		if isCaptchaRequired {
			var service req
			bodyCopy := new(bytes.Buffer)
			_, err := io.Copy(bodyCopy, c.Request.Body)
			if err != nil {
				c.JSON(200, serializer.Err(serializer.CodeCaptchaError, captchaNotMatch, err))
				c.Abort()
				return
			}

			bodyData := bodyCopy.Bytes()
			err = json.Unmarshal(bodyData, &service)
			if err != nil {
				c.JSON(200, serializer.Err(serializer.CodeCaptchaError, captchaNotMatch, err))
				c.Abort()
				return
			}

			c.Request.Body = ioutil.NopCloser(bytes.NewReader(bodyData))
			switch options["captcha_type"] {
			case "normal":
				captchaID := util.GetSession(c, "captchaID")
				util.DeleteSession(c, "captchaID")
				if captchaID == nil || !base64Captcha.VerifyCaptcha(captchaID.(string), service.CaptchaCode) {
					c.JSON(200, serializer.Err(serializer.CodeCaptchaError, captchaNotMatch, err))
					c.Abort()
					return
				}

				break
			case "recaptcha":
				reCAPTCHA, err := recaptcha.NewReCAPTCHA(options["captcha_ReCaptchaSecret"], recaptcha.V2, 10*time.Second)
				if err != nil {
					util.Log().Warning("reCAPTCHA verification failed, %s", err)
					c.Abort()
					break
				}

				err = reCAPTCHA.Verify(service.CaptchaCode)
				if err != nil {
					util.Log().Warning("reCAPTCHA verification failed, %s", err)
					c.JSON(200, serializer.Err(serializer.CodeCaptchaRefreshNeeded, captchaRefresh, nil))
					c.Abort()
					return
				}

				break
			case "tcaptcha":
				credential := common.NewCredential(
					options["captcha_TCaptcha_SecretId"],
					options["captcha_TCaptcha_SecretKey"],
				)
				cpf := profile.NewClientProfile()
				cpf.HttpProfile.Endpoint = "captcha.tencentcloudapi.com"
				client, _ := captcha.NewClient(credential, "", cpf)
				request := captcha.NewDescribeCaptchaResultRequest()
				request.CaptchaType = common.Uint64Ptr(9)
				appid, _ := strconv.Atoi(options["captcha_TCaptcha_CaptchaAppId"])
				request.CaptchaAppId = common.Uint64Ptr(uint64(appid))
				request.AppSecretKey = common.StringPtr(options["captcha_TCaptcha_AppSecretKey"])
				request.Ticket = common.StringPtr(service.Ticket)
				request.Randstr = common.StringPtr(service.Randstr)
				request.UserIp = common.StringPtr(c.ClientIP())
				response, err := client.DescribeCaptchaResult(request)
				if err != nil {
					util.Log().Warning("TCaptcha verification failed, %s", err)
					c.Abort()
					break
				}

				if *response.Response.CaptchaCode != int64(1) {
					c.JSON(200, serializer.Err(serializer.CodeCaptchaRefreshNeeded, captchaRefresh, nil))
					c.Abort()
					return
				}

				break
			}
		}
		c.Next()
	}
}
