package kas

import (
	"time"

	agent_configuration_server "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/agent_configuration/server"
	autoflow_server "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/autoflow/server"
	autoflow_codec_server "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/autoflow_codec_server/server"
	event_tracker_server "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/event_tracker/server"
	google_profiler_server "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/google_profiler/server"
	kas2agentk_tunnel_server "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/kas2agentk_tunnel/server"
	kubernetes_api_server "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/kubernetes_api/server"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/modserver"
	observability_server "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/observability/server"
	usage_metrics_server "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/usage_metrics/server"
	workspaces_server "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/workspaces/server"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/prototool"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/pkg/kascfg"
)

const (
	defaultGitLabAPIRateLimitRefillRate = 50.0
	defaultGitLabAPIRateLimitBucketSize = 250

	defaultListenGracePeriod = 5 * time.Second

	defaultAgentInfoCacheTTL         = 5 * time.Minute
	defaultAgentInfoCacheErrorTTL    = 1 * time.Minute
	defaultAgentRedisConnInfoTTL     = 5 * time.Minute
	defaultAgentRedisConnInfoRefresh = 4 * time.Minute
	defaultAgentRedisConnInfoGC      = 10 * time.Minute

	defaultAgentListenNetwork                      = "tcp"
	defaultAgentListenAddress                      = "127.0.0.1:8150"
	defaultAgentListenWebSocket                    = false
	defaultAgentListenConnectionsPerTokenPerMinute = 40000
	defaultAgentListenMaxConnectionAge             = 2 * time.Hour

	defaultGitalyGlobalAPIRefillRate    = 30.0
	defaultGitalyGlobalAPIBucketSize    = 70
	defaultGitalyPerServerAPIRate       = 15.0
	defaultGitalyPerServerAPIBucketSize = 40

	defaultRedisDialTimeout  = 5 * time.Second
	defaultRedisWriteTimeout = 3 * time.Second
	defaultRedisKeyPrefix    = "gitlab-kas"
	defaultRedisNetwork      = "tcp"

	defaultAPIListenNetwork          = "tcp"
	defaultAPIListenAddress          = "127.0.0.1:8153"
	defaultAPIListenMaxConnectionAge = 2 * time.Hour

	defaultPrivateAPIListenNetwork = "tcp"
	defaultPrivateAPIListenAddress = ":8155"
	// Should be equal to the defaultAgentListenMaxConnectionAge as agent's tunnel requests go via private API server.
	defaultPrivateAPIListenMaxConnectionAge = defaultAgentListenMaxConnectionAge
)

var (
	defaulters = []modserver.ApplyDefaults{
		agent_configuration_server.ApplyDefaults,
		event_tracker_server.ApplyDefaults,
		google_profiler_server.ApplyDefaults,
		kas2agentk_tunnel_server.ApplyDefaults,
		kubernetes_api_server.ApplyDefaults,
		observability_server.ApplyDefaults,
		usage_metrics_server.ApplyDefaults,
		workspaces_server.ApplyDefaults,
		autoflow_server.ApplyDefaults,
		// NOTE: must strictly come after autoflow_server.ApplyDefaults because they share a config node.
		autoflow_codec_server.ApplyDefaults,
	}
)

func ApplyDefaultsToKASConfigurationFile(cfg *kascfg.ConfigurationFile) {
	prototool.NotNil(&cfg.Gitlab)
	defaultGitLab(cfg.Gitlab)

	prototool.NotNil(&cfg.Agent)
	defaultAgent(cfg.Agent)

	prototool.NotNil(&cfg.Gitaly)
	defaultGitaly(cfg.Gitaly)

	prototool.NotNil(&cfg.Redis)
	defaultRedis(cfg.Redis)

	prototool.NotNil(&cfg.Api)
	defaultAPI(cfg.Api)

	prototool.NotNil(&cfg.PrivateApi)
	defaultPrivateAPI(cfg.PrivateApi)

	for _, defaulter := range defaulters {
		defaulter(cfg)
	}
}

