package ikucun

import (
	"context"
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"github.com/gogf/gf/encoding/gjson"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/util/gconv"
	"math/rand"
	"net/url"
	"sort"
	"strings"
	"time"
)

var server *Config

func New(req *Config) {
	server = req
	return
}

const pkgName = "ikucun"

type Config struct {
	AppId       string
	AppSecret   string
	ApiUrl      string
	AccessToken string
}

// 生成随机 nonce 字符串 (等效 JS 的 makeNonceStr)
func makeNonceStr() string {
	// 原始有效字符集(排除易混淆字符)
	const baseStr = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789"

	// 打乱字符顺序(Go 的稳定洗牌算法)
	shuffled := []rune(baseStr)
	rand.Seed(time.Now().UnixNano())
	rand.Shuffle(len(shuffled), func(i, j int) {
		shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
	})

	// 随机截取 10 位字符
	start := rand.Intn(len(shuffled) - 10)
	return string(shuffled[start : start+10])
}

// 构建签名前字符串 (等效 JS 的 beforeSignBuild)
func beforeSignBuild(nonceStr, interfaceName string, queryParams map[string]string, body string) string {
	// 处理空参数
	if queryParams == nil {
		queryParams = make(map[string]string)
	}

	// 准备 POST 参数
	postParams := make(map[string]string)
	if body != "" {
		postParams["body"] = body
	}

	// 获取公共参数
	appSecret := server.AppSecret
	timestamp := time.Now().Unix() // 直接获取秒级时间戳
	commonParams := map[string]string{
		"appid":         queryParams["appid"],
		"noncestr":      nonceStr,
		"timestamp":     fmt.Sprintf("%d", timestamp),
		"version":       queryParams["version"],
		"appsecret":     appSecret,
		"format":        queryParams["json"],
		"interfaceName": interfaceName,
	}

	// 合并参数(注意合并顺序)
	allParams := make(map[string]string)
	for k, v := range commonParams {
		allParams[k] = v
	}
	for k, v := range queryParams {
		allParams[k] = v
	}
	for k, v := range postParams {
		allParams[k] = v
	}

	// 按键名排序
	keys := make([]string, 0, len(allParams))
	for k := range allParams {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// 构建签名字符串
	var builder strings.Builder
	for i, k := range keys {
		if i > 0 {
			builder.WriteString("&")
		}
		builder.WriteString(fmt.Sprintf("%s=%s", k, allParams[k]))
	}

	return builder.String()
}

func getQueryParams(interfaceName, body string, queryParamsTemp map[string]interface{}) map[string]string {

	// 生成 nonce 并添加到查询参数
	nonceStr := makeNonceStr()
	timestamp := time.Now().Unix() // 直接获取秒级时间戳
	queryParams := map[string]string{
		"appid":         server.AppId,
		"version":       "1.0",
		"format":        "json",
		"timestamp":     fmt.Sprintf("%d", timestamp),
		"interfaceName": interfaceName,
		"accessToken":   server.AccessToken,
		// 其他查询参数...
	}

	if nil != queryParamsTemp {
		for k, v := range queryParamsTemp {
			queryParams[k] = gconv.String(v)
		}
	}

	queryParams["noncestr"] = nonceStr

	// 构建签名前字符串
	signStr := beforeSignBuild(
		nonceStr,
		interfaceName,
		queryParams,
		body,
	)

	// 计算 SHA1 签名
	hasher := sha1.New()
	hasher.Write([]byte(signStr))
	sign := hex.EncodeToString(hasher.Sum(nil))

	// 添加签名到查询参数
	queryParams["sign"] = sign

	//res := gsha1.Encrypt(req)
	return queryParams
}

// 处理 interface{} 类型的值
func mapToSortedQuery(params map[string]string) string {
	if len(params) == 0 {
		return ""
	}

	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	var builder strings.Builder
	for i, k := range keys {
		if i > 0 {
			builder.WriteByte('&')
		}
		builder.WriteString(url.QueryEscape(k))
		builder.WriteByte('=')
		builder.WriteString(url.QueryEscape(params[k]))
	}
	return builder.String()
}

func post(ctx context.Context, method string, req interface{}) (res string, err error) {
	Start := gtime.TimestampMilli()
	param := gjson.New(req)
	queryParam := getQueryParams(method, param.MustToJsonString(), nil)
	Url := server.ApiUrl + "?" + mapToSortedQuery(queryParam)
	Request := g.Client()
	Request.SetHeader("Content-Type", "application/json")
	resp, err := Request.Timeout(time.Second*5).Post(Url, param.MustToJsonString())
	defer func() {
		_ = resp.Close()
		ctx = context.WithValue(ctx, "Method", "POST")
		ctx = context.WithValue(ctx, "URI", Url)
		if err != nil {
			g.Log().Ctx(ctx).Cat(pkgName).Cat("error").Infof("参数【%v】错误【%v】响应时间【%v ms】", param.MustToJsonString(), err.Error(), gtime.TimestampMilli()-Start)
		} else {
			g.Log().Ctx(ctx).Cat(pkgName).Infof("参数【%v】响应【%v】响应时间【%v ms】", param.MustToJsonString(), res, gtime.TimestampMilli()-Start)
		}
	}()
	res = resp.ReadAllString()
	return
}

func get(ctx context.Context, method string, req interface{}) (res string, err error) {
	Start := gtime.TimestampMilli()
	param := gjson.New(req)
	queryParam := getQueryParams(method, "", gconv.Map(req))
	Url := server.ApiUrl + "?" + mapToSortedQuery(queryParam)
	Request := g.Client()
	Request.SetHeader("Content-Type", "application/json")
	resp, err := Request.Timeout(time.Second*5).Get(Url, param.MustToJsonString())
	defer func() {
		_ = resp.Close()
		ctx = context.WithValue(ctx, "Method", "GET")
		ctx = context.WithValue(ctx, "URI", Url)
		if err != nil {
			g.Log().Ctx(ctx).Cat(pkgName).Cat("error").Infof("参数【%v】错误【%v】响应时间【%v ms】", param.MustToJsonString(), err.Error(), gtime.TimestampMilli()-Start)
		} else {
			g.Log().Ctx(ctx).Cat(pkgName).Infof("参数【%v】响应【%v】响应时间【%v ms】", param.MustToJsonString(), res, gtime.TimestampMilli()-Start)
		}
	}()
	res = resp.ReadAllString()
	return
}