package config

import (
	"crypto/tls"
	"net/http"
	"time"
)

type ProxyServerConfig struct {
	Debug     InterpolatedBool `yaml:"debug"`
	HTTP      HTTPConfig       `yaml:"http"`
	Metrics   MetricsConfig    `yaml:"metrics"`
	Profiling ProfilingConfig  `yaml:"profiling"`
	Transport TransportConfig  `yaml:"transport"`
	Dial      DialConfig       `yaml:"dial"`
	Sentry    SentryConfig     `yaml:"sentry"`
	Cache     CacheConfig      `yaml:"cache"`
	Templates TemplatesConfig  `yaml:"templates"`
}

func NewDefaultProxyServerConfig() ProxyServerConfig {
	return ProxyServerConfig{
		Debug:     false,
		HTTP:      NewHTTPConfig("0.0.0.0", 8080),
		Metrics:   NewDefaultMetricsConfig(),
		Transport: NewDefaultTransportConfig(),
		Dial:      NewDefaultDialConfig(),
		Sentry:    NewDefaultSentryConfig(),
		Cache:     NewDefaultCacheConfig(),
		Templates: NewDefaultTemplatesConfig(),
		Profiling: NewDefaultProfilingConfig(),
	}
}

// See https://pkg.go.dev/net/http#Transport
type TransportConfig struct {
	ForceAttemptHTTP2      InterpolatedBool      `yaml:"forceAttemptHTTP2"`
	MaxIdleConns           InterpolatedInt       `yaml:"maxIdleConns"`
	MaxIdleConnsPerHost    InterpolatedInt       `yaml:"maxIdleConnsPerHost"`
	MaxConnsPerHost        InterpolatedInt       `yaml:"maxConnsPerHost"`
	IdleConnTimeout        *InterpolatedDuration `yaml:"idleConnTimeout"`
	TLSHandshakeTimeout    *InterpolatedDuration `yaml:"tlsHandshakeTimeout"`
	ExpectContinueTimeout  *InterpolatedDuration `yaml:"expectContinueTimeout"`
	DisableKeepAlives      InterpolatedBool      `yaml:"disableKeepAlives"`
	DisableCompression     InterpolatedBool      `yaml:"disableCompression"`
	ResponseHeaderTimeout  *InterpolatedDuration `yaml:"responseHeaderTimeout"`
	WriteBufferSize        InterpolatedInt       `yaml:"writeBufferSize"`
	ReadBufferSize         InterpolatedInt       `yaml:"readBufferSize"`
	MaxResponseHeaderBytes InterpolatedInt       `yaml:"maxResponseHeaderBytes"`
	InsecureSkipVerify     InterpolatedBool      `yaml:"insecureSkipVerify"`
}

func (c TransportConfig) AsTransport() *http.Transport {
	httpTransport := http.DefaultTransport.(*http.Transport).Clone()

	httpTransport.Proxy = http.ProxyFromEnvironment
	httpTransport.ForceAttemptHTTP2 = bool(c.ForceAttemptHTTP2)
	httpTransport.MaxIdleConns = int(c.MaxIdleConns)
	httpTransport.MaxIdleConnsPerHost = int(c.MaxIdleConnsPerHost)
	httpTransport.MaxConnsPerHost = int(c.MaxConnsPerHost)
	httpTransport.IdleConnTimeout = time.Duration(*c.IdleConnTimeout)
	httpTransport.TLSHandshakeTimeout = time.Duration(*c.TLSHandshakeTimeout)
	httpTransport.ExpectContinueTimeout = time.Duration(*c.ExpectContinueTimeout)
	httpTransport.DisableKeepAlives = bool(c.DisableKeepAlives)
	httpTransport.DisableCompression = bool(c.DisableCompression)
	httpTransport.ResponseHeaderTimeout = time.Duration(*c.ResponseHeaderTimeout)
	httpTransport.WriteBufferSize = int(c.WriteBufferSize)
	httpTransport.ReadBufferSize = int(c.ReadBufferSize)
	httpTransport.MaxResponseHeaderBytes = int64(c.MaxResponseHeaderBytes)

	if httpTransport.TLSClientConfig == nil {
		httpTransport.TLSClientConfig = &tls.Config{}
	}
	httpTransport.TLSClientConfig.InsecureSkipVerify = bool(c.InsecureSkipVerify)

	return httpTransport
}

func NewDefaultTransportConfig() TransportConfig {
	return TransportConfig{
		ForceAttemptHTTP2:      true,
		MaxIdleConns:           100,
		MaxIdleConnsPerHost:    100,
		MaxConnsPerHost:        100,
		IdleConnTimeout:        NewInterpolatedDuration(90 * time.Second),
		TLSHandshakeTimeout:    NewInterpolatedDuration(10 * time.Second),
		ExpectContinueTimeout:  NewInterpolatedDuration(1 * time.Second),
		ResponseHeaderTimeout:  NewInterpolatedDuration(10 * time.Second),
		DisableCompression:     false,
		DisableKeepAlives:      false,
		ReadBufferSize:         4096,
		WriteBufferSize:        4096,
		MaxResponseHeaderBytes: 0,
		InsecureSkipVerify:     false,
	}
}

// See https://pkg.go.dev/net#Dialer
type DialConfig struct {
	Timeout       *InterpolatedDuration `yaml:"timeout"`
	KeepAlive     *InterpolatedDuration `yaml:"keepAlive"`
	FallbackDelay *InterpolatedDuration `yaml:"fallbackDelay"`
	DualStack     InterpolatedBool      `yaml:"dualStack"`
}

func NewDefaultDialConfig() DialConfig {
	return DialConfig{
		Timeout:       NewInterpolatedDuration(30 * time.Second),
		KeepAlive:     NewInterpolatedDuration(30 * time.Second),
		FallbackDelay: NewInterpolatedDuration(300 * time.Millisecond),
		DualStack:     true,
	}
}

type CacheConfig struct {
	TTL *InterpolatedDuration `yaml:"ttl"`
}

func NewDefaultCacheConfig() CacheConfig {
	return CacheConfig{
		TTL: NewInterpolatedDuration(time.Second * 30),
	}
}

type TemplatesConfig struct {
	Dir InterpolatedString `yaml:"dir"`
}

func NewDefaultTemplatesConfig() TemplatesConfig {
	return TemplatesConfig{
		Dir: "./templates",
	}
}