package agentk

import (
	"fmt"

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/agent_registrar"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/agent_registrar/agent"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/agent_registrar/rpc"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/modagentk"
	"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/pkg/agentcfg"
	"google.golang.org/protobuf/proto"
)

type Factory struct {
	agent.Factory
}

func (f *Factory) New(config *modagentk.Config) (modagentk.Module, error) {
	kubeClientset, err := config.K8sUtilFactory.KubernetesClientSet()
	if err != nil {
		return nil, fmt.Errorf("could not create kubernetes clientset: %w", err)
	}

	// Create a deep copy of AgentMeta to prevent unexpected mutations
	agentMeta := proto.CloneOf(config.AgentMeta)

	config.RegisterKASAPI(&rpc.AgentRegistrar_ServiceDesc)

	m := &agent.Module[*agentcfg.AgentConfiguration]{
		Log: config.Log,
		PollConfig: retry.NewPollConfigFactory(agent_registrar.RegisterAttemptInterval, retry.NewExponentialBackoffFactory(
			agent.RegisterInitBackoff,
			agent.RegisterMaxBackoff,
			agent.RegisterResetDuration,
			agent.RegisterBackoffFactor,
			agent.RegisterJitter,
		)),
		Client: rpc.NewAgentRegistrarClient(config.KASConn),
		RegisterRequest: func() *rpc.RegisterRequest {
			// Retrieve and set the Kubernetes version
			version, err := kubeClientset.ServerVersion()
			if err == nil {
				agentMeta.KubernetesVersion.Major = version.Major
				agentMeta.KubernetesVersion.Minor = version.Minor
				agentMeta.KubernetesVersion.GitVersion = version.GitVersion
				agentMeta.KubernetesVersion.Platform = version.Platform
			} else {
				config.Log.Warn("Failed to fetch Kubernetes version", logz.Error(err))
			}

			return &rpc.RegisterRequest{
				Meta: &rpc.RegisterRequest_AgentkMeta{
					AgentkMeta: agentMeta,
				},
				InstanceId: config.InstanceID,
			}
		},
		UnregisterRequest: func() *rpc.UnregisterRequest {
			return &rpc.UnregisterRequest{
				Meta: &rpc.UnregisterRequest_AgentkMeta{
					AgentkMeta: agentMeta,
				},
				InstanceId: config.InstanceID,
			}
		},
	}
	return m, nil
}
