package agentk

import (
	"context"
	"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/agentk_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/pkg/agentcfg"
	"gitlab.com/gitlab-org/cluster-integration/tunnel/tool/retry"
	"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(&agentk_rpc.AgentRegistrar_ServiceDesc)

	client := agentk_rpc.NewAgentRegistrarClient(config.KASConn)

	m := &agent.Module[*agentcfg.AgentConfiguration]{
		Log: config.Log,
		PollConfig: retry.PollConfig{
			Interval: agent_registrar.RegisterAttemptInterval,
			Backoff: retry.NewExponentialBackoff(
				agent.RegisterInitBackoff,
				agent.RegisterMaxBackoff,
				agent.RegisterResetDuration,
				agent.RegisterBackoffFactor,
				agent.RegisterJitter,
			),
		},
		Register: func(ctx context.Context) error {
			// 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))
			}

			_, err = client.Register(ctx, &agentk_rpc.RegisterRequest{
				Meta:       agentMeta,
				InstanceId: config.InstanceID,
			})
			return err
		},
		Unregister: func(ctx context.Context) error {
			_, err := client.Unregister(ctx, &agentk_rpc.UnregisterRequest{
				Meta:       agentMeta,
				InstanceId: config.InstanceID,
			})
			return err
		},
	}
	return m, nil
}
