package notify

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"bindbox-game/internal/pkg/httpclient"
	pkgutils "bindbox-game/internal/pkg/utils"

	"go.uber.org/zap"
)

// WechatNotifyConfig 微信通知配置
type WechatNotifyConfig struct {
	AppID                   string
	AppSecret               string
	LotteryResultTemplateID string
}

// LotteryResultNotificationRequest 开奖结果通知请求结构
type LotteryResultNotificationRequest struct {
	Touser           string                        `json:"touser"`
	TemplateID       string                        `json:"template_id"`
	Page             string                        `json:"page"`
	MiniprogramState string                        `json:"miniprogram_state"`
	Lang             string                        `json:"lang"`
	Data             LotteryResultNotificationData `json:"data"`
}

// LotteryResultNotificationData 开奖结果通知数据字段
// 使用 map 支持动态字段类型，根据模板灵活配置
type LotteryResultNotificationData map[string]DataValue

// DataValue 数据值包装
type DataValue struct {
	Value string `json:"value"`
}

// LotteryResultNotificationResponse 发送结果响应
type LotteryResultNotificationResponse struct {
	Errcode int    `json:"errcode"`
	Errmsg  string `json:"errmsg"`
}

// AccessTokenResponse access_token 响应
type AccessTokenResponse struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
	ErrCode     int    `json:"errcode,omitempty"`
	ErrMsg      string `json:"errmsg,omitempty"`
}

// getAccessToken 获取微信 access_token
func getAccessToken(ctx context.Context, appID, appSecret string) (string, error) {
	url := "https://api.weixin.qq.com/cgi-bin/token"
	client := httpclient.GetHttpClient()
	resp, err := client.R().
		SetQueryParams(map[string]string{
			"grant_type": "client_credential",
			"appid":      appID,
			"secret":     appSecret,
		}).
		Get(url)
	if err != nil {
		return "", fmt.Errorf("获取access_token失败: %v", err)
	}
	if resp.StatusCode() != http.StatusOK {
		return "", fmt.Errorf("HTTP请求失败，状态码: %d", resp.StatusCode())
	}
	var tokenResp AccessTokenResponse
	if err := json.Unmarshal(resp.Body(), &tokenResp); err != nil {
		return "", fmt.Errorf("解析access_token响应失败: %v", err)
	}
	if tokenResp.ErrCode != 0 {
		return "", fmt.Errorf("获取access_token失败: errcode=%d, errmsg=%s", tokenResp.ErrCode, tokenResp.ErrMsg)
	}
	if tokenResp.AccessToken == "" {
		return "", fmt.Errorf("获取到的access_token为空")
	}
	return tokenResp.AccessToken, nil
}

// SendLotteryResultNotification 发送开奖结果订阅消息
// ctx: context
// cfg: 微信通知配置
// openid: 用户 openid
// activityName: 活动名称
// rewardNames: 中奖奖品列表
// orderNo: 订单编号
// drawTime: 开奖时间
func SendLotteryResultNotification(ctx context.Context, cfg *WechatNotifyConfig, openid string, activityName string, rewardNames []string, orderNo string, drawTime time.Time) error {
	if cfg == nil || cfg.LotteryResultTemplateID == "" {
		fmt.Printf("[开奖通知] 模板ID未配置，跳过发送 openid=%s\n", openid)
		return nil
	}
	if openid == "" {
		fmt.Printf("[开奖通知] openid为空，跳过发送\n")
		return nil
	}

	// 获取 access_token
	accessToken, err := getAccessToken(ctx, cfg.AppID, cfg.AppSecret)
	if err != nil {
		zap.L().Error("[开奖通知] 获取access_token失败", zap.Error(err), zap.String("openid", openid))
		return err
	}
	// 活动名称限制长度（thing类型不超过20个字符）
	activityName = pkgutils.TruncateRunes(activityName, 20)

	// 活动结果：展示奖品列表
	rewardsStr := strings.Join(rewardNames, ",")
	if rewardsStr == "" {
		rewardsStr = "无奖励"
	}
	// thing类型限制20字符
	resultVal := pkgutils.TruncateRunes(rewardsStr, 20)

	// 当前进度：固定为"已发货"
	progress := "已发货"

	// 使用模板字段：thing6=活动名称, thing8=当前进度, thing9=活动结果
	req := &LotteryResultNotificationRequest{
		Touser:           openid,
		TemplateID:       cfg.LotteryResultTemplateID,
		Page:             "pages/mine/index", // 点击跳转到"我的"页面
		MiniprogramState: "formal",           // 正式版
		Lang:             "zh_CN",
		Data: LotteryResultNotificationData{
			"thing6": {Value: activityName}, // 活动名称
			"thing8": {Value: progress},     // 当前进度
			"thing9": {Value: resultVal},    // 活动结果
		},
	}

	zap.L().Info("[开奖通知] 尝试发送", zap.String("openid", openid), zap.String("activity", activityName), zap.Strings("rewards", rewardNames))

	// 发送请求
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%s", accessToken)
	client := httpclient.GetHttpClient()
	resp, err := client.R().
		SetHeader("Content-Type", "application/json").
		SetBody(req).
		Post(url)
	if err != nil {
		zap.L().Error("[开奖通知] 发送失败", zap.Error(err), zap.String("openid", openid))
		return err
	}

	var result LotteryResultNotificationResponse
	if err := json.Unmarshal(resp.Body(), &result); err != nil {
		zap.L().Error("[开奖通知] 解析响应失败", zap.Error(err), zap.String("body", string(resp.Body())))
		return err
	}

	if result.Errcode != 0 {
		// 常见错误码：
		// 43101: 用户拒绝接受消息
		// 47003: 模板参数不准确
		zap.L().Warn("[开奖通知] 发送失败", zap.Int("errcode", result.Errcode), zap.String("errmsg", result.Errmsg), zap.String("openid", openid))
		return fmt.Errorf("发送订阅消息失败: errcode=%d, errmsg=%s", result.Errcode, result.Errmsg)
	}

	zap.L().Info("[开奖通知] ✅ 发送成功", zap.String("openid", openid))
	return nil
}
