package user

import (
	"context"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"

	"tydata-server/app/main/api/internal/svc"
	"tydata-server/app/main/api/internal/types"
	"tydata-server/common/xerr"

	"github.com/pkg/errors"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetSignatureLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewGetSignatureLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSignatureLogic {
	return &GetSignatureLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *GetSignatureLogic) GetSignature(req *types.GetSignatureReq) (resp *types.GetSignatureResp, err error) {
	// 1. 获取access_token
	accessToken, err := l.getAccessToken()
	if err != nil {
		l.Errorf("获取access_token失败: %v", err)
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取access_token失败: %v", err)
	}

	// 2. 获取jsapi_ticket
	jsapiTicket, err := l.getJsapiTicket(accessToken)
	if err != nil {
		l.Errorf("获取jsapi_ticket失败: %v", err)
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取jsapi_ticket失败: %v", err)
	}

	// 3. 生成签名
	timestamp := time.Now().Unix()
	nonceStr := l.generateNonceStr(16)
	signature := l.generateSignature(jsapiTicket, nonceStr, timestamp, req.Url)

	// 4. 返回完整的JS-SDK配置信息
	return &types.GetSignatureResp{
		AppId:     l.svcCtx.Config.WechatH5.AppID,
		Timestamp: timestamp,
		NonceStr:  nonceStr,
		Signature: signature,
	}, nil
}

// getAccessToken 获取微信公众号access_token
func (l *GetSignatureLogic) getAccessToken() (string, error) {
	appID := l.svcCtx.Config.WechatH5.AppID
	appSecret := l.svcCtx.Config.WechatH5.AppSecret

	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appID, appSecret)

	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	var result struct {
		AccessToken string `json:"access_token"`
		ExpiresIn   int    `json:"expires_in"`
		ErrCode     int    `json:"errcode"`
		ErrMsg      string `json:"errmsg"`
	}

	if err = json.Unmarshal(body, &result); err != nil {
		return "", err
	}

	if result.ErrCode != 0 {
		return "", fmt.Errorf("获取access_token失败: errcode=%d, errmsg=%s", result.ErrCode, result.ErrMsg)
	}

	return result.AccessToken, nil
}

// getJsapiTicket 获取jsapi_ticket
func (l *GetSignatureLogic) getJsapiTicket(accessToken string) (string, error) {
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi", accessToken)

	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	var result struct {
		Ticket    string `json:"ticket"`
		ExpiresIn int    `json:"expires_in"`
		ErrCode   int    `json:"errcode"`
		ErrMsg    string `json:"errmsg"`
	}

	if err = json.Unmarshal(body, &result); err != nil {
		return "", err
	}

	if result.ErrCode != 0 {
		return "", fmt.Errorf("获取jsapi_ticket失败: errcode=%d, errmsg=%s", result.ErrCode, result.ErrMsg)
	}

	return result.Ticket, nil
}

// generateNonceStr 生成随机字符串
func (l *GetSignatureLogic) generateNonceStr(length int) string {
	chars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	result := make([]byte, length)
	for i := 0; i < length; i++ {
		result[i] = chars[i%len(chars)]
	}
	return string(result)
}

// generateSignature 生成签名
func (l *GetSignatureLogic) generateSignature(jsapiTicket, nonceStr string, timestamp int64, urlStr string) string {
	// 对URL进行解码，避免重复编码
	decodedURL, err := url.QueryUnescape(urlStr)
	if err != nil {
		decodedURL = urlStr
	}

	// 构建签名字符串
	params := map[string]string{
		"jsapi_ticket": jsapiTicket,
		"noncestr":     nonceStr,
		"timestamp":    fmt.Sprintf("%d", timestamp),
		"url":          decodedURL,
	}

	// 对参数进行字典序排序
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// 拼接字符串
	var signStr strings.Builder
	for i, k := range keys {
		if i > 0 {
			signStr.WriteString("&")
		}
		signStr.WriteString(k)
		signStr.WriteString("=")
		signStr.WriteString(params[k])
	}

	// SHA1加密
	h := sha1.New()
	h.Write([]byte(signStr.String()))
	signature := fmt.Sprintf("%x", h.Sum(nil))

	return signature
}
