package provider

import (
	"context"
	"strings"

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

const (
	defaultSpotifyAPIBase  = "api.spotify.com/v1"   // Used to get user data
	defaultSpotifyAuthBase = "accounts.spotify.com" // Used for OAuth flow
)

type spotifyProvider struct {
	*oauth2.Config
	APIPath string
}

type spotifyUser struct {
	DisplayName string             `json:"display_name"`
	Avatars     []spotifyUserImage `json:"images"`
	Email       string             `json:"email"`
	ID          string             `json:"id"`
}

type spotifyUserImage struct {
	Url    string `json:"url"`
	Height int    `json:"height"`
	Width  int    `json:"width"`
}

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

	apiPath := chooseHost(ext.URL, defaultSpotifyAPIBase)
	authPath := chooseHost(ext.URL, defaultSpotifyAuthBase)

	oauthScopes := []string{
		"user-read-email",
	}

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

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

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

func (g spotifyProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) {
	var u spotifyUser
	if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/me", &u); err != nil {
		return nil, err
	}

	data := &UserProvidedData{}
	if u.Email != "" {
		data.Emails = []Email{{
			Email: u.Email,
			// Spotify dosen't provide data on whether the user's email is verified.
			// https://developer.spotify.com/documentation/web-api/reference/get-current-users-profile
			Verified: false,
			Primary:  true,
		}}
	}

	var avatarURL string

	// Spotify returns a list of avatars, we want to use the largest one
	if len(u.Avatars) >= 1 {
		largestAvatar := u.Avatars[0]

		for _, avatar := range u.Avatars {
			if avatar.Height*avatar.Width > largestAvatar.Height*largestAvatar.Width {
				largestAvatar = avatar
			}
		}

		avatarURL = largestAvatar.Url
	}

	data.Metadata = &Claims{
		Issuer:  g.APIPath,
		Subject: u.ID,
		Name:    u.DisplayName,
		Picture: avatarURL,

		// To be deprecated
		AvatarURL:  avatarURL,
		FullName:   u.DisplayName,
		ProviderId: u.ID,
	}
	return data, nil
}
