package discord

import (
    "arimelody-web/model"
    "encoding/json"
    "errors"
    "fmt"
    "net/http"
    "net/url"
    "strings"
)

const API_ENDPOINT = "https://discord.com/api/v10"

type (
    AccessTokenResponse struct {
        AccessToken  string `json:"access_token"`
        TokenType    string `json:"token_type"`
        ExpiresIn    int    `json:"expires_in"`
        RefreshToken string `json:"refresh_token"`
        Scope        string `json:"scope"`
    }

    AuthInfoResponse struct {
        Application struct {
            ID                  string  `json:"id"`
            Name                string  `json:"name"`
            Icon                string  `json:"icon"`
            Description         string  `json:"description"`
            Hook                bool    `json:"hook"`
            BotPublic           bool    `json:"bot_public"`
            BotRequireCodeGrant bool    `json:"bot_require_code_grant"`
            VerifyKey           string  `json:"verify_key"`
        } `json:"application"`
        Scopes  []string    `json:"scopes"`
        Expires string      `json:"expires"`
        User    DiscordUser `json:"user"`
    }

    DiscordUser struct {
        ID            string    `json:"id"`
        Username      string    `json:"username"`
        Avatar        string    `json:"avatar"`
        Discriminator string    `json:"discriminator"`
        GlobalName    string    `json:"global_name"`
        PublicFlags   int       `json:"public_flags"`
    }
)

func GetOAuthTokenFromCode(app *model.AppState, code string) (string, error) {
    // let's get an oauth token!
    req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/oauth2/token", API_ENDPOINT),
    strings.NewReader(url.Values{
        "client_id":     {app.Config.Discord.ClientID},
        "client_secret": {app.Config.Discord.Secret},
        "grant_type":    {"authorization_code"},
        "code":          {code},
        "redirect_uri":  {GetOAuthCallbackURI(app.Config.BaseUrl)},
    }.Encode()))
    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

    res, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", errors.New(fmt.Sprintf("Failed while contacting discord API: %s", err))
    }

    oauth := AccessTokenResponse{}

    err = json.NewDecoder(res.Body).Decode(&oauth)
    if err != nil {
        return "", errors.New(fmt.Sprintf("Failed to parse OAuth response data from discord: %s\n", err))
    }
    res.Body.Close()

    return oauth.AccessToken, nil
}

func GetDiscordUserFromAuth(token string) (DiscordUser, error) {
    // let's get authorisation information!
    req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/oauth2/@me", API_ENDPOINT), nil)
    req.Header.Add("Authorization", "Bearer " + token)

    res, err := http.DefaultClient.Do(req)
    if err != nil {
        return DiscordUser{}, errors.New(fmt.Sprintf("Failed to retrieve discord auth information: %s\n", err))
    }

    auth_info := AuthInfoResponse{}
    err = json.NewDecoder(res.Body).Decode(&auth_info)
    if err != nil {
        return DiscordUser{}, errors.New(fmt.Sprintf("Failed to parse auth information from discord: %s\n", err))
    }
    defer res.Body.Close()

    return auth_info.User, nil
}

func GetOAuthCallbackURI(baseURL string) string {
    return fmt.Sprintf("%s/admin/login", baseURL)
}

func GetRedirectURI(app *model.AppState) string {
    return fmt.Sprintf(
        "https://discord.com/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s&scope=identify",
        app.Config.Discord.ClientID,
        GetOAuthCallbackURI(app.Config.BaseUrl),
    )
}
