package server

import (
	"context"
	"net"
	"time"

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/modserver"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/workspaces"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/workspaces/rpc"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/nettool"
	"gitlab.com/gitlab-org/cluster-integration/tunnel/tool/retry"
)

const (
	interval      = 10 * time.Second
	initBackoff   = 10 * time.Second
	maxBackoff    = time.Minute
	resetDuration = 2 * time.Minute
	backoffFactor = 2.0
	jitter        = 5.0
)

type Factory struct {
}

func (f *Factory) New(config *modserver.Config) (modserver.Module, error) {
	if !config.Config.Workspaces.Enabled {
		return nil, nil
	}

	config.RegisterAgentAPI(&rpc.Workspaces_ServiceDesc)

	listenCfg := config.Config.Workspaces.Listen
	listener := func(ctx context.Context) (net.Listener, error) {
		return nettool.ListenWithOSTCPKeepAlive(ctx, *listenCfg.Network, listenCfg.Address)
	}

	return &module{
		log:          config.Log,
		listener:     listener,
		gitLabClient: config.GitLabClient,
		pollConfig: retry.PollConfig{
			Interval: interval,
			Backoff: retry.NewExponentialBackoff(
				initBackoff,
				maxBackoff,
				resetDuration,
				backoffFactor,
				jitter,
			),
		},
		gitlabExternalURL:   config.Config.Gitlab.ExternalURL(),
		listenerGracePeriod: listenCfg.ListenGracePeriod.AsDuration(),
		shutdownTimeout:     listenCfg.ShutdownGracePeriod.AsDuration(),
		agentConnPool:       config.AgentConnPool,
		redisClient:         config.RedisClient,
		redisPrefix:         config.Config.Redis.KeyPrefix + ":workspaces",
		handleProcessingError: func(msg string, err error) {
			config.API.HandleProcessingError(context.Background(), config.Log, msg, err)
		},
		validator:  config.Validator,
		serverName: config.KASNameVersion,
		serverVia:  "gRPC/1.0 " + config.KASNameVersion,
		ready:      config.ProbeRegistry.RegisterReadinessToggle("workspacesServer"),
	}, nil
}

func (f *Factory) Name() string {
	return workspaces.ModuleName
}
