package febbox

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

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/clientcredentials"
)

type customTokenSource struct {
	config       *clientcredentials.Config
	ctx          context.Context
	refreshToken string
}

func (c *customTokenSource) Token() (*oauth2.Token, error) {
	v := url.Values{}
	if c.refreshToken != "" {
		v.Set("grant_type", "refresh_token")
		v.Set("refresh_token", c.refreshToken)
	} else {
		v.Set("grant_type", "client_credentials")
	}

	v.Set("client_id", c.config.ClientID)
	v.Set("client_secret", c.config.ClientSecret)

	req, err := http.NewRequestWithContext(c.ctx, http.MethodPost, c.config.TokenURL, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, errors.New("oauth2: cannot fetch token")
	}

	var tokenResp struct {
		Code int    `json:"code"`
		Msg  string `json:"msg"`
		Data struct {
			AccessToken  string `json:"access_token"`
			ExpiresIn    int64  `json:"expires_in"`
			TokenType    string `json:"token_type"`
			Scope        string `json:"scope"`
			RefreshToken string `json:"refresh_token"`
		} `json:"data"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
		return nil, err
	}

	if tokenResp.Code != 1 {
		return nil, errors.New("oauth2: server response error")
	}

	c.refreshToken = tokenResp.Data.RefreshToken

	token := &oauth2.Token{
		AccessToken:  tokenResp.Data.AccessToken,
		TokenType:    tokenResp.Data.TokenType,
		RefreshToken: tokenResp.Data.RefreshToken,
		Expiry:       time.Now().Add(time.Duration(tokenResp.Data.ExpiresIn) * time.Second),
	}

	return token, nil
}

func (d *FebBox) initializeOAuth2Token(ctx context.Context, oauth2Config *clientcredentials.Config, refreshToken string) {
	d.oauth2Token = oauth2.ReuseTokenSource(nil, &customTokenSource{
		config:       oauth2Config,
		ctx:          ctx,
		refreshToken: refreshToken,
	})
}
