package provider

import (
	"context"
	"strings"

	"github.com/supabase/auth/internal/conf"
	"golang.org/x/oauth2"
)

const (
	defaultFlyAPIBase = "api.fly.io"
)

type flyProvider struct {
	*oauth2.Config
	APIPath string
}

type flyUser struct {
	ResourceOwnerID string `json:"resource_owner_id"`
	UserID          string `json:"user_id"`
	UserName        string `json:"user_name"`
	Email           string `json:"email"`
	Organizations   []struct {
		ID   string `json:"id"`
		Role string `json:"role"`
	} `json:"organizations"`
	Scope       []string          `json:"scope"`
	Application map[string]string `json:"application"`
	ExpiresIn   int               `json:"expires_in"`
	CreatedAt   int               `json:"created_at"`
}

// NewFlyProvider creates a Fly oauth provider.
func NewFlyProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) {
	if err := ext.ValidateOAuth(); err != nil {
		return nil, err
	}

	authHost := chooseHost(ext.URL, defaultFlyAPIBase)

	// Fly only provides the "read" scope.
	// https://fly.io/docs/reference/extensions_api/#single-sign-on-flow
	oauthScopes := []string{
		"read",
	}

	if scopes != "" {
		oauthScopes = append(oauthScopes, strings.Split(scopes, ",")...)
	}

	return &flyProvider{
		Config: &oauth2.Config{
			ClientID:     ext.ClientID[0],
			ClientSecret: ext.Secret,
			Endpoint: oauth2.Endpoint{
				AuthURL:  authHost + "/oauth/authorize",
				TokenURL: authHost + "/oauth/token",
			},
			RedirectURL: ext.RedirectURI,
			Scopes:      oauthScopes,
		},
		APIPath: authHost,
	}, nil
}

func (p flyProvider) GetOAuthToken(code string) (*oauth2.Token, error) {
	return p.Exchange(context.Background(), code)
}

func (p flyProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) {
	var u flyUser
	if err := makeRequest(ctx, tok, p.Config, p.APIPath+"/oauth/token/info", &u); err != nil {
		return nil, err
	}

	data := &UserProvidedData{}
	if u.Email != "" {
		data.Emails = []Email{{
			Email:    u.Email,
			Verified: true,
			Primary:  true,
		}}
	}

	data.Metadata = &Claims{
		Issuer:        p.APIPath,
		Subject:       u.UserID,
		FullName:      u.UserName,
		Email:         u.Email,
		EmailVerified: true,
		ProviderId:    u.UserID,
		CustomClaims: map[string]interface{}{
			"resource_owner_id": u.ResourceOwnerID,
			"organizations":     u.Organizations,
			"application":       u.Application,
			"scope":             u.Scope,
			"created_at":        u.CreatedAt,
		},
	}
	return data, nil
}
