package providers

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

// QQProvider QQ OAuth提供者
type QQProvider struct {
	AppID       string
	AppKey      string
	RedirectURI string
}

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

// QQTokenResponse QQ Token响应
type QQTokenResponse struct {
	AccessToken  string `json:"access_token"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
}

// QQOpenIDResponse QQ OpenID响应
type QQOpenIDResponse struct {
	ClientID string `json:"client_id"`
	OpenID   string `json:"openid"`
}

// QQUserInfo QQ用户信息
type QQUserInfo struct {
	Ret        int    `json:"ret"`
	Msg        string `json:"msg"`
	Nickname   string `json:"nickname"`
	Gender     string `json:"gender"` // 男, 女
	Province   string `json:"province"`
	City       string `json:"city"`
	Year       string `json:"year"`
	FigureURL  string `json:"figureurl"`
	FigureURL1 string `json:"figureurl_1"`
	FigureURL2 string `json:"figureurl_2"`
}

// NewQQProvider 创建QQ OAuth提供者
func NewQQProvider(appID, appKey, redirectURI string) *QQProvider {
	return &QQProvider{
		AppID:       appID,
		AppKey:      appKey,
		RedirectURI: redirectURI,
	}
}

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

// GetAuthURL 获取QQ授权URL
func (q *QQProvider) GetAuthURL(state string) (*QQAuthURLResponse, error) {
	authURL := fmt.Sprintf(
		"https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&scope=get_user_info&state=%s",
		q.AppID,
		url.QueryEscape(q.RedirectURI),
		state,
	)

	return &QQAuthURLResponse{
		URL:      authURL,
		State:    state,
		Redirect: q.RedirectURI,
	}, nil
}

// ExchangeCode 用授权码换取访问令牌
func (q *QQProvider) ExchangeCode(ctx context.Context, code string) (*QQTokenResponse, error) {
	tokenURL := fmt.Sprintf(
		"https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&fmt=json",
		q.AppID,
		q.AppKey,
		code,
		url.QueryEscape(q.RedirectURI),
	)

	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 tokenResp QQTokenResponse
	if err := json.Unmarshal(body, &tokenResp); err != nil {
		return nil, fmt.Errorf("parse token response failed: %w", err)
	}

	return &tokenResp, nil
}

// GetOpenID 用访问令牌获取OpenID
func (q *QQProvider) GetOpenID(ctx context.Context, accessToken string) (*QQOpenIDResponse, error) {
	openIDURL := fmt.Sprintf(
		"https://graph.qq.com/oauth2.0/me?access_token=%s&fmt=json",
		accessToken,
	)

	req, err := http.NewRequestWithContext(ctx, "GET", openIDURL, 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 openIDResp QQOpenIDResponse
	if err := json.Unmarshal(body, &openIDResp); err != nil {
		return nil, fmt.Errorf("parse openid response failed: %w", err)
	}

	return &openIDResp, nil
}

// GetUserInfo 获取QQ用户信息
func (q *QQProvider) GetUserInfo(ctx context.Context, accessToken, openID string) (*QQUserInfo, error) {
	userInfoURL := fmt.Sprintf(
		"https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s&format=json",
		accessToken,
		q.AppID,
		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 userInfo QQUserInfo
	if err := json.Unmarshal(body, &userInfo); err != nil {
		return nil, fmt.Errorf("parse user info failed: %w", err)
	}

	if userInfo.Ret != 0 {
		return nil, fmt.Errorf("qq api error: %s", userInfo.Msg)
	}

	return &userInfo, nil
}

// ValidateToken 验证访问令牌是否有效
func (q *QQProvider) ValidateToken(ctx context.Context, accessToken string) (bool, error) {
	_, err := q.GetOpenID(ctx, accessToken)
	if err != nil {
		return false, err
	}
	return true, nil
}
