package wechat

import (
	"context"
	"encoding/json"
	"net/http"
	"net/url"
	"time"

	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"go.uber.org/zap"
)

const miniAppCode2SessionURL = "https://api.weixin.qq.com/sns/jscode2session"

// MiniAppServiceInterface 微信小程序服务接口
type MiniAppServiceInterface interface {
	Code2Session(ctx context.Context, code string) (openID, unionID, sessionKey string, err error)
}

// MiniAppService 微信小程序服务实现
type MiniAppService struct {
	appID     string
	appSecret string
	client    *http.Client
	logger    *zap.Logger
}

// NewMiniAppService 创建微信小程序服务
func NewMiniAppService(appID, appSecret string, logger *zap.Logger) *MiniAppService {
	return &MiniAppService{
		appID:     appID,
		appSecret: appSecret,
		client: &http.Client{
			Timeout: 10 * time.Second,
		},
		logger: logger,
	}
}

type code2SessionResponse struct {
	OpenID     string `json:"openid"`
	UnionID    string `json:"unionid"`
	SessionKey string `json:"session_key"`
	ErrCode    int    `json:"errcode"`
	ErrMsg     string `json:"errmsg"`
}

// Code2Session 通过小程序 code 换取 openid/session_key
func (s *MiniAppService) Code2Session(ctx context.Context, code string) (openID, unionID, sessionKey string, err error) {
	if code == "" {
		return "", "", "", errors.New(errors.CodeInvalidParam, "授权码不能为空")
	}
	if s.appID == "" || s.appSecret == "" {
		return "", "", "", errors.New(errors.CodeWechatConfigUnavailable, "小程序配置不完整")
	}

	params := url.Values{}
	params.Set("appid", s.appID)
	params.Set("secret", s.appSecret)
	params.Set("js_code", code)
	params.Set("grant_type", "authorization_code")

	req, reqErr := http.NewRequestWithContext(ctx, http.MethodGet, miniAppCode2SessionURL+"?"+params.Encode(), nil)
	if reqErr != nil {
		s.logger.Error("构建小程序 Code2Session 请求失败", zap.Error(reqErr))
		return "", "", "", errors.Wrap(errors.CodeWechatOAuthFailed, reqErr, "构建微信请求失败")
	}

	resp, doErr := s.client.Do(req)
	if doErr != nil {
		s.logger.Error("调用小程序 Code2Session 失败", zap.Error(doErr))
		return "", "", "", errors.Wrap(errors.CodeWechatOAuthFailed, doErr, "调用微信接口失败")
	}
	defer resp.Body.Close()

	var result code2SessionResponse
	if decodeErr := json.NewDecoder(resp.Body).Decode(&result); decodeErr != nil {
		s.logger.Error("解析小程序 Code2Session 响应失败", zap.Error(decodeErr))
		return "", "", "", errors.Wrap(errors.CodeWechatOAuthFailed, decodeErr, "解析微信响应失败")
	}

	if result.ErrCode != 0 {
		s.logger.Error("小程序 Code2Session 返回错误",
			zap.Int("errcode", result.ErrCode),
			zap.String("errmsg", result.ErrMsg),
		)
		return "", "", "", errors.New(errors.CodeWechatOAuthFailed)
	}

	if result.OpenID == "" || result.SessionKey == "" {
		s.logger.Error("小程序 Code2Session 响应缺少关键字段", zap.String("open_id", result.OpenID))
		return "", "", "", errors.New(errors.CodeWechatOAuthFailed, "微信返回数据不完整")
	}

	return result.OpenID, result.UnionID, result.SessionKey, nil
}
