package auth0

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/tidbcloud/account/commons/config"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

const (
	CreateAccessTokenUrlFormat      = "https://%s/oauth/token"
	CreateAccessTokenAudienceFormat = "https://%s/api/v2/"
)

var (
	customHttpClient = &http.Client{
		Timeout: time.Second * 6,
	}
)

func GetAuth0AccessToken(ctx context.Context) (string, error) {
	ssoConfig := config.Global().SSOConfig

	createAccessTokenUrl := fmt.Sprintf(CreateAccessTokenUrlFormat, ssoConfig.Auth0Domain)

	values := url.Values{}
	values.Set("grant_type", "client_credentials")
	values.Set("client_id", ssoConfig.M2mAppClientID)
	values.Set("client_secret", ssoConfig.M2mAppClientSecret)
	values.Set("audience", fmt.Sprintf(CreateAccessTokenAudienceFormat, ssoConfig.Auth0Domain))

	tilog.Info(ctx, "GetAuth0AccessToken request", zap.Any("url", createAccessTokenUrl), zap.Any("values", values))
	rsp, err := customHttpClient.PostForm(createAccessTokenUrl, values)
	if err != nil {
		tilog.Error(ctx, "GetAuth0AccessToken post form error", zap.Any("err", err))
		return "", err
	}
	defer func() {
		err := rsp.Body.Close()
		if err != nil {
			tilog.Error(ctx, "GetAuth0AccessToken close body error", zap.Any("err", err))
		}
	}()
	httpCode := rsp.StatusCode
	body, err := io.ReadAll(rsp.Body)
	if err != nil {
		tilog.Error(ctx, "GetAuth0AccessToken read body error", zap.Any("err", err))
		return "", err
	}
	tilog.Info(ctx, "GetAuth0AccessToken response", zap.Any("httpCode", httpCode), zap.Any("rspBody", string(body)))

	accessTokenRet := &AccessTokenRet{}
	err = json.Unmarshal(body, accessTokenRet)
	if err != nil {
		tilog.Error(ctx, "GetAuth0AccessToken unmarshal body error", zap.Any("err", err))
	}
	return accessTokenRet.AccessToken, nil
}

type AccessTokenRet struct {
	AccessToken  string `json:"access_token"`
	Scope        string `json:"scope"`
	ExpiresIn    uint64 `json:"expires_in"`
	TokenType    string `json:"token_type"`
	IDToken      string `json:"id_token"`
	RecoveryCode string `json:"recovery_code"`
}
