package provider

import (
	"context"
	"strconv"
	"strings"

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

// Github

const (
	defaultGitHubAuthBase = "github.com"
	defaultGitHubAPIBase  = "api.github.com"
)

type githubProvider struct {
	*oauth2.Config
	APIHost string
}

type githubUser struct {
	ID        int    `json:"id"`
	UserName  string `json:"login"`
	Email     string `json:"email"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
}

type githubUserEmail struct {
	Email    string `json:"email"`
	Primary  bool   `json:"primary"`
	Verified bool   `json:"verified"`
}

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

	authHost := chooseHost(ext.URL, defaultGitHubAuthBase)
	apiHost := chooseHost(ext.URL, defaultGitHubAPIBase)
	if !strings.HasSuffix(apiHost, defaultGitHubAPIBase) {
		apiHost += "/api/v3"
	}

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

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

	return &githubProvider{
		Config: &oauth2.Config{
			ClientID:     ext.ClientID[0],
			ClientSecret: ext.Secret,
			Endpoint: oauth2.Endpoint{
				AuthURL:  authHost + "/login/oauth/authorize",
				TokenURL: authHost + "/login/oauth/access_token",
			},
			RedirectURL: ext.RedirectURI,
			Scopes:      oauthScopes,
		},
		APIHost: apiHost,
	}, nil
}

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

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

	data := &UserProvidedData{
		Metadata: &Claims{
			Issuer:            g.APIHost,
			Subject:           strconv.Itoa(u.ID),
			Name:              u.Name,
			PreferredUsername: u.UserName,

			// To be deprecated
			AvatarURL:   u.AvatarURL,
			FullName:    u.Name,
			ProviderId:  strconv.Itoa(u.ID),
			UserNameKey: u.UserName,
		},
	}

	var emails []*githubUserEmail
	if err := makeRequest(ctx, tok, g.Config, g.APIHost+"/user/emails", &emails); err != nil {
		return nil, err
	}

	for _, e := range emails {
		if e.Email != "" {
			data.Emails = append(data.Emails, Email{Email: e.Email, Verified: e.Verified, Primary: e.Primary})
		}
	}

	return data, nil
}
