package openfga

import (
	"net/http"

	"github.com/openfga/go-sdk/credentials"
	"github.com/openfga/go-sdk/internal/constants"
	"github.com/openfga/go-sdk/internal/utils/retryutils"
	"github.com/openfga/go-sdk/telemetry"
)

const (
	SdkVersion = constants.SdkVersion

	defaultUserAgent = constants.UserAgent
)

// RetryParams provides configuration for retries in case of server errors
type RetryParams = retryutils.RetryParams

// Configuration stores the configuration of the API client
type Configuration struct {
	// ApiScheme - defines the scheme for the API: http or https
	// Deprecated: use ApiUrl instead of ApiScheme and ApiHost
	ApiScheme string `json:"api_scheme,omitempty"`
	// ApiHost - defines the host for the API without the scheme e.g. (api.fga.example)
	// Deprecated: use ApiUrl instead of ApiScheme and ApiHost
	ApiHost        string                   `json:"api_host,omitempty"`
	ApiUrl         string                   `json:"api_url,omitempty"`
	Credentials    *credentials.Credentials `json:"credentials,omitempty"`
	DefaultHeaders map[string]string        `json:"default_headers,omitempty"`
	UserAgent      string                   `json:"user_agent,omitempty"`
	Debug          bool                     `json:"debug,omitempty"`
	HTTPClient     *http.Client
	RetryParams    *RetryParams
	Telemetry      *telemetry.Configuration `json:"telemetry,omitempty"`
}

func GetSdkUserAgent() string {
	return defaultUserAgent
}

// NewConfiguration returns a new Configuration object
func NewConfiguration(config Configuration) (*Configuration, error) {
	apiUrl := config.ApiUrl

	apiScheme := config.ApiScheme
	if apiScheme == "" {
		apiScheme = "https"
	}

	if apiUrl == "" {
		// If api url is not provided, fall back to deprecated config fields
		apiUrl = apiScheme + "://" + config.ApiHost
	}

	cfg := &Configuration{
		ApiUrl:         apiUrl,
		Credentials:    config.Credentials,
		DefaultHeaders: config.DefaultHeaders,
		UserAgent:      config.UserAgent,
		Debug:          config.Debug,
		HTTPClient:     config.HTTPClient,
		RetryParams:    config.RetryParams,
		Telemetry:      config.Telemetry,
	}

	if cfg.UserAgent == "" {
		cfg.UserAgent = GetSdkUserAgent()
	}

	if cfg.DefaultHeaders == nil {
		cfg.DefaultHeaders = make(map[string]string)
	}

	if cfg.Telemetry == nil {
		cfg.Telemetry = telemetry.DefaultTelemetryConfiguration()
	}

	retryParams, err := retryutils.NewRetryParams(cfg.RetryParams)
	if err != nil {
		return nil, err
	}
	cfg.RetryParams = retryParams

	if err := cfg.ValidateConfig(); err != nil {
		return nil, err
	}

	return cfg, nil
}

// GetRetryParams
func (c *Configuration) GetRetryParams() RetryParams {
	return retryutils.GetRetryParamsOrDefault(c.RetryParams)
}

// AddDefaultHeader adds a new HTTP header to the default header in the request
func (c *Configuration) AddDefaultHeader(key string, value string) {
	c.DefaultHeaders[key] = value
}

// ValidateConfig ensures that the given configuration is valid
func (c *Configuration) ValidateConfig() error {
	if c.ApiUrl == "" {
		return reportError("Configuration.ApiUrl is required")
	}

	if !IsWellFormedUri(c.ApiUrl) {
		return reportError("Configuration.ApiUrl (%s) does not form a valid uri", c.ApiUrl)
	}

	if c.Credentials != nil {
		if err := c.Credentials.ValidateCredentialsConfig(); err != nil {
			return reportError("Credentials are invalid: %v", err)
		}
	}

	if err := c.RetryParams.Validate(); err != nil {
		return err
	}

	return nil
}
