/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package recaptcha

import (
	"embed"
	"github.com/apache/answer-plugins/util"
	"io"
	"net/http"
	"time"

	"github.com/apache/answer-plugins/captcha-google-v2/i18n"
	"github.com/apache/answer/plugin"

	"encoding/json"

	"github.com/segmentfault/pacman/log"
)

//go:embed  info.yaml
var Info embed.FS

type Captcha struct {
	Config *CaptchaConfig
}

type CaptchaConfig struct {
	SiteKey            string `json:"site_key"`
	SecretKey          string `json:"secret_key"`
	SiteVerifyEndpoint string `json:"site_verify_endpoint"`
}

type GoogleCaptchaResponse struct {
	Success    bool     `json:"success"`
	ErrorCodes []string `json:"error-codes"`
}

func init() {
	plugin.Register(&Captcha{
		Config: &CaptchaConfig{},
	})
}

func (c *Captcha) Info() plugin.Info {
	info := &util.Info{}
	info.GetInfo(Info)

	return plugin.Info{
		Name:        plugin.MakeTranslator(i18n.InfoName),
		SlugName:    info.SlugName,
		Description: plugin.MakeTranslator(i18n.InfoDescription),
		Author:      info.Author,
		Version:     info.Version,
		Link:        info.Link,
	}
}

func (c *Captcha) GetConfig() (config string) {
	data, _ := json.Marshal(map[string]interface{}{
		"key": c.Config.SiteKey,
	})
	return string(data)
}

func (c *Captcha) Create() (captcha, code string) {
	return "", ""
}

func (c *Captcha) Verify(captcha, userInput string) (pass bool) {
	if len(userInput) == 0 {
		return false
	}
	cli := &http.Client{}
	cli.Timeout = 10 * time.Second
	siteVerifyEndpoint := c.Config.SiteVerifyEndpoint
	if siteVerifyEndpoint == "" {
		siteVerifyEndpoint = "https://www.google.com/recaptcha/api/siteverify"
	}
	resp, err := cli.PostForm(siteVerifyEndpoint, map[string][]string{
		"secret":   {c.Config.SecretKey},
		"response": {userInput},
	})
	if err != nil {
		log.Errorf("verify captcha error %s", err.Error())
		return false
	}
	defer resp.Body.Close()
	all, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("verify captcha, read body error %s", err.Error())
		return false
	}
	r := &GoogleCaptchaResponse{}
	_ = json.Unmarshal(all, r)
	if r.Success {
		return true
	}
	log.Debugf("user input is wrong %s, user input is %s", string(all), userInput)
	return false
}

func (c *Captcha) ConfigFields() []plugin.ConfigField {
	return []plugin.ConfigField{
		{
			Name:        "site_key",
			Type:        plugin.ConfigTypeInput,
			Title:       plugin.MakeTranslator(i18n.ConfigSiteKeyTitle),
			Description: plugin.MakeTranslator(i18n.ConfigSiteKeyDescription),
			Required:    true,
			UIOptions: plugin.ConfigFieldUIOptions{
				InputType: plugin.InputTypeText,
			},
			Value: c.Config.SiteKey,
		},
		{
			Name:        "secret_key",
			Type:        plugin.ConfigTypeInput,
			Title:       plugin.MakeTranslator(i18n.ConfigSecretKeyTitle),
			Description: plugin.MakeTranslator(i18n.ConfigSecretKeyDescription),
			Required:    true,
			UIOptions: plugin.ConfigFieldUIOptions{
				InputType: plugin.InputTypeText,
			},
			Value: c.Config.SecretKey,
		},
		{
			Name:        "site_verify_endpoint",
			Type:        plugin.ConfigTypeInput,
			Title:       plugin.MakeTranslator(i18n.ConfigSiteVerifyEndpointTitle),
			Description: plugin.MakeTranslator(i18n.ConfigSiteVerifyEndpointDescription),
			Required:    false,
			UIOptions: plugin.ConfigFieldUIOptions{
				InputType: plugin.InputTypeText,
			},
			Value: c.Config.SiteVerifyEndpoint,
		},
	}
}

func (c *Captcha) ConfigReceiver(config []byte) error {
	conf := &CaptchaConfig{}
	_ = json.Unmarshal(config, conf)
	c.Config = conf
	return nil
}
