package providers

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// WeChatProvider 微信OAuth提供者
type WeChatProvider struct {
	AppID     string
	AppSecret string
	Type      string // "web" for 扫码登录, "mp" for 公众号, "mini" for 小程序
}

// WeChatAuthURLResponse 获取授权URL响应
type WeChatAuthURLResponse struct {
	URL      string `json:"url"`
	State    string `json:"state"`
	Redirect string `json:"redirect,omitempty"`
}

// WeChatTokenResponse 微信Token响应
type WeChatTokenResponse struct {
	AccessToken  string `json:"access_token"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
	OpenID       string `json:"openid"`
	Scope        string `json:"scope"`
	UnionID      string `json:"unionid,omitempty"`
}

// WeChatUserInfo 微信用户信息
type WeChatUserInfo struct {
	OpenID     string `json:"openid"`
	Nickname   string `json:"nickname"`
	Sex        int    `json:"sex"` // 1男性, 2女性, 0未知
	Province   string `json:"province"`
	City       string `json:"city"`
	Country    string `json:"country"`
	HeadImgURL string `json:"headimgurl"`
	UnionID    string `json:"unionid,omitempty"`
}

// WeChatErrorCode 微信错误码
type WeChatErrorCode struct {
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}

// NewWeChatProvider 创建微信OAuth提供者
func NewWeChatProvider(appID, appSecret, oAuthType string) *WeChatProvider {
	return &WeChatProvider{
		AppID:     appID,
		AppSecret: appSecret,
		Type:      oAuthType,
	}
}

// GenerateState 生成随机状态码
func (w *WeChatProvider) GenerateState() (string, error) {
	b := make([]byte, 32)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(b), nil
}

// GetAuthURL 获取微信授权URL
func (w *WeChatProvider) GetAuthURL(redirectURI, state string) (*WeChatAuthURLResponse, error) {
	var authURL string

	switch w.Type {
	case "web":
		// 微信扫码登录 (开放平台)
		authURL = fmt.Sprintf(
			"https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#wechat_redirect",
			w.AppID,
			url.QueryEscape(redirectURI),
			state,
		)
	case "mp":
		// 微信公众号登录
		authURL = fmt.Sprintf(
			"https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=%s#wechat_redirect",
			w.AppID,
			url.QueryEscape(redirectURI),
			state,
		)
	default:
		return nil, fmt.Errorf("unsupported wechat oauth type: %s", w.Type)
	}

	return &WeChatAuthURLResponse{
		URL:      authURL,
		State:    state,
		Redirect: redirectURI,
	}, nil
}

// ExchangeCode 用授权码换取访问令牌
func (w *WeChatProvider) ExchangeCode(ctx context.Context, code string) (*WeChatTokenResponse, error) {
	tokenURL := fmt.Sprintf(
		"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
		w.AppID,
		w.AppSecret,
		code,
	)

	req, err := http.NewRequestWithContext(ctx, "GET", tokenURL, nil)
	if err != nil {
		return nil, fmt.Errorf("create request failed: %w", err)
	}

	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 errResp WeChatErrorCode
	if err := json.Unmarshal(body, &errResp); err == nil && errResp.ErrCode != 0 {
		return nil, fmt.Errorf("wechat api error: %d - %s", errResp.ErrCode, errResp.ErrMsg)
	}

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

	return &tokenResp, nil
}

// GetUserInfo 获取微信用户信息
func (w *WeChatProvider) GetUserInfo(ctx context.Context, accessToken, openID string) (*WeChatUserInfo, error) {
	userInfoURL := fmt.Sprintf(
		"https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN",
		accessToken,
		openID,
	)

	req, err := http.NewRequestWithContext(ctx, "GET", userInfoURL, nil)
	if err != nil {
		return nil, fmt.Errorf("create request failed: %w", err)
	}

	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 errResp WeChatErrorCode
	if err := json.Unmarshal(body, &errResp); err == nil && errResp.ErrCode != 0 {
		return nil, fmt.Errorf("wechat api error: %d - %s", errResp.ErrCode, errResp.ErrMsg)
	}

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

	return &userInfo, nil
}

// RefreshToken 刷新访问令牌
func (w *WeChatProvider) RefreshToken(ctx context.Context, refreshToken string) (*WeChatTokenResponse, error) {
	refreshURL := fmt.Sprintf(
		"https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s",
		w.AppID,
		refreshToken,
	)

	req, err := http.NewRequestWithContext(ctx, "GET", refreshURL, nil)
	if err != nil {
		return nil, fmt.Errorf("create request failed: %w", err)
	}

	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 errResp WeChatErrorCode
	if err := json.Unmarshal(body, &errResp); err == nil && errResp.ErrCode != 0 {
		return nil, fmt.Errorf("wechat api error: %d - %s", errResp.ErrCode, errResp.ErrMsg)
	}

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

	return &tokenResp, nil
}

// ValidateToken 验证访问令牌是否有效
func (w *WeChatProvider) ValidateToken(ctx context.Context, accessToken, openID string) (bool, error) {
	validateURL := fmt.Sprintf(
		"https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s",
		accessToken,
		openID,
	)

	req, err := http.NewRequestWithContext(ctx, "GET", validateURL, nil)
	if err != nil {
		return false, fmt.Errorf("create request failed: %w", err)
	}

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

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

	var result struct {
		ErrCode int    `json:"errcode"`
		ErrMsg  string `json:"errmsg"`
	}
	if err := json.Unmarshal(body, &result); err != nil {
		return false, fmt.Errorf("parse response failed: %w", err)
	}

	return result.ErrCode == 0, nil
}
