// 微信小程序 SDK 封装，使用 silenceper/wechat/v2
package wechat

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"strings"

	myconfig "com.sclktx/m/v2/internal/pkg/config"

	"github.com/silenceper/wechat/v2"
	"github.com/silenceper/wechat/v2/cache"
	miniProgram "github.com/silenceper/wechat/v2/miniprogram"
	"github.com/silenceper/wechat/v2/miniprogram/auth"
	"github.com/silenceper/wechat/v2/miniprogram/subscribe"
	wechatConfig "github.com/silenceper/wechat/v2/miniprogram/config"
	"github.com/sirupsen/logrus"
)

// MiniProgramClient 微信小程序客户端
type MiniProgramClient struct {
	mp *miniProgram.MiniProgram
}

// NewMiniProgramClient 创建小程序客户端
func NewMiniProgramClient(cfg *myconfig.WechatConfig) *MiniProgramClient {
	// 开启 wechat SDK 日志（开发阶段使用 DebugLevel，生产环境可改为 WarnLevel）
	logrus.SetOutput(os.Stdout)
	logrus.SetLevel(logrus.DebugLevel)
	logrus.SetFormatter(&logrus.TextFormatter{
		FullTimestamp:   true,
		TimestampFormat: "2006-01-02 15:04:05",
	})

	wc := wechat.NewWechat()
	// 使用内存缓存 access_token（生产环境可换 Redis）
	memoryCache := cache.NewMemory()

	mp := wc.GetMiniProgram(&wechatConfig.Config{
		AppID:     cfg.AppID,
		AppSecret: cfg.AppSecret,
		Cache:     memoryCache,
	})

	return &MiniProgramClient{mp: mp}
}

// Code2Session 用 wx.login 返回的 code 换取 openid 和 session_key
func (c *MiniProgramClient) Code2Session(code string) (*auth.ResCode2Session, error) {
	result, err := c.mp.GetAuth().Code2Session(code)
	if err != nil {
		return nil, fmt.Errorf("code2session failed: %w", err)
	}
	if result.ErrCode != 0 {
		return nil, fmt.Errorf("code2session error: %d %s", result.ErrCode, result.ErrMsg)
	}
	return &result, nil
}

// DecryptPhone 解密手机号（需 session_key + encrypted_data + iv）
func (c *MiniProgramClient) DecryptPhone(sessionKey, encryptedData, iv string) (string, error) {
	phoneInfo, err := c.mp.GetEncryptor().Decrypt(sessionKey, encryptedData, iv)
	if err != nil {
		return "", fmt.Errorf("decrypt phone failed: %w", err)
	}
	return phoneInfo.PhoneNumber, nil
}

// GetAccessToken 获取小程序 access_token
func (c *MiniProgramClient) GetAccessToken() (string, error) {
	token, err := c.mp.GetContext().GetAccessToken()
	if err != nil {
		return "", fmt.Errorf("get access_token failed: %w", err)
	}
	return token, nil
}

// UploadImgResponse 微信上传图片接口返回结构
type UploadImgResponse struct {
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
	URL     string `json:"url"` // 微信返回的图片 URL
}

// UploadImg 上传图片到微信服务器（作为永久素材）
// imageData: 图片二进制数据
// filename: 文件名
func (c *MiniProgramClient) UploadImg(imageData []byte, filename string) (*UploadImgResponse, error) {
	// 获取 access_token
	accessToken, err := c.GetAccessToken()
	if err != nil {
		return nil, err
	}

	// 构造 multipart/form-data 请求
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	// 添加 media 字段（微信要求的字段名）
	part, err := writer.CreateFormFile("media", filename)
	if err != nil {
		return nil, fmt.Errorf("create form file failed: %w", err)
	}
	if _, err := part.Write(imageData); err != nil {
		return nil, fmt.Errorf("write image data failed: %w", err)
	}

	if err := writer.Close(); err != nil {
		return nil, fmt.Errorf("close writer failed: %w", err)
	}

	// 发送请求
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=%s", accessToken)
	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, fmt.Errorf("create request failed: %w", err)
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("upload img request failed: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read response failed: %w", err)
	}

	var result UploadImgResponse
	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("parse response failed: %w, body: %s", err, string(respBody))
	}

	if result.ErrCode != 0 {
		return nil, fmt.Errorf("wechat upload img error: %d %s", result.ErrCode, result.ErrMsg)
	}

	// 微信返回的 URL 是 http:// 协议，统一替换为 https://
	result.URL = strings.Replace(result.URL, "http://", "https://", 1)

	return &result, nil
}

// SendSubscribeMessage 发送微信订阅消息
// openid: 接收用户的 openid
// templateID: 模板 ID
// data: 模板数据，key 为模板字段名（如 thing1, date2），value 为字段值
func (c *MiniProgramClient) SendSubscribeMessage(openid, templateID string, data map[string]string, page string) error {
	msg := &subscribe.Message{
		ToUser:     openid,
		TemplateID: templateID,
		Page:       page,
		Data:       make(map[string]*subscribe.DataItem),
	}
	for k, v := range data {
		msg.Data[k] = &subscribe.DataItem{Value: v}
	}
	err := c.mp.GetSubscribe().Send(msg)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"openid":      openid,
			"template_id": templateID,
		}).Errorf("微信订阅消息推送失败: %v", err)
	} else {
		logrus.WithFields(logrus.Fields{
			"openid":      openid,
			"template_id": templateID,
		}).Info("微信订阅消息推送成功")
	}
	return err
}
