// Code generated by Fern. DO NOT EDIT.

package core

import (
	context "context"
	http "net/http"
	url "net/url"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/clientcredentials"

	internal "github.com/auth0/go-auth0/v2/internal/client"
)

// RequestOption adapts the behavior of the client or an individual request.
type RequestOption interface {
	applyRequestOptions(*RequestOptions)
}

// RequestOptions defines all of the possible request options.
//
// This type is primarily used by the generated code and is not meant
// to be used directly; use the option package instead.
type RequestOptions struct {
	BaseURL         string
	HTTPClient      HTTPClient
	HTTPHeader      http.Header
	BodyProperties  map[string]interface{}
	QueryParameters url.Values
	MaxAttempts     uint

	Ctx         context.Context
	Token       string
	TokenSource oauth2.TokenSource
	Insecure    bool // For testing purposes only - allows HTTP instead of HTTPS

	// Auth0ClientEnv holds custom environment entries for the Auth0-Client header
	Auth0ClientEnv map[string]string

	// NoAuth0ClientInfo when true, prevents sending the "Auth0-Client" header
	NoAuth0ClientInfo bool

	// CustomDomainHeader is the custom domain to be sent in the "Auth0-Custom-Domain" header
	// for whitelisted API endpoints
	CustomDomainHeader string
}

// NewRequestOptions returns a new *RequestOptions value.
//
// This function is primarily used by the generated code and is not meant
// to be used directly; use RequestOption instead.
func NewRequestOptions(opts ...RequestOption) *RequestOptions {
	options := &RequestOptions{
		HTTPHeader:      make(http.Header),
		BodyProperties:  make(map[string]interface{}),
		QueryParameters: make(url.Values),
	}
	for _, opt := range opts {
		opt.applyRequestOptions(options)
	}
	return options
}

// ToHeader maps the configured request options into a http.Header used
// for the request(s).
func (r *RequestOptions) ToHeader() http.Header {
	header := r.cloneHeader()

	// Prefer TokenSource for automatic token refresh
	if r.TokenSource != nil {
		if token, err := r.TokenSource.Token(); err == nil {
			header.Set("Authorization", "Bearer "+token.AccessToken)
		}
	} else if r.Token != "" {
		header.Set("Authorization", "Bearer "+r.Token)
	}

	// Add custom domain header hint for request-level usage
	// This will be read by the CustomDomainHeaderTransport
	if r.CustomDomainHeader != "" {
		header.Set("X-Auth0-Custom-Domain-Hint", r.CustomDomainHeader)
	}

	return header
}

func (r *RequestOptions) cloneHeader() http.Header {
	headers := r.HTTPHeader.Clone()
	return headers
}

// BaseURLOption implements the RequestOption interface.
type BaseURLOption struct {
	BaseURL string
}

func (b *BaseURLOption) applyRequestOptions(opts *RequestOptions) {
	opts.BaseURL = b.BaseURL
}

// HTTPClientOption implements the RequestOption interface.
type HTTPClientOption struct {
	HTTPClient HTTPClient
}

func (h *HTTPClientOption) applyRequestOptions(opts *RequestOptions) {
	opts.HTTPClient = h.HTTPClient
}

// HTTPHeaderOption implements the RequestOption interface.
type HTTPHeaderOption struct {
	HTTPHeader http.Header
}

func (h *HTTPHeaderOption) applyRequestOptions(opts *RequestOptions) {
	// Merge headers instead of replacing to preserve existing headers
	if opts.HTTPHeader == nil {
		opts.HTTPHeader = make(http.Header)
	}
	for key, values := range h.HTTPHeader {
		opts.HTTPHeader[key] = values
	}
}

// BodyPropertiesOption implements the RequestOption interface.
type BodyPropertiesOption struct {
	BodyProperties map[string]interface{}
}

func (b *BodyPropertiesOption) applyRequestOptions(opts *RequestOptions) {
	// Merge properties instead of replacing to preserve existing properties
	if opts.BodyProperties == nil {
		opts.BodyProperties = make(map[string]interface{})
	}
	for key, value := range b.BodyProperties {
		opts.BodyProperties[key] = value
	}
}

// QueryParametersOption implements the RequestOption interface.
type QueryParametersOption struct {
	QueryParameters url.Values
}

func (q *QueryParametersOption) applyRequestOptions(opts *RequestOptions) {
	// Merge query parameters instead of replacing to preserve existing parameters
	if opts.QueryParameters == nil {
		opts.QueryParameters = make(url.Values)
	}
	for key, values := range q.QueryParameters {
		opts.QueryParameters[key] = values
	}
}

// MaxAttemptsOption implements the RequestOption interface.
type MaxAttemptsOption struct {
	MaxAttempts uint
}

func (m *MaxAttemptsOption) applyRequestOptions(opts *RequestOptions) {
	opts.MaxAttempts = m.MaxAttempts
}

// TokenOption implements the RequestOption interface.
type TokenOption struct {
	Token string
}

func (t *TokenOption) applyRequestOptions(opts *RequestOptions) {
	opts.Token = t.Token
}

type ClientCredentialsOption struct {
	Ctx          *context.Context
	ClientID     string
	ClientSecret string
	tokenSource  oauth2.TokenSource
}

