package twitch

import (
	"fmt"
	"time"

	"github.com/nicklaw5/helix/v2"

	"git.sr.ht/~ashkeel/strimertul/database"
)

func GetConfig(db database.Database) (Config, error) {
	var config Config
	if err := db.GetJSON(ConfigKey, &config); err != nil {
		return Config{}, fmt.Errorf("failed to get twitch config: %w", err)
	}
	return config, nil
}

type AuthResponse struct {
	AccessToken  string   `json:"access_token"`
	RefreshToken string   `json:"refresh_token"`
	ExpiresIn    int      `json:"expires_in"`
	Scope        []string `json:"scope"`
	Time         time.Time
}

func GetUserClient(db database.Database, keyPath string, forceRefresh bool) (*helix.Client, error) {
	var authResp AuthResponse
	if err := db.GetJSON(keyPath, &authResp); err != nil {
		return nil, err
	}

	// Handle token expiration
	if forceRefresh || time.Now().After(authResp.Time.Add(time.Duration(authResp.ExpiresIn)*time.Second)) {
		// Refresh tokens
		api, err := GetHelixAPI(db, "")
		if err != nil {
			return nil, err
		}

		refreshed, err := api.RefreshUserAccessToken(authResp.RefreshToken)
		if err != nil {
			return nil, err
		}

		authResp.AccessToken = refreshed.Data.AccessToken
		authResp.RefreshToken = refreshed.Data.RefreshToken
		authResp.Time = time.Now().Add(time.Duration(refreshed.Data.ExpiresIn) * time.Second)

		// Save new token pair
		err = db.PutJSON(keyPath, authResp)
		if err != nil {
			return nil, err
		}
	}

	return GetHelixAPI(db, authResp.AccessToken)
}

func GetHelixAPI(db database.Database, userToken string) (*helix.Client, error) {
	config, err := GetConfig(db)
	if err != nil {
		return nil, err
	}

	// If a user token is provided, create a user client
	if userToken != "" {
		return helix.NewClient(&helix.Options{
			ClientID:        config.APIClientID,
			ClientSecret:    config.APIClientSecret,
			UserAccessToken: userToken,
		})
	}

	// If no user token is provided, create an app client
	baseurl, err := baseURL(db)
	if err != nil {
		return nil, err
	}

	redirectURI := getRedirectURI(baseurl)

	// Create Twitch client
	api, err := helix.NewClient(&helix.Options{
		ClientID:     config.APIClientID,
		ClientSecret: config.APIClientSecret,
		RedirectURI:  redirectURI,
	})
	if err != nil {
		return nil, err
	}

	// Get access token
	resp, err := api.RequestAppAccessToken([]string{"user:read:email"})
	if err != nil {
		return nil, err
	}
	// Set the access token on the client
	api.SetAppAccessToken(resp.Data.AccessToken)

	return api, nil
}

func baseURL(db database.Database) (string, error) {
	var severConfig struct {
		Bind string `json:"bind"`
	}
	err := db.GetJSON("http/config", &severConfig)
	return severConfig.Bind, err
}

func getRedirectURI(baseurl string) string {
	return fmt.Sprintf("http://%s/twitch/callback", baseurl)
}
