package agent

import (
	"context"
	"log/slog"
	"time"

	"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/rpc"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/grpctool"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/logz"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/retry"
)

type Module[CM any] struct {
	Log               *slog.Logger
	PollConfig        retry.PollConfigFactory
	Client            rpc.AgentRegistrarClient
	RegisterRequest   func() *rpc.RegisterRequest
	UnregisterRequest func() *rpc.UnregisterRequest
}

func (m *Module[CM]) Run(ctx context.Context, cfg <-chan CM) error {
	// Defer unregistration of this agent on shut down.
	defer func() {
		unregisterCtx, unregisterCancel := context.WithTimeout(context.WithoutCancel(ctx), 1*time.Second)
		defer unregisterCancel()
		_, err := m.Client.Unregister(unregisterCtx, m.UnregisterRequest())
		if err != nil {
			m.Log.Error("Unable to unregister", logz.Error(err))
		}
	}()

	_ = retry.PollWithBackoff(ctx, m.PollConfig(), func(ctx context.Context) (error, retry.AttemptResult) { //nolint:staticcheck
		_, err := m.Client.Register(ctx, m.RegisterRequest())
		if err != nil {
			if !grpctool.RequestCanceledOrTimedOut(err) {
				m.Log.Error("Failed to register agent instance. Please make sure the agent version matches the server version", logz.Error(err))
			}
			return nil, retry.Backoff
		}

		return nil, retry.Continue
	})
	return nil
}

func (m *Module[CM]) DefaultAndValidateConfiguration(config CM) error {
	return nil
}

func (m *Module[CM]) Name() string {
	return agent_registrar.ModuleName
}
