package server

import (
	"log/slog"
	"time"

	"buf.build/go/protovalidate"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/api"
	gapi "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/gitlab/api"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/modserver"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/logz"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/retry"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/syncz"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tunnel/info"
	otelmetric "go.opentelemetry.io/otel/metric"
	"go.opentelemetry.io/otel/propagation"
	"go.opentelemetry.io/otel/trace"
	"google.golang.org/grpc"
)

var (
	_ syncz.WorkerFactory[api.AgentKey, *gapi.ReceptiveAgent] = (*connWorkerFactory)(nil)
)

type connWorkerFactory struct {
	log *slog.Logger
	api modserver.API
	// minIdleConnections is the minimum number of connections that are not streaming a request.
	minIdleConnections int32
	// maxConnections is the maximum number of connections (idle and active).
	maxConnections int32
	scaleUpStep    int32
	// maxIdleTime is the maximum duration of time a connection can stay in an idle state.
	maxIdleTime          time.Duration
	userAgent            string
	jwtAudience          string
	jwtIssuer            string
	connectionPollConfig retry.PollConfigFactory
	// apiDescriptor returns the APIDescriptor for the gRPC server agents can talk to via the reverse tunnel.
	apiDescriptor     func() *info.APIDescriptor
	agentConnPool     AgentConnPool
	agentServerConn   grpc.ClientConnInterface
	tp                trace.TracerProvider
	mp                otelmetric.MeterProvider
	p                 propagation.TextMapPropagator
	streamClientProm  grpc.StreamClientInterceptor
	unaryClientProm   grpc.UnaryClientInterceptor
	validator         protovalidate.Validator
	grpcClientTracing bool
}

func (w *connWorkerFactory) New(src syncz.WorkSource[api.AgentKey, *gapi.ReceptiveAgent]) syncz.Worker {
	return &connWorker{
		log:                  w.log.With(logz.AgentKey(src.ID)),
		api:                  w.api,
		minIdleConnections:   w.minIdleConnections,
		maxConnections:       w.maxConnections,
		scaleUpStep:          w.scaleUpStep,
		maxIdleTime:          w.maxIdleTime,
		userAgent:            w.userAgent,
		jwtAudience:          w.jwtAudience,
		jwtIssuer:            w.jwtIssuer,
		connectionPollConfig: w.connectionPollConfig,
		apiDescriptor:        w.apiDescriptor(),
		agent:                src.Configuration,
		agentConnPool:        w.agentConnPool,
		agentServerConn:      w.agentServerConn,
		tp:                   w.tp,
		mp:                   w.mp,
		p:                    w.p,
		streamClientProm:     w.streamClientProm,
		unaryClientProm:      w.unaryClientProm,
		validator:            w.validator,
		grpcClientTracing:    w.grpcClientTracing,
	}
}