func defaultAPI(api *kascfg.ApiCF) {
	prototool.NotNil(&api.Listen)
	prototool.DefaultValPtr(&api.Listen.Network, defaultAPIListenNetwork)
	prototool.DefaultVal(&api.Listen.Address, defaultAPIListenAddress)
	prototool.Duration(&api.Listen.MaxConnectionAge, defaultAPIListenMaxConnectionAge)
	prototool.Duration(&api.Listen.ListenGracePeriod, defaultListenGracePeriod)
}

func defaultPrivateAPI(api *kascfg.PrivateApiCF) {
	prototool.NotNil(&api.Listen)
	prototool.DefaultValPtr(&api.Listen.Network, defaultPrivateAPIListenNetwork)
	prototool.DefaultVal(&api.Listen.Address, defaultPrivateAPIListenAddress)
	prototool.Duration(&api.Listen.MaxConnectionAge, defaultPrivateAPIListenMaxConnectionAge)
	prototool.Duration(&api.Listen.ListenGracePeriod, defaultListenGracePeriod)
}

func defaultGitLab(g *kascfg.GitLabCF) {
	prototool.NotNil(&g.ApiRateLimit)
	prototool.DefaultVal(&g.ApiRateLimit.RefillRatePerSecond, defaultGitLabAPIRateLimitRefillRate)
	prototool.DefaultVal(&g.ApiRateLimit.BucketSize, defaultGitLabAPIRateLimitBucketSize)
}

func defaultAgent(a *kascfg.AgentCF) {
	prototool.NotNil(&a.Listen)
	prototool.DefaultValPtr(&a.Listen.Network, defaultAgentListenNetwork)
	prototool.DefaultVal(&a.Listen.Address, defaultAgentListenAddress)
	prototool.DefaultValPtr(&a.Listen.Websocket, defaultAgentListenWebSocket)
	prototool.DefaultVal(&a.Listen.ConnectionsPerTokenPerMinute, defaultAgentListenConnectionsPerTokenPerMinute)
	prototool.Duration(&a.Listen.MaxConnectionAge, defaultAgentListenMaxConnectionAge)
	prototool.Duration(&a.Listen.ListenGracePeriod, defaultListenGracePeriod)

	prototool.Duration(&a.InfoCacheTtl, defaultAgentInfoCacheTTL)
	prototool.Duration(&a.InfoCacheErrorTtl, defaultAgentInfoCacheErrorTTL)
	prototool.Duration(&a.RedisConnInfoTtl, defaultAgentRedisConnInfoTTL)
	prototool.Duration(&a.RedisConnInfoRefresh, defaultAgentRedisConnInfoRefresh)
	prototool.Duration(&a.RedisConnInfoGc, defaultAgentRedisConnInfoGC)
}

func defaultGitaly(g *kascfg.GitalyCF) {
	prototool.NotNil(&g.GlobalApiRateLimit)
	prototool.DefaultVal(&g.GlobalApiRateLimit.RefillRatePerSecond, defaultGitalyGlobalAPIRefillRate)
	prototool.DefaultVal(&g.GlobalApiRateLimit.BucketSize, defaultGitalyGlobalAPIBucketSize)

	prototool.NotNil(&g.PerServerApiRateLimit)
	prototool.DefaultVal(&g.PerServerApiRateLimit.RefillRatePerSecond, defaultGitalyPerServerAPIRate)
	prototool.DefaultVal(&g.PerServerApiRateLimit.BucketSize, defaultGitalyPerServerAPIBucketSize)
}

func defaultRedis(r *kascfg.RedisCF) {
	prototool.Duration(&r.DialTimeout, defaultRedisDialTimeout)
	prototool.Duration(&r.WriteTimeout, defaultRedisWriteTimeout)
	prototool.DefaultVal(&r.KeyPrefix, defaultRedisKeyPrefix)
	prototool.DefaultValPtr(&r.Network, defaultRedisNetwork)
}
