package client

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

	"stereo.cat/backend/internal/auth"
)

type Client struct {
	RedirectUri  string
	ClientSecret string
	ClientId     string
}

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

func New(redirectUri, clientId, clientSecret string) Client {
	return Client{
		RedirectUri:  redirectUri,
		ClientId:     clientId,
		ClientSecret: clientSecret,
	}
}

func (c Client) GetUser(t auth.TokenResponse) (auth.User, error) {
	user := auth.User{
		Blacklisted: false,
		CreatedAt:   time.Now(),
	}

	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/%s", api, "users/@me"), nil)

	if err != nil {
		return user, nil
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", "Bearer "+t.AccessToken)

	resp, err := http.DefaultClient.Do(req)

	if resp.StatusCode != http.StatusOK {
		bodyBytes, err := io.ReadAll(resp.Body)

		if err != nil {
			return user, err
		}

		return user, errors.New(string(bodyBytes))
	}

	if err != nil {
		return user, err
	}

	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&user)

	if err != nil {
		return user, err
	}

	return user, nil
}

func (c Client) ExchangeCode(code string) (auth.TokenResponse, error) {
	var tokenResponse auth.TokenResponse

	requestBody := url.Values{
		"grant_type":   {"authorization_code"},
		"code":         {code},
		"redirect_uri": {c.RedirectUri},
	}

	req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/%s", api, "/oauth2/token"), strings.NewReader(requestBody.Encode()))

	if err != nil {
		return tokenResponse, err
	}

	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.SetBasicAuth(c.ClientId, c.ClientSecret)

	resp, err := http.DefaultClient.Do(req)

	if resp.StatusCode != http.StatusOK {
		bodyBytes, err := io.ReadAll(resp.Body)

		if err != nil {
			return tokenResponse, err
		}

		return tokenResponse, errors.New(string(bodyBytes))
	}

	if err != nil {
		return tokenResponse, err
	}

	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&tokenResponse)

	if err != nil {
		return tokenResponse, err
	}

	return tokenResponse, nil
}