package wx

import (
	"errors"
	"fmt"
	"github.com/medivhzhan/weapp/v2"
)

const (
	apiLogin          = "/sns/jscode2session"
	apiGetAccessToken = "/cgi-bin/token"
	apiGetPaidUnionID = "/wxa/getpaidunionid"
)

// LoginResponse 返回给用户的数据
type LoginResponse struct {
	CommonError
	OpenID     string `json:"openid"`
	SessionKey string `json:"session_key"`
	// 用户在开放平台的唯一标识符 只在关注公众号的前提下
	UnionID string `json:"unionid"`
	Mobile  string `json:"mobile"`
}

// Login 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。
//
// appID 小程序 appID
// secret 小程序的 app secret
// code 小程序登录时获取的 code
func GetWxLoginRes(appID, secret, code, encryptedDate, iv string) (*LoginResponse, error) {
	api := baseURL + apiLogin
	loginResponse, err := getWxLoginRes(appID, secret, code, api)
	if err != nil {
		fmt.Println("GetWxLoginRes ERR:", err)
		return nil, err
	}
	if loginResponse.CommonError.ErrCode != 0 {
		fmt.Println("GetWxLoginRes ERR2:", loginResponse.CommonError.ErrCode)
		return nil, errors.New("code过期：" + loginResponse.CommonError.ErrMSG)
	}

	mobile, err := weapp.DecryptMobile(loginResponse.SessionKey, encryptedDate, iv)
	if err != nil {
		fmt.Println("GetWxLoginRes ERR3:", err)
		return nil, err
	}
	loginResponse.Mobile = mobile.PhoneNumber
	return loginResponse, nil
}

func GetOpenId(appID, secret, code string) (string, error) {
	api := baseURL + apiLogin
	queries := requestQueries{
		"appid":      appID,
		"secret":     secret,
		"js_code":    code,
		"grant_type": "authorization_code",
	}
	url, err := encodeURL(api, queries)
	if err != nil {
		fmt.Println("GetOpenId Err1:", err)
		return "", err
	}
	res := new(LoginResponse)
	if err := getJSON(url, res); err != nil {
		fmt.Println("GetOpenId Err2:", err)
		return "", err
	}
	fmt.Println("GetOpenId resp:", res)

	return res.OpenID, nil
}

func getWxLoginRes(appID, secret, code, api string) (*LoginResponse, error) {
	queries := requestQueries{
		"appid":      appID,
		"secret":     secret,
		"js_code":    code,
		"grant_type": "authorization_code",
	}
	url, err := encodeURL(api, queries)
	if err != nil {
		return nil, err
	}

	res := new(LoginResponse)
	if err := getJSON(url, res); err != nil {
		return nil, err
	}

	return res, nil
}

// TokenResponse 获取 access_token 成功返回数据
type TokenResponse struct {
	CommonError
	AccessToken string `json:"access_token"` // 获取到的凭证
	ExpiresIn   uint   `json:"expires_in"`   // 凭证有效时间，单位：秒。目前是7200秒之内的值。
}

// GetAccessToken 获取小程序全局唯一后台接口调用凭据（access_token）。
// 调调用绝大多数后台接口时都需使用 access_token，开发者需要进行妥善保存，注意缓存。
func GetAccessToken(appID, secret string) (*TokenResponse, error) {
	api := baseURL + apiGetAccessToken
	return getAccessToken(appID, secret, api)
}

func getAccessToken(appID, secret, api string) (*TokenResponse, error) {

	queries := requestQueries{
		"appid":      appID,
		"secret":     secret,
		"grant_type": "client_credential",
	}

	url, err := encodeURL(api, queries)
	if err != nil {
		return nil, err
	}

	res := new(TokenResponse)
	if err := getJSON(url, res); err != nil {
		return nil, err
	}

	return res, nil
}

// GetPaidUnionIDResponse response data
type GetPaidUnionIDResponse struct {
	CommonError
	UnionID string `json:"unionid"`
}

// GetPaidUnionID 用户支付完成后，通过微信支付订单号（transaction_id）获取该用户的 UnionId，
func GetPaidUnionID(accessToken, openID, transactionID string) (*GetPaidUnionIDResponse, error) {
	api := baseURL + apiGetPaidUnionID
	return getPaidUnionID(accessToken, openID, transactionID, api)
}

func getPaidUnionID(accessToken, openID, transactionID, api string) (*GetPaidUnionIDResponse, error) {
	queries := requestQueries{
		"openid":         openID,
		"access_token":   accessToken,
		"transaction_id": transactionID,
	}

	return getPaidUnionIDRequest(api, queries)
}

// GetPaidUnionIDWithMCH 用户支付完成后，通过微信支付商户订单号和微信支付商户号（out_trade_no 及 mch_id）获取该用户的 UnionId，
func GetPaidUnionIDWithMCH(accessToken, openID, outTradeNo, mchID string) (*GetPaidUnionIDResponse, error) {
	api := baseURL + apiGetPaidUnionID
	return getPaidUnionIDWithMCH(accessToken, openID, outTradeNo, mchID, api)
}

func getPaidUnionIDWithMCH(accessToken, openID, outTradeNo, mchID, api string) (*GetPaidUnionIDResponse, error) {
	queries := requestQueries{
		"openid":       openID,
		"mch_id":       mchID,
		"out_trade_no": outTradeNo,
		"access_token": accessToken,
	}

	return getPaidUnionIDRequest(api, queries)
}

func getPaidUnionIDRequest(api string, queries requestQueries) (*GetPaidUnionIDResponse, error) {
	url, err := encodeURL(api, queries)
	if err != nil {
		return nil, err
	}

	res := new(GetPaidUnionIDResponse)
	if err := getJSON(url, res); err != nil {
		return nil, err
	}

	return res, nil
}
