package agent

import (
	"context"
	"log/slog"
	"reflect"

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/google_profiler"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/logz"
)

type ModuleCfg struct {
	ProfilerCfg ProfilerCfg
}

type Module[CM any] struct {
	Log                                     *slog.Logger
	Runner                                  ProfilerRunner
	CfgAccessor                             func(CM) *ModuleCfg
	DefaultAndValidateConfigurationCallback func(CM) error
}

func (m *Module[CM]) Run(_ context.Context, cmCh <-chan CM) error {
	// NOTE: The Google profiler cannot be stopped or restarted. Therefore, the first config
	// received that enables the profiler is to last to take effect. To stop or restart the profiler
	// the agent must be restarted.
	// See https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/issues/469#note_1585658837

	// Wait for the first config that enables and successfully starts the profiler
	var configInUse ProfilerCfg
	for cm := range cmCh {
		config := m.CfgAccessor(cm)
		profilerCfg := config.ProfilerCfg
		if !profilerCfg.Enabled {
			continue
		}

		if err := m.Runner.Start(profilerCfg); err != nil {
			m.Log.Error("Failed to start profiler", logz.Error(err))
			continue
		}

		configInUse = profilerCfg
		m.Log.Info("Started profiler. Changes to the observability.google_profiler config won't affect this agent until it's restarted")
		break
	}

	for cm := range cmCh {
		config := m.CfgAccessor(cm)
		if !reflect.DeepEqual(config.ProfilerCfg, configInUse) {
			m.Log.Warn("The observability.google_profiler config has changed, but a previous configuration of the profiler is already running. Please restart the agent for it to have any effect")
		}
	}
	return nil
}

func (m *Module[CM]) DefaultAndValidateConfiguration(cm CM) error {
	return m.DefaultAndValidateConfigurationCallback(cm)
}

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