// Package cluster is an API client for the cluster service
package cluster

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/pomerium/pomerium/internal/version"
)

const (
	defaultMinTokenTTL = time.Minute * 5
)

var userAgent = version.UserAgent()

type client struct {
	tokenProvider TokenCache
	httpClient    *http.Client
	minTokenTTL   time.Duration
}

// TokenCache interface for fetching and caching tokens
type TokenCache interface {
	// GetToken returns a token that is expected to be valid for at least minTTL duration
	GetToken(ctx context.Context, minTTL time.Duration) (string, error)
	// Reset resets the token cache
	Reset()
}

// NewAuthorizedClient creates a new HTTP client that will automatically add an authorization header
func NewAuthorizedClient(
	endpoint string,
	tokenProvider TokenCache,
	httpClient *http.Client,
) (ClientWithResponsesInterface, error) {
	c := &client{
		minTokenTTL: defaultMinTokenTTL,
		httpClient:  httpClient,
	}

	c.tokenProvider = tokenProvider

	return NewClientWithResponses(endpoint, WithHTTPClient(c))
}

func (c *client) Do(req *http.Request) (*http.Response, error) {
	ctx := req.Context()
	token, err := c.tokenProvider.GetToken(ctx, c.minTokenTTL)
	if err != nil {
		return nil, fmt.Errorf("error getting token: %w", err)
	}
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("User-Agent", userAgent)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode == http.StatusUnauthorized {
		c.tokenProvider.Reset()
	}

	return resp, nil
}