package providers

import (
	"context"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

// AlipayProvider 支付宝 OAuth提供者
// 支付宝使用 RSA2 签名（SHA256withRSA）
type AlipayProvider struct {
	AppID       string
	PrivateKey  string // RSA2 私钥（PKCS#8 PEM格式）
	RedirectURI string
	IsSandbox   bool
}

// AlipayTokenResponse 支付宝 Token响应
type AlipayTokenResponse struct {
	UserID       string `json:"user_id"`
	AccessToken  string `json:"access_token"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
}

// AlipayUserInfo 支付宝用户信息
type AlipayUserInfo struct {
	UserID   string `json:"user_id"`
	Nickname string `json:"nick_name"`
	Avatar   string `json:"avatar"`
	Gender   string `json:"gender"`
}

// NewAlipayProvider 创建支付宝 OAuth提供者
func NewAlipayProvider(appID, privateKey, redirectURI string, isSandbox bool) *AlipayProvider {
	return &AlipayProvider{
		AppID:       appID,
		PrivateKey:  privateKey,
		RedirectURI: redirectURI,
		IsSandbox:   isSandbox,
	}
}

func (a *AlipayProvider) getGateway() string {
	if a.IsSandbox {
		return "https://openapi-sandbox.dl.alipaydev.com/gateway.do"
	}
	return "https://openapi.alipay.com/gateway.do"
}

// GetAuthURL 获取支付宝授权URL
func (a *AlipayProvider) GetAuthURL(state string) (string, error) {
	authURL := fmt.Sprintf(
		"https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=%s&scope=auth_user&redirect_uri=%s&state=%s",
		a.AppID,
		url.QueryEscape(a.RedirectURI),
		url.QueryEscape(state),
	)
	return authURL, nil
}

// ExchangeCode 用授权码换取 access_token
func (a *AlipayProvider) ExchangeCode(ctx context.Context, code string) (*AlipayTokenResponse, error) {
	params := map[string]string{
		"app_id":     a.AppID,
		"method":     "alipay.system.oauth.token",
		"charset":    "UTF-8",
		"sign_type":  "RSA2",
		"timestamp":  time.Now().Format("2006-01-02 15:04:05"),
		"version":    "1.0",
		"grant_type": "authorization_code",
		"code":       code,
	}

	if a.PrivateKey != "" {
		sign, err := a.signParams(params)
		if err != nil {
			return nil, fmt.Errorf("sign failed: %w", err)
		}
		params["sign"] = sign
	}

	form := url.Values{}
	for k, v := range params {
		form.Set(k, v)
	}

	req, err := http.NewRequestWithContext(ctx, "POST", a.getGateway(),
		strings.NewReader(form.Encode()))
	if err != nil {
		return nil, fmt.Errorf("create request failed: %w", err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

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

	body, err := readOAuthResponseBody(resp)
	if err != nil {
		return nil, fmt.Errorf("read response failed: %w", err)
	}

	var rawResp map[string]json.RawMessage
	if err := json.Unmarshal(body, &rawResp); err != nil {
		return nil, fmt.Errorf("parse response failed: %w", err)
	}

	tokenData, ok := rawResp["alipay_system_oauth_token_response"]
	if !ok {
		return nil, fmt.Errorf("invalid alipay response structure")
	}

	var tokenResp AlipayTokenResponse
	if err := json.Unmarshal(tokenData, &tokenResp); err != nil {
		return nil, fmt.Errorf("parse token response failed: %w", err)
	}

	return &tokenResp, nil
}

// GetUserInfo 获取支付宝用户信息
func (a *AlipayProvider) GetUserInfo(ctx context.Context, accessToken string) (*AlipayUserInfo, error) {
	params := map[string]string{
		"app_id":     a.AppID,
		"method":     "alipay.user.info.share",
		"charset":    "UTF-8",
		"sign_type":  "RSA2",
		"timestamp":  time.Now().Format("2006-01-02 15:04:05"),
		"version":    "1.0",
		"auth_token": accessToken,
	}

	if a.PrivateKey != "" {
		sign, err := a.signParams(params)
		if err != nil {
			return nil, fmt.Errorf("sign failed: %w", err)
		}
		params["sign"] = sign
	}

	form := url.Values{}
	for k, v := range params {
		form.Set(k, v)
	}

	req, err := http.NewRequestWithContext(ctx, "POST", a.getGateway(),
		strings.NewReader(form.Encode()))
	if err != nil {
		return nil, fmt.Errorf("create request failed: %w", err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

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

	body, err := readOAuthResponseBody(resp)
	if err != nil {
		return nil, fmt.Errorf("read response failed: %w", err)
	}

	var rawResp map[string]json.RawMessage
	if err := json.Unmarshal(body, &rawResp); err != nil {
		return nil, fmt.Errorf("parse response failed: %w", err)
	}

	userData, ok := rawResp["alipay_user_info_share_response"]
	if !ok {
		return nil, fmt.Errorf("invalid alipay user info response")
	}

	var userInfo AlipayUserInfo
	if err := json.Unmarshal(userData, &userInfo); err != nil {
		return nil, fmt.Errorf("parse user info failed: %w", err)
	}

	return &userInfo, nil
}

// signParams 使用 RSA2（SHA256withRSA）对参数签名
func (a *AlipayProvider) signParams(params map[string]string) (string, error) {
	// 按字典序排列参数
	keys := make([]string, 0, len(params))
	for k := range params {
		if k != "sign" {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)

	var parts []string
	for _, k := range keys {
		parts = append(parts, k+"="+params[k])
	}
	signContent := strings.Join(parts, "&")

	// 解析私钥
	privKey, err := parseAlipayPrivateKey(a.PrivateKey)
	if err != nil {
		return "", fmt.Errorf("parse private key: %w", err)
	}

	// SHA256withRSA 签名
	hash := sha256.Sum256([]byte(signContent))
	signature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA256, hash[:])
	if err != nil {
		return "", fmt.Errorf("rsa sign: %w", err)
	}

	return base64.StdEncoding.EncodeToString(signature), nil
}

// parseAlipayPrivateKey 解析支付宝私钥（支持 PKCS#8 和 PKCS#1）
func parseAlipayPrivateKey(pemStr string) (*rsa.PrivateKey, error) {
	// 如果没有 PEM 头，添加 PKCS#8 头
	if !strings.Contains(pemStr, "-----BEGIN") {
		pemStr = "-----BEGIN PRIVATE KEY-----\n" + pemStr + "\n-----END PRIVATE KEY-----"
	}

	block, _ := pem.Decode([]byte(pemStr))
	if block == nil {
		return nil, fmt.Errorf("failed to decode PEM block")
	}

	// 尝试 PKCS#8
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err == nil {
		rsaKey, ok := key.(*rsa.PrivateKey)
		if !ok {
			return nil, fmt.Errorf("not an RSA private key")
		}
		return rsaKey, nil
	}

	// 尝试 PKCS#1
	return x509.ParsePKCS1PrivateKey(block.Bytes)
}