func (c *ClientCredentialsOption) applyRequestOptions(options *RequestOptions) {
	// Initialize token source if needed
	if c.tokenSource == nil {
		parsedURL, err := url.Parse(options.BaseURL)
		if err != nil {
			return
		}

		tokenURL := parsedURL.Scheme + "://" + parsedURL.Host + "/oauth/token"
		cfg := &clientcredentials.Config{
			ClientID:     c.ClientID,
			ClientSecret: c.ClientSecret,
			TokenURL:     tokenURL,
			EndpointParams: url.Values{
				"audience": []string{parsedURL.Scheme + "://" + parsedURL.Host + "/api/v2/"},
			},
		}
		// oauth2.TokenSource already uses ReuseTokenSource internally for caching
		c.tokenSource = cfg.TokenSource(*c.Ctx)
	}

	// Set the token source for automatic token management
	options.TokenSource = c.tokenSource
}

type ClientCredentialsAndAudienceOption struct {
	Ctx          *context.Context
	ClientID     string
	ClientSecret string
	Audience     string
	tokenSource  oauth2.TokenSource
}

func (c *ClientCredentialsAndAudienceOption) applyRequestOptions(options *RequestOptions) {
	// Initialize token source if needed
	if c.tokenSource == nil {
		cfg := &clientcredentials.Config{
			ClientID:     c.ClientID,
			ClientSecret: c.ClientSecret,
			TokenURL:     options.BaseURL + "/oauth/token",
			EndpointParams: url.Values{
				"audience": []string{c.Audience},
			},
		}
		c.tokenSource = cfg.TokenSource(*c.Ctx)
	}

	// Set the token source for automatic token management
	options.TokenSource = c.tokenSource
}

type ClientCredentialsPrivateKeyJwtOption struct {
	Ctx         *context.Context
	ClientID    string
	PrivateKey  string
	Algorithm   string
	tokenSource oauth2.TokenSource
}

func (c *ClientCredentialsPrivateKeyJwtOption) applyRequestOptions(options *RequestOptions) {
	// Initialize token source if needed
	if c.tokenSource == nil {
		c.tokenSource = internal.OAuth2ClientCredentialsPrivateKeyJwt(*c.Ctx, options.BaseURL, c.Algorithm, c.PrivateKey, c.ClientID)
	}

	// Set the token source for automatic token management
	options.TokenSource = c.tokenSource
}

type ClientCredentialsPrivateKeyJwtAndAudienceOption struct {
	Ctx         *context.Context
	ClientID    string
	PrivateKey  string
	Algorithm   string
	Audience    string
	tokenSource oauth2.TokenSource
}

func (c *ClientCredentialsPrivateKeyJwtAndAudienceOption) applyRequestOptions(options *RequestOptions) {
	// Initialize token source if needed
	if c.tokenSource == nil {
		c.tokenSource = internal.OAuth2ClientCredentialsPrivateKeyJwtAndAudience(*c.Ctx, options.BaseURL, c.Algorithm, c.PrivateKey, c.ClientID, c.Audience)
	}

	// Set the token source for automatic token management
	options.TokenSource = c.tokenSource
}

// DebugOption implements the RequestOption interface.
type DebugOption struct {
	Debug bool
}

func (d *DebugOption) applyRequestOptions(opts *RequestOptions) {
	if opts.HTTPClient == nil {
		opts.HTTPClient = &http.Client{}
	}

	// Ensure we have a concrete *http.Client to modify its Transport
	if client, ok := opts.HTTPClient.(*http.Client); ok {
		client.Transport = internal.DebugTransport(client.Transport, d.Debug)
	}
}

// UserAgentOption implements the RequestOption interface.
type UserAgentOption struct {
	UserAgent string
}

func (u *UserAgentOption) applyRequestOptions(opts *RequestOptions) {
	// Set the User-Agent header directly
	if opts.HTTPHeader == nil {
		opts.HTTPHeader = make(http.Header)
	}
	opts.HTTPHeader.Set("User-Agent", u.UserAgent)
}

// InsecureOption implements the RequestOption interface.
// This option is for testing purposes only and should not be used in production.
type InsecureOption struct{}

func (i *InsecureOption) applyRequestOptions(opts *RequestOptions) {
	opts.Insecure = true

	// Change the BaseURL scheme from https to http
	if opts.BaseURL != "" {
		if u, err := url.Parse(opts.BaseURL); err == nil && u.Scheme == "https" {
			u.Scheme = "http"
			opts.BaseURL = u.String()
		}
	}

	// Set a static "insecure" token if no credentials are provided (matches v1 behavior)
	if opts.Token == "" && opts.TokenSource == nil {
		opts.Token = "insecure"
	}
}

// Auth0ClientEnvEntryOption implements the RequestOption interface.
type Auth0ClientEnvEntryOption struct {
	Key   string
	Value string
}

func (a *Auth0ClientEnvEntryOption) applyRequestOptions(opts *RequestOptions) {
	if opts.Auth0ClientEnv == nil {
		opts.Auth0ClientEnv = make(map[string]string)
	}
	opts.Auth0ClientEnv[a.Key] = a.Value
}

// NoAuth0ClientInfoOption implements the RequestOption interface.
type NoAuth0ClientInfoOption struct{}

func (n *NoAuth0ClientInfoOption) applyRequestOptions(opts *RequestOptions) {
	opts.NoAuth0ClientInfo = true
}

// CustomDomainHeaderOption implements the RequestOption interface.
type CustomDomainHeaderOption struct {
	CustomDomainHeader string
}

func (c *CustomDomainHeaderOption) applyRequestOptions(opts *RequestOptions) {
	opts.CustomDomainHeader = c.CustomDomainHeader
}
