package framework

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"strings"
	"sync"
	"testing"
	"time"
)

type testReceiver struct {
	mu    sync.Mutex
	names []EventName
	data  [][]EventData
}

func (r *testReceiver) OnEvent(name EventName, data ...EventData) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.names = append(r.names, name)
	r.data = append(r.data, data)
}

func (r *testReceiver) count(name EventName) int {
	r.mu.Lock()
	defer r.mu.Unlock()
	cnt := 0
	for _, n := range r.names {
		if n == name {
			cnt++
		}
	}
	return cnt
}

type testConfig struct {
	uniq       string
	fillCalled bool
	fillRet    ConfigData
}

func (c *testConfig) FillBlankValuesFrom(another ConfigData) ConfigData {
	c.fillCalled = true
	if c.fillRet != nil {
		return c.fillRet
	}
	return another
}

func (c *testConfig) UniqStr() string {
	return c.uniq
}

type testCloneable struct {
	value string
}

func (c *testCloneable) CloneableClone() Cloneable {
	if c == nil {
		return (*testCloneable)(nil)
	}
	cloned := *c
	return &cloned
}

func TestMergeConfigData(t *testing.T) {
	t.Run("nil left", func(t *testing.T) {
		right := &testConfig{uniq: "right"}
		got := MergeConfigData(nil, right)
		if got != right {
			t.Fatalf("expected right config to be returned")
		}
	})

	t.Run("nil right", func(t *testing.T) {
		left := &testConfig{uniq: "left"}
		got := MergeConfigData(left, nil)
		if got != left {
			t.Fatalf("expected left config to be returned")
		}
	})

	t.Run("delegate fill", func(t *testing.T) {
		ret := &testConfig{uniq: "ret"}
		left := &testConfig{uniq: "left", fillRet: ret}
		right := &testConfig{uniq: "right"}
		got := MergeConfigData(left, right)
		if got != ret {
			t.Fatalf("expected merged return from FillBlankValuesFrom")
		}
		if !left.fillCalled {
			t.Fatalf("expected FillBlankValuesFrom to be called")
		}
	})
}

func TestInputProviderMockConfigV1FillBlankValuesFrom(t *testing.T) {
	t.Run("keeps explicit value", func(t *testing.T) {
		cfg := &InputProviderMockConfigV1{Step: 2}
		fallback := &InputProviderMockConfigV1{Step: 9}
		merged := cfg.FillBlankValuesFrom(fallback).(*InputProviderMockConfigV1)
		if merged.Step != 2 {
			t.Fatalf("expected step 2, got %d", merged.Step)
		}
	})

	t.Run("fills from fallback", func(t *testing.T) {
		cfg := &InputProviderMockConfigV1{}
		fallback := &InputProviderMockConfigV1{Step: 9}
		merged := cfg.FillBlankValuesFrom(fallback).(*InputProviderMockConfigV1)
		if merged.Step != 9 {
			t.Fatalf("expected step 9, got %d", merged.Step)
		}
	})
}

func TestScalerInterfaceV1ExecutorSyncLoopConfigFillBlankValuesFrom(t *testing.T) {
	cfg := &ScalerInterfaceV1ExecutorSyncLoopConfig{}
	fallback := &ScalerInterfaceV1ExecutorSyncLoopConfig{PauseSecs: 7}
	merged := cfg.FillBlankValuesFrom(fallback).(*ScalerInterfaceV1ExecutorSyncLoopConfig)
	if merged.PauseSecs != 7 {
		t.Fatalf("expected pause 7, got %d", merged.PauseSecs)
	}

	cfg = &ScalerInterfaceV1ExecutorSyncLoopConfig{PauseSecs: 3}
	merged = cfg.FillBlankValuesFrom(fallback).(*ScalerInterfaceV1ExecutorSyncLoopConfig)
	if merged.PauseSecs != 3 {
		t.Fatalf("expected pause 3, got %d", merged.PauseSecs)
	}
}

func TestClusterScalerPhasedComponentScalerV1ConfigFillBlankValuesFrom(t *testing.T) {
	fallback := &ClusterScalerPhasedComponentScalerV1Config{
		ComponentName:         ComponentName("tidb"),
		ComponentScalerConfig: &InputProviderMockConfigV1{Step: 10},
	}
	cfg := &ClusterScalerPhasedComponentScalerV1Config{}
	merged := cfg.FillBlankValuesFrom(fallback).(*ClusterScalerPhasedComponentScalerV1Config)
	if merged.ComponentName != ComponentName("tidb") {
		t.Fatalf("expected merged component name")
	}
	inner, ok := merged.ComponentScalerConfig.(*InputProviderMockConfigV1)
	if !ok || inner.Step != 10 {
		t.Fatalf("expected merged nested config")
	}

	cfg = &ClusterScalerPhasedComponentScalerV1Config{
		ComponentName:         ComponentName("tikv"),
		ComponentScalerConfig: &InputProviderMockConfigV1{Step: 3},
	}
	merged = cfg.FillBlankValuesFrom(fallback).(*ClusterScalerPhasedComponentScalerV1Config)
	if merged.ComponentName != ComponentName("tikv") {
		t.Fatalf("expected existing component name to be kept")
	}
	inner, ok = merged.ComponentScalerConfig.(*InputProviderMockConfigV1)
	if !ok || inner.Step != 3 {
		t.Fatalf("expected existing nested config to be kept")
	}
}

func TestEventDataClone(t *testing.T) {
	t.Run("nil object", func(t *testing.T) {
		ed := EventData{DataName: "x", TypeHint: EventDataStr, Object: nil}
		cloned := ed.Clone()
		if cloned.Object != nil {
			t.Fatalf("expected nil object clone")
		}
	})

	t.Run("non nil object", func(t *testing.T) {
		src := &testCloneable{value: "v1"}
		ed := EventData{Object: src}
		cloned := ed.Clone()
		obj, ok := cloned.Object.(*testCloneable)
		if !ok {
			t.Fatalf("expected *testCloneable")
		}
		if obj == src {
			t.Fatalf("expected cloned object pointer to differ")
		}
		if obj.value != src.value {
			t.Fatalf("expected cloned value to match")
		}
	})
}

func TestGeneralLogger(t *testing.T) {
	buf := bytes.NewBuffer(nil)
	logger := NewGeneralLogger("unit", buf)
	logger.OnEvent(EventDebug,
		EventDataWithName("a", CloneableInt(1)),
		EventDataWithNameType("b", EventDataError, NewCloneableError(errors.New("boom"))))

	out := buf.String()
	if !strings.Contains(out, "[unit] ") {
		t.Fatalf("expected prefix in output: %q", out)
	}
	if !strings.Contains(out, string(EventDebug)) {
		t.Fatalf("expected event name in output: %q", out)
	}
	if !strings.Contains(out, "'a'") || !strings.Contains(out, "'b'") {
		t.Fatalf("expected data names in output: %q", out)
	}
}

func TestEventFilters(t *testing.T) {
	rec := &testReceiver{}
	allow := NewEventAllowFilter(rec, EventDebug)
	allow.OnEvent(EventDebug)
	allow.OnEvent(EventAny)
	if rec.count(EventDebug) != 1 || rec.count(EventAny) != 0 {
		t.Fatalf("allow filter mismatch")
	}

	rec = &testReceiver{}
	deny := NewEventDenyFilter(rec, EventAny)
	deny.OnEvent(EventDebug)
	deny.OnEvent(EventAny)
	if rec.count(EventDebug) != 1 || rec.count(EventAny) != 0 {
		t.Fatalf("deny filter mismatch")
	}
}

func TestEventsCounter(t *testing.T) {
	counter := NewEventsCounter(EventAny)
	counter.OnEvent(EventAny)
	counter.OnEvent(EventDebug)
	if counter.GetCount(EventAny) != 1 {
		t.Fatalf("expected any count 1")
	}
	if counter.GetCount(EventDebug) != 1 {
		t.Fatalf("expected debug count 1")
	}

	buf := bytes.NewBuffer(nil)
	counter.Dump(buf)
	out := buf.String()
	if !strings.Contains(out, string(EventAny)) || !strings.Contains(out, string(EventDebug)) {
		t.Fatalf("unexpected dump output: %q", out)
	}
}

func TestSyncEventHubWithAsyncHub(t *testing.T) {
	syncHub := NewSyncEventHub()
	syncRec := &testReceiver{}
	asyncRec := &testReceiver{}
	syncHub.RegisterReceiver(syncRec)

	hub1 := syncHub.GetAsyncHub()
	hub2 := syncHub.GetAsyncHub()
	if hub1 != hub2 {
		t.Fatalf("expected async hub singleton")
	}
	hub1.RegisterReceiver(asyncRec)

	syncHub.OnEvent(EventDebug, EventDataWithName("x", CloneableString("y")))
	if syncRec.count(EventDebug) != 1 {
		t.Fatalf("expected sync receiver to receive event")
	}
	if asyncRec.count(EventDebug) != 1 {
		t.Fatalf("expected async receiver to receive event")
	}
}

func TestInputProviderManager(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewInputProviderManager(rec)
	meta := InputProviderMeta{Type: "mock", InterfaceVersion: InputInterfaceV1IdStr}
	creator := func(eventReceiver EventReceiver, config ConfigData) (any, error) {
		return NewInputProviderMockEmpty(eventReceiver), nil
	}

	if err := mgr.Register(meta, creator); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	if err := mgr.Register(meta, creator); err == nil {
		t.Fatalf("expected duplicate register error")
	}

	if _, err := mgr.NewInput(InputProviderMeta{Type: "missing", InterfaceVersion: InputInterfaceV1IdStr}, nil); err == nil {
		t.Fatalf("expected creator not found error")
	}
}

func TestInputProviderManagerNewInputInterfaceMismatch(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewInputProviderManager(rec)
	meta := InputProviderMeta{Type: "bad", InterfaceVersion: InputInterfaceV1IdStr}
	if err := mgr.Register(meta, func(EventReceiver, ConfigData) (any, error) { return "bad", nil }); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	if _, err := mgr.NewInput(meta, nil); err == nil || !strings.Contains(err.Error(), "does not implement") {
		t.Fatalf("expected interface mismatch error, got: %v", err)
	}
}

func TestDefaultInputProviderManager(t *testing.T) {
	rec := &testReceiver{}
	mgr, err := NewDefaultInputProviderManager(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}

	if _, err := mgr.NewInput(InputProviderMockV1Meta, nil); err == nil {
		t.Fatalf("expected type mismatch on nil config")
	}

	input, err := mgr.NewInput(InputProviderMockV1Meta, &InputProviderMockConfigV1{Step: 1})
	if err != nil {
		t.Fatalf("expected success: %v", err)
	}
	if _, ok := input.(InputInterfaceV1); !ok {
		t.Fatalf("expected InputInterfaceV1")
	}
}

func TestOutputManager(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewOutputManager(rec)
	meta := OutputMeta{Type: "o", InterfaceVersion: OutputInterfaceV1IdStr}
	creator := func(eventReceiver EventReceiver, config ConfigData) (any, error) {
		return NewOutputMockEmpty(eventReceiver), nil
	}

	if err := mgr.Register(meta, creator); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	if err := mgr.Register(meta, creator); err == nil {
		t.Fatalf("expected duplicate register error")
	}
	if _, err := mgr.NewOutput(OutputMeta{Type: "missing", InterfaceVersion: OutputInterfaceV1IdStr}, nil); err == nil {
		t.Fatalf("expected creator not found")
	}
}

func TestDefaultOutputManager(t *testing.T) {
	rec := &testReceiver{}
	mgr, err := NewDefaultOutputManager(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}
	out, err := mgr.NewOutput(OutputMockEmptyMeta, nil)
	if err != nil {
		t.Fatalf("new output failed: %v", err)
	}
	if _, ok := out.(OutputInterfaceV1); !ok {
		t.Fatalf("expected OutputInterfaceV1")
	}
}

func TestPhasedComponentScalerManager(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewPhasedComponentScalerManager(rec)
	creator := func(eventReceiver EventReceiver, clusterId ClusterId, componentName ComponentName, config ConfigData, _, _ any) (any, error) {
		return NewPhasedComponentScalerMockEmpty(eventReceiver, clusterId, componentName), nil
	}

	if err := mgr.Register("x", creator); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	if err := mgr.Register("x", creator); err == nil {
		t.Fatalf("expected duplicate register error")
	}
	if _, err := mgr.NewPhasedComponentScaler("missing", "c", "tidb", nil, nil, nil); err == nil {
		t.Fatalf("expected creator not found")
	}
}

func TestDefaultPhasedComponentScalerManager(t *testing.T) {
	rec := &testReceiver{}
	mgr, err := NewDefaultPhasedComponentScalerManager(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}
	scaler, err := mgr.NewPhasedComponentScaler(PhasedComponentScalerMockEmptyIdStr, "c1", "tidb", nil, nil, nil)
	if err != nil {
		t.Fatalf("expected success: %v", err)
	}
	if _, ok := scaler.(PhasedComponentScalerV1); !ok {
		t.Fatalf("expected PhasedComponentScalerV1")
	}
}

func TestClusterScalerManager(t *testing.T) {
	rec := &testReceiver{}
	inMgr, _ := NewDefaultInputProviderManager(rec)
	outMgr, _ := NewDefaultOutputManager(rec)
	mgr, err := NewClusterScalerManager(rec, inMgr, outMgr)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}

	meta := &ClusterScalerMeta{
		ScalerMeta: UnattachedScalerMeta{Type: "t1", InterfaceVersion: ClusterScalerInterfaceV1IdStr},
		InputMeta:  InputProviderMockEmptyMeta,
		OutputMeta: OutputMockEmptyMeta,
	}
	creator := func(eventReceiver EventReceiver, clusterId ClusterId, scalerConfig ConfigData, pInputInterface, pOutputInterface any) (any, error) {
		return NewClusterScalerMockEmpty(eventReceiver, clusterId, pInputInterface.(InputInterfaceV1), pOutputInterface.(OutputInterfaceV1)), nil
	}
	if err := mgr.Register(meta, creator); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	if err := mgr.Register(meta, creator); err == nil {
		t.Fatalf("expected duplicate register error")
	}

	_, _, _, err = mgr.NewClusterScaler(&ClusterScalerMeta{
		ScalerMeta: UnattachedScalerMeta{Type: "missing", InterfaceVersion: ClusterScalerInterfaceV1IdStr},
		InputMeta:  InputProviderMockEmptyMeta,
		OutputMeta: OutputMockEmptyMeta,
	}, "c1", nil, nil, nil)
	if err == nil {
		t.Fatalf("expected missing creator error")
	}
}

func TestDefaultClusterScalerManager(t *testing.T) {
	rec := &testReceiver{}
	mgr, err := NewDefaultClusterScalerManager(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}

	scaler, input, output, err := mgr.NewClusterScaler(ClusterScalerMockEmptyMeta, "c1", nil, nil, nil)
	if err != nil {
		t.Fatalf("expected success: %v", err)
	}
	if _, ok := scaler.(ClusterScalerInterfaceV1); !ok {
		t.Fatalf("expected ClusterScalerInterfaceV1")
	}
	if _, ok := input.(InputInterfaceV1); !ok {
		t.Fatalf("expected InputInterfaceV1")
	}
	if _, ok := output.(OutputInterfaceV1); !ok {
		t.Fatalf("expected OutputInterfaceV1")
	}

	_, _, _, err = mgr.NewClusterScaler(
		ClusterScalerPhasedComponentScalerMockV1Meta,
		"c2",
		&InputProviderMockConfigV1{Step: 1},
		&InputProviderMockConfigV1{Step: 1},
		nil,
	)
	if err == nil || !strings.Contains(err.Error(), "config type unmatched") {
		t.Fatalf("expected cluster scaler config type mismatch, got: %v", err)
	}
}

func TestScalingTaskExecutorManager(t *testing.T) {
	rec := &testReceiver{}
	mgr, err := NewDefaultScalingTaskExecutorManager(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}
	meta := &ScalingTaskExecutorMeta{
		ScalerType:             ClusterScalerMockEmptyIdStr,
		ScalerInterfaceVersion: ClusterScalerInterfaceV1IdStr,
		InputInterfaceVersion:  InputInterfaceV1IdStr,
		OutputInterfaceVersion: OutputInterfaceV1IdStr,
		ExecutorName:           ScalerInterfaceV1ExecutorSyncLoopIdStr,
	}

	_, err = mgr.NewExecutor(meta, "c1", "bad", NewInputProviderMockEmpty(rec), NewOutputMockEmpty(rec), &ScalerInterfaceV1ExecutorSyncLoopConfig{})
	if err == nil || !strings.Contains(err.Error(), "scaler interface unmatched") {
		t.Fatalf("expected scaler interface mismatch, got: %v", err)
	}

	scaler := NewClusterScalerMockEmpty(rec, "c1", NewInputProviderMockEmpty(rec), NewOutputMockEmpty(rec))
	_, err = mgr.NewExecutor(meta, "c1", scaler, NewInputProviderMockEmpty(rec), NewOutputMockEmpty(rec), &InputProviderMockConfigV1{})
	if err == nil || !strings.Contains(err.Error(), "executor config unmatched") {
		t.Fatalf("expected executor config mismatch, got: %v", err)
	}

	exec, err := mgr.NewExecutor(meta, "c1", scaler, NewInputProviderMockEmpty(rec), NewOutputMockEmpty(rec), &ScalerInterfaceV1ExecutorSyncLoopConfig{})
	if err != nil {
		t.Fatalf("expected success: %v", err)
	}
	if exec == nil {
		t.Fatalf("expected non nil executor")
	}
}

func TestConfigManagersAddAndGet(t *testing.T) {
	rec := &testReceiver{}

	inMgr := NewInputProviderConfigManager(rec)
	if ok := inMgr.Add(InputProviderConfig{InputProviderMeta: InputProviderMockEmptyMeta}, false); !ok {
		t.Fatalf("expected add success")
	}
	if ok := inMgr.Add(InputProviderConfig{InputProviderMeta: InputProviderMockEmptyMeta}, false); ok {
		t.Fatalf("expected duplicate add failure")
	}
	if ok := inMgr.Add(InputProviderConfig{InputProviderMeta: InputProviderMockEmptyMeta}, true); !ok {
		t.Fatalf("expected replace success")
	}
	if _, ok := inMgr.Get(InputProviderMockEmptyMeta.String()); !ok {
		t.Fatalf("expected auto generated key")
	}

	outMgr := NewOutputConfigManager(rec)
	if ok := outMgr.Add(OutputConfig{OutputMeta: OutputMockEmptyMeta}, false); !ok {
		t.Fatalf("expected add success")
	}
	if ok := outMgr.Add(OutputConfig{OutputMeta: OutputMockEmptyMeta}, false); ok {
		t.Fatalf("expected duplicate add failure")
	}
	if _, ok := outMgr.Get(OutputMockEmptyMeta.String()); !ok {
		t.Fatalf("expected auto generated key")
	}

	scalerMgr := NewScalerConfigManager(rec)
	sc := &ScalerConfig{ScalerMeta: ClusterScalerMockEmptyMeta.ScalerMeta, InputUniqName: "in", OutputUniqName: "out"}
	if ok := scalerMgr.Add(sc, false); !ok {
		t.Fatalf("expected add success")
	}
	if ok := scalerMgr.Add(sc, false); ok {
		t.Fatalf("expected duplicate add failure")
	}
	if _, ok := scalerMgr.Get(sc.UniqName); !ok {
		t.Fatalf("expected auto generated key")
	}
}

func TestClusterScalerConfigManagerReplaceNoDuplicateIterate(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewClusterScalerConfigManager(rec)
	cfg := &ClusterScalerConfig{
		ScalerUniqName:  "s1",
		TargetClusterId: "c1",
		ExecutorName:    ScalerInterfaceV1ExecutorSyncLoopIdStr,
		ScalerConfig:    &ClusterScalerPhasedComponentScalerV1Config{ComponentName: "tidb"},
	}
	if ok := mgr.Add(cfg, false); !ok {
		t.Fatalf("expected first add success")
	}
	cfg2 := &ClusterScalerConfig{
		ScalerUniqName:  "s1",
		TargetClusterId: "c1",
		ExecutorName:    ScalerInterfaceV1ExecutorSyncLoopIdStr,
		ScalerConfig:    &ClusterScalerPhasedComponentScalerV1Config{ComponentName: "tidb"},
		ExecutorConfig:  &ScalerInterfaceV1ExecutorSyncLoopConfig{PauseSecs: 2},
	}
	if ok := mgr.Add(cfg2, true); !ok {
		t.Fatalf("expected replace success")
	}
	count := 0
	mgr.Iterate(func(config *ClusterScalerConfig) bool {
		count++
		if config.ExecutorConfig == nil {
			t.Fatalf("expected replaced config to be visible")
		}
		return true
	})
	if count != 1 {
		t.Fatalf("expected exactly one iterated config, got %d", count)
	}
}

func TestClusterScalerExecutorCreateScalingExecutor(t *testing.T) {
	rec := &testReceiver{}
	exec, err := NewClusterScalerExecutor(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}

	_, err = exec.CreateScalingExecutor(&ClusterScalerConfig{ScalerUniqName: "missing"})
	if err == nil || !strings.Contains(err.Error(), "but not found") {
		t.Fatalf("expected missing scaler error, got: %v", err)
	}

	exec.LoadMockConfig()
	created, err := exec.CreateScalingExecutor(ClusterScalerConfigMockItem1)
	if err != nil {
		t.Fatalf("expected success with mock config: %v", err)
	}
	if created == nil {
		t.Fatalf("expected non nil executor")
	}
}

type testInputV1 struct {
	topology ClusterTopologyV1
	err      error
}

func (i *testInputV1) GetRangeMetrics(clusterId ClusterId, name MetricName, fromTo TimeRange) (MetricTimeValueInt64s, error) {
	return nil, nil
}

func (i *testInputV1) GetClusterTopology(clusterId ClusterId) (ClusterTopologyV1, error) {
	return i.topology, i.err
}

type testScalerV1 struct {
	topology ClusterTopologyV1
	err      error
}

func (s *testScalerV1) PullNewClusterTopology(clusterCurrent ClusterTopologyV1) (ClusterTopologyV1, error) {
	return s.topology, s.err
}

type testOutputV1 struct {
	called   int
	err      error
	current  ClusterTopologyV1
	expected ClusterTopologyV1
}

func (o *testOutputV1) SetNewClusterTopology(clusterId ClusterId, current, expected ClusterTopologyV1) error {
	o.called++
	o.current = current
	o.expected = expected
	return o.err
}

func TestScalerInterfaceV1ExecutorSyncLoopRunOnce(t *testing.T) {
	base := ClusterTopologyV1{
		"tidb": {
			Name:     "tidb",
			Type:     "x",
			Replicas: 1,
		},
	}
	changed := ClusterTopologyV1{
		"tidb": {
			Name:     "tidb",
			Type:     "x",
			Replicas: 2,
		},
	}

	newExecutor := func(input InputInterfaceV1, scaler ClusterScalerInterfaceV1, output OutputInterfaceV1) *ScalerInterfaceV1ExecutorSyncLoop {
		return NewScalerInterfaceV1ExecutorSyncLoop(
			&testReceiver{},
			"c1",
			scaler,
			input,
			output,
			&ScalerInterfaceV1ExecutorSyncLoopConfig{PauseSecs: 1},
		)
	}

	t.Run("input error", func(t *testing.T) {
		exec := newExecutor(&testInputV1{err: errors.New("in")}, &testScalerV1{}, &testOutputV1{})
		reason, err := exec.RunOnce()
		if err == nil || reason != "GetClusterTopology" {
			t.Fatalf("unexpected result: reason=%s err=%v", reason, err)
		}
	})

	t.Run("scaler error", func(t *testing.T) {
		exec := newExecutor(&testInputV1{topology: base}, &testScalerV1{err: errors.New("scale")}, &testOutputV1{})
		reason, err := exec.RunOnce()
		if err == nil || reason != "PullNewClusterTopology" {
			t.Fatalf("unexpected result: reason=%s err=%v", reason, err)
		}
	})

	t.Run("output called when changed", func(t *testing.T) {
		out := &testOutputV1{}
		exec := newExecutor(&testInputV1{topology: base}, &testScalerV1{topology: changed}, out)
		reason, err := exec.RunOnce()
		if err != nil || reason != "" {
			t.Fatalf("unexpected result: reason=%s err=%v", reason, err)
		}
		if out.called != 1 {
			t.Fatalf("expected output to be called once")
		}
	})

	t.Run("output not called when unchanged", func(t *testing.T) {
		out := &testOutputV1{}
		exec := newExecutor(&testInputV1{topology: base}, &testScalerV1{topology: base}, out)
		reason, err := exec.RunOnce()
		if err != nil || reason != "" {
			t.Fatalf("unexpected result: reason=%s err=%v", reason, err)
		}
		if out.called != 0 {
			t.Fatalf("expected output not to be called")
		}
	})

	t.Run("output error", func(t *testing.T) {
		out := &testOutputV1{err: errors.New("out")}
		exec := newExecutor(&testInputV1{topology: base}, &testScalerV1{topology: changed}, out)
		reason, err := exec.RunOnce()
		if err == nil || reason != "SetNewClusterTopology" {
			t.Fatalf("unexpected result: reason=%s err=%v", reason, err)
		}
	})
}

func TestClusterTopologyEqual(t *testing.T) {
	a := ClusterTopologyV1{
		"a": {Name: "a", Type: "x", Replicas: 1},
	}
	b := ClusterTopologyV1{
		"a": {Name: "a", Type: "x", Replicas: 1},
	}
	c := ClusterTopologyV1{
		"a": {Name: "a", Type: "x", Replicas: 2},
	}
	if !a.Equal(b) {
		t.Fatalf("expected equal topologies")
	}
	if a.Equal(c) {
		t.Fatalf("expected different topologies")
	}
}

func TestTimeRangeString(t *testing.T) {
	r := NewTimeRangeLatest(2 * time.Second)
	if !r.From.Before(r.To) {
		t.Fatalf("expected from before to")
	}
	s := r.String()
	if !strings.Contains(s, "[") || !strings.Contains(s, "~") || !strings.Contains(s, ")") {
		t.Fatalf("unexpected range format: %s", s)
	}
}

func TestMetaStringers(t *testing.T) {
	in := (&InputProviderMeta{Type: "t", InterfaceVersion: "v"}).String()
	out := (&OutputMeta{Type: "t2", InterfaceVersion: "v2"}).String()
	scaler := (&UnattachedScalerMeta{Type: "s", InterfaceVersion: "sv"}).String()
	exec := (&ScalingTaskExecutorMeta{
		ScalerType:             "a",
		ScalerInterfaceVersion: "b",
		InputInterfaceVersion:  "c",
		OutputInterfaceVersion: "d",
		ExecutorName:           "e",
	}).String()

	for _, got := range []string{in, out, scaler, exec} {
		if got == "" {
			t.Fatalf("expected non-empty string")
		}
	}
	if !strings.Contains(exec, "ExecutorName:e") {
		t.Fatalf("unexpected executor meta string: %s", exec)
	}
}

func TestCloneableError(t *testing.T) {
	if NewCloneableError(nil) != nil {
		t.Fatalf("expected nil cloneable error for nil input")
	}
	err := errors.New("boom")
	ce := NewCloneableError(err)
	if ce.Error() != "boom" || ce.String() != "boom" {
		t.Fatalf("unexpected cloneable error text")
	}
	if ce.CloneableClone() != ce {
		t.Fatalf("expected clone returns same pointer for cloneable error")
	}
}

func TestEventDataConstructors(t *testing.T) {
	obj := CloneableString("x")
	a := EventDataWithType(EventDataStr, obj)
	b := EventDataWithName("name", obj)
	c := EventDataWithNameType("name2", EventDataBool, CloneableBool(true))

	if a.TypeHint != EventDataStr || a.Object == nil {
		t.Fatalf("unexpected data from EventDataWithType")
	}
	if b.DataName != "name" || b.Object == nil {
		t.Fatalf("unexpected data from EventDataWithName")
	}
	if c.DataName != "name2" || c.TypeHint != EventDataBool {
		t.Fatalf("unexpected data from EventDataWithNameType")
	}
}

func TestDragSlow(t *testing.T) {
	d := NewDragSlow(20 * time.Millisecond)
	start := time.Now()
	d.OnEvent(EventDebug)
	if time.Since(start) < 15*time.Millisecond {
		t.Fatalf("drag slow did not sleep enough")
	}
}

func TestRunMockConfigFlowDoesNotErrorBeforeLoopSetup(t *testing.T) {
	// This test validates wiring helpers without calling RunMock (which blocks in sync loop).
	rec := &testReceiver{}
	exec, err := NewClusterScalerExecutor(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}
	exec.LoadMockConfig()
	if _, err := exec.CreateScalingExecutor(ClusterScalerConfigMockItem1); err != nil {
		t.Fatalf("create scaling executor failed: %v", err)
	}
}

func TestClusterScalerMockEmptyPullNewClusterTopology(t *testing.T) {
	rec := &testReceiver{}
	input := NewInputProviderMockEmpty(rec)
	output := NewOutputMockEmpty(rec)
	s := NewClusterScalerMockEmpty(rec, "c1", input, output)
	top, err := s.PullNewClusterTopology(ClusterTopologyV1{"x": {Name: "x", Type: "t", Replicas: 1}})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if top["x"].Replicas != 1 {
		t.Fatalf("expected unchanged topology")
	}
}

func TestClusterScalerPhasedComponentScalerV1PullNewClusterTopology(t *testing.T) {
	rec := &testReceiver{}
	input := NewInputProviderMockV1(rec, &InputProviderMockConfigV1{Step: 1})
	output := NewOutputMockEmpty(rec)
	componentScaler := NewPhasedComponentScalerMockEmpty(rec, "c1", ComponentName(InstanceNameMock))
	s := NewClusterScalerPhasedComponentScalerV1(rec, "c1", input, output, componentScaler, ComponentName(InstanceNameMock))

	newTopology, err := s.PullNewClusterTopology(ClusterTopologyV1Mock)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if !newTopology.Equal(ClusterTopologyV1Mock) {
		t.Fatalf("expected unchanged topology")
	}

	_, err = s.PullNewClusterTopology(ClusterTopologyV1{})
	if err == nil || !strings.Contains(err.Error(), "not found") {
		t.Fatalf("expected missing component error, got: %v", err)
	}
}

func TestDefaultManagersInteroperate(t *testing.T) {
	rec := &testReceiver{}
	clusterMgr, err := NewDefaultClusterScalerManager(rec)
	if err != nil {
		t.Fatalf("cluster manager init failed: %v", err)
	}
	execMgr, err := NewDefaultScalingTaskExecutorManager(rec)
	if err != nil {
		t.Fatalf("executor manager init failed: %v", err)
	}

	scaler, input, output, err := clusterMgr.NewClusterScaler(ClusterScalerMockEmptyMeta, "c1", nil, nil, nil)
	if err != nil {
		t.Fatalf("new cluster scaler failed: %v", err)
	}
	meta := &ScalingTaskExecutorMeta{
		ScalerType:             ClusterScalerMockEmptyMeta.ScalerMeta.Type,
		ScalerInterfaceVersion: ClusterScalerMockEmptyMeta.ScalerMeta.InterfaceVersion,
		InputInterfaceVersion:  ClusterScalerMockEmptyMeta.InputMeta.InterfaceVersion,
		OutputInterfaceVersion: ClusterScalerMockEmptyMeta.OutputMeta.InterfaceVersion,
		ExecutorName:           ScalerInterfaceV1ExecutorSyncLoopIdStr,
	}

	e, err := execMgr.NewExecutor(meta, "c1", scaler, input, output, &ScalerInterfaceV1ExecutorSyncLoopConfig{PauseSecs: 1})
	if err != nil {
		t.Fatalf("new executor failed: %v", err)
	}
	if fmt.Sprintf("%T", e) == "" {
		t.Fatalf("expected concrete executor")
	}
}

// ==================== Cloneable Tests ====================

func TestCloneableStrings(t *testing.T) {
	cs := CloneableStrings{"a", "b", "c"}
	cloned := cs.CloneableClone()
	clonedSlice, ok := cloned.(CloneableStrings)
	if !ok {
		t.Fatalf("expected CloneableStrings")
	}
	if len(clonedSlice) != 3 || clonedSlice[0] != "a" {
		t.Fatalf("expected cloned slice to match")
	}
}

func TestCloneableInt(t *testing.T) {
	ci := CloneableInt(42)
	cloned := ci.CloneableClone()
	clonedInt, ok := cloned.(CloneableInt)
	if !ok {
		t.Fatalf("expected CloneableInt")
	}
	if clonedInt != 42 {
		t.Fatalf("expected cloned value to match")
	}
}

func TestCloneableBool(t *testing.T) {
	cb := CloneableBool(true)
	cloned := cb.CloneableClone()
	clonedBool, ok := cloned.(CloneableBool)
	if !ok {
		t.Fatalf("expected CloneableBool")
	}
	if !clonedBool {
		t.Fatalf("expected cloned value to match")
	}
}

func TestCloneableString(t *testing.T) {
	cs := CloneableString("hello")
	cloned := cs.CloneableClone()
	clonedStr, ok := cloned.(CloneableString)
	if !ok {
		t.Fatalf("expected CloneableString")
	}
	if clonedStr != "hello" {
		t.Fatalf("expected cloned value to match")
	}
}

// ==================== Meta String Tests ====================

func TestUnattachedScalerMetaString(t *testing.T) {
	meta := UnattachedScalerMeta{Type: "test", InterfaceVersion: "v1"}
	s := meta.String()
	if s != "Type:test;InterfaceVersion:v1" {
		t.Fatalf("unexpected meta string: %s", s)
	}
}

func TestClusterScalerMetaString(t *testing.T) {
	meta := ClusterScalerMeta{
		ScalerMeta: UnattachedScalerMeta{Type: "s", InterfaceVersion: "sv"},
		InputMeta:  InputProviderMeta{Type: "i", InterfaceVersion: "iv"},
		OutputMeta: OutputMeta{Type: "o", InterfaceVersion: "ov"},
	}
	s := meta.String()
	if !strings.Contains(s, "ScalerMeta:") || !strings.Contains(s, "InputMeta:") || !strings.Contains(s, "OutputMeta:") {
		t.Fatalf("unexpected cluster scaler meta string: %s", s)
	}
}

func TestInputProviderMetaString(t *testing.T) {
	meta := InputProviderMeta{Type: "input", InterfaceVersion: "v1"}
	s := meta.String()
	if s != "Type:input;InterfaceVersion:v1" {
		t.Fatalf("unexpected input provider meta string: %s", s)
	}
}

func TestOutputMetaString(t *testing.T) {
	meta := OutputMeta{Type: "output", InterfaceVersion: "v1"}
	s := meta.String()
	if s != "Type:output;InterfaceVersion:v1" {
		t.Fatalf("unexpected output meta string: %s", s)
	}
}

func TestScalingTaskExecutorMetaString(t *testing.T) {
	meta := ScalingTaskExecutorMeta{
		ScalerType:             "st",
		ScalerInterfaceVersion: "sv",
		InputInterfaceVersion:  "iv",
		OutputInterfaceVersion: "ov",
		ExecutorName:           "en",
	}
	s := meta.String()
	if !strings.Contains(s, "ScalerType:st") || !strings.Contains(s, "ExecutorName:en") {
		t.Fatalf("unexpected executor meta string: %s", s)
	}
}

// ==================== Input/Output Provider Mock Tests ====================

func TestInputProviderMockEmptyGetClusterTopology(t *testing.T) {
	rec := &testReceiver{}
	input := NewInputProviderMockEmpty(rec)
	topology, err := input.GetClusterTopology("c1")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(topology) != 0 {
		t.Fatalf("expected empty topology")
	}
	if rec.count(EventInputV1GetTopology) != 1 {
		t.Fatalf("expected event to be emitted")
	}
}

func TestInputProviderMockEmptyGetRangeMetrics(t *testing.T) {
	rec := &testReceiver{}
	input := NewInputProviderMockEmpty(rec)
	tr := NewTimeRangeLatest(time.Second)
	metrics, err := input.GetRangeMetrics("c1", "cpu", tr)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if metrics != nil {
		t.Fatalf("expected nil metrics")
	}
	if rec.count(EventInputV1GetRangeMetrics) != 1 {
		t.Fatalf("expected event to be emitted")
	}
}

func TestInputProviderMockV1GetClusterTopology(t *testing.T) {
	rec := &testReceiver{}
	input := NewInputProviderMockV1(rec, &InputProviderMockConfigV1{Step: 1})
	topology, err := input.GetClusterTopology("c1")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if !topology.Equal(ClusterTopologyV1Mock) {
		t.Fatalf("expected mock topology")
	}
	if rec.count(EventInputV1GetTopology) != 1 {
		t.Fatalf("expected event to be emitted")
	}
}

func TestInputProviderMockV1GetRangeMetrics(t *testing.T) {
	rec := &testReceiver{}
	input := NewInputProviderMockV1(rec, &InputProviderMockConfigV1{Step: 1})
	tr := NewTimeRangeLatest(time.Second)
	metrics, err := input.GetRangeMetrics("c1", "memory", tr)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if metrics != nil {
		t.Fatalf("expected nil metrics")
	}
	if rec.count(EventInputV1GetRangeMetrics) != 1 {
		t.Fatalf("expected event to be emitted")
	}
}

func TestInputProviderMockConfigV1UniqStr(t *testing.T) {
	cfg := &InputProviderMockConfigV1{Step: 5}
	s := cfg.UniqStr()
	if s != "" {
		t.Fatalf("expected empty uniq string")
	}
}

func TestOutputMockEmptySetNewClusterTopology(t *testing.T) {
	rec := &testReceiver{}
	output := NewOutputMockEmpty(rec)
	current := ClusterTopologyV1{"a": {Name: "a", Type: "t", Replicas: 1}}
	expected := ClusterTopologyV1{"a": {Name: "a", Type: "t", Replicas: 2}}
	err := output.SetNewClusterTopology("c1", current, expected)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if rec.count(EventOutputV1SetNewClusterTopology) != 1 {
		t.Fatalf("expected event to be emitted")
	}
}

// ==================== ClusterTopologyV1 Equal Edge Cases ====================

func TestClusterTopologyEqualEmpty(t *testing.T) {
	a := ClusterTopologyV1{}
	b := ClusterTopologyV1{}
	if !a.Equal(b) {
		t.Fatalf("expected equal empty topologies")
	}
}

func TestClusterTopologyEqualDifferentLength(t *testing.T) {
	a := ClusterTopologyV1{"a": {Name: "a", Type: "t", Replicas: 1}}
	b := ClusterTopologyV1{
		"a": {Name: "a", Type: "t", Replicas: 1},
		"b": {Name: "b", Type: "t", Replicas: 1},
	}
	if a.Equal(b) {
		t.Fatalf("expected different length topologies to be unequal")
	}
}

func TestClusterTopologyEqualDifferentKeys(t *testing.T) {
	a := ClusterTopologyV1{"a": {Name: "a", Type: "t", Replicas: 1}}
	b := ClusterTopologyV1{"b": {Name: "b", Type: "t", Replicas: 1}}
	if a.Equal(b) {
		t.Fatalf("expected different key topologies to be unequal")
	}
}

func TestClusterTopologyEqualDifferentReplicas(t *testing.T) {
	a := ClusterTopologyV1{"a": {Name: "a", Type: "t", Replicas: 1}}
	b := ClusterTopologyV1{"a": {Name: "a", Type: "t", Replicas: 2}}
	if a.Equal(b) {
		t.Fatalf("expected different replicas topologies to be unequal")
	}
}

func TestClusterTopologyEqualDifferentType(t *testing.T) {
	a := ClusterTopologyV1{"a": {Name: "a", Type: "t1", Replicas: 1}}
	b := ClusterTopologyV1{"a": {Name: "a", Type: "t2", Replicas: 1}}
	if a.Equal(b) {
		t.Fatalf("expected different type topologies to be unequal")
	}
}

// ==================== Manager Error Path Tests ====================

func TestInputProviderManagerRegisterDuplicate(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewInputProviderManager(rec)
	meta := InputProviderMeta{Type: "dup", InterfaceVersion: InputInterfaceV1IdStr}
	creator := func(EventReceiver, ConfigData) (any, error) { return NewInputProviderMockEmpty(rec), nil }

	if err := mgr.Register(meta, creator); err != nil {
		t.Fatalf("first register failed: %v", err)
	}
	if err := mgr.Register(meta, creator); err == nil {
		t.Fatalf("expected duplicate register error")
	}
	if rec.count(EventInputProviderManagerRegisterCreatorDuplicated) != 1 {
		t.Fatalf("expected duplicated event")
	}
}

func TestOutputManagerRegisterDuplicate(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewOutputManager(rec)
	meta := OutputMeta{Type: "dup", InterfaceVersion: OutputInterfaceV1IdStr}
	creator := func(EventReceiver, ConfigData) (any, error) { return NewOutputMockEmpty(rec), nil }

	if err := mgr.Register(meta, creator); err != nil {
		t.Fatalf("first register failed: %v", err)
	}
	if err := mgr.Register(meta, creator); err == nil {
		t.Fatalf("expected duplicate register error")
	}
	if rec.count(EventOutputManagerRegisterCreatorDuplicated) != 1 {
		t.Fatalf("expected duplicated event")
	}
}

func TestOutputManagerCreatorNotFound(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewOutputManager(rec)
	_, err := mgr.NewOutput(OutputMeta{Type: "missing", InterfaceVersion: OutputInterfaceV1IdStr}, nil)
	if err == nil {
		t.Fatalf("expected creator not found error")
	}
	if rec.count(EventOutputManagerCreatorNotFound) != 1 {
		t.Fatalf("expected creator not found event")
	}
}

func TestPhasedComponentScalerManagerRegisterDuplicate(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewPhasedComponentScalerManager(rec)
	creator := func(EventReceiver, ClusterId, ComponentName, ConfigData, any, any) (any, error) {
		return NewPhasedComponentScalerMockEmpty(rec, "c1", "tidb"), nil
	}

	if err := mgr.Register("dup", creator); err != nil {
		t.Fatalf("first register failed: %v", err)
	}
	if err := mgr.Register("dup", creator); err == nil {
		t.Fatalf("expected duplicate register error")
	}
	if rec.count(EventPhasedComponentScalerManagerRegisterCreatorDuplicated) != 1 {
		t.Fatalf("expected duplicated event")
	}
}

func TestPhasedComponentScalerManagerCreatorNotFound(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewPhasedComponentScalerManager(rec)
	_, err := mgr.NewPhasedComponentScaler("missing", "c1", "tidb", nil, nil, nil)
	if err == nil {
		t.Fatalf("expected creator not found error")
	}
	if rec.count(EventPhasedComponentScalerManagerCreatorNotFound) != 1 {
		t.Fatalf("expected creator not found event")
	}
}

func TestScalingTaskExecutorManagerRegisterDuplicate(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewScalingTaskExecutorManager(rec)
	meta := &ScalingTaskExecutorMeta{
		ScalerType:             "test",
		ScalerInterfaceVersion: ClusterScalerInterfaceV1IdStr,
		InputInterfaceVersion:  InputInterfaceV1IdStr,
		OutputInterfaceVersion: OutputInterfaceV1IdStr,
		ExecutorName:           ScalerInterfaceV1ExecutorSyncLoopIdStr,
	}
	creator := func(EventReceiver, ClusterId, any, any, any, ConfigData) (ScalingTaskExecutor, error) {
		return nil, errors.New("test error")
	}

	if err := mgr.Register(meta, creator); err != nil {
		t.Fatalf("first register failed: %v", err)
	}
	if err := mgr.Register(meta, creator); err == nil {
		t.Fatalf("expected duplicate register error")
	}
	if rec.count(EventScalingTaskExecutorManagerRegisterCreatorDuplicated) != 1 {
		t.Fatalf("expected duplicated event")
	}
}

func TestScalingTaskExecutorManagerCreatorNotFound(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewScalingTaskExecutorManager(rec)
	meta := &ScalingTaskExecutorMeta{
		ScalerType:             "missing",
		ScalerInterfaceVersion: ClusterScalerInterfaceV1IdStr,
		InputInterfaceVersion:  InputInterfaceV1IdStr,
		OutputInterfaceVersion: OutputInterfaceV1IdStr,
		ExecutorName:           ScalerInterfaceV1ExecutorSyncLoopIdStr,
	}
	_, err := mgr.NewExecutor(meta, "c1", nil, nil, nil, nil)
	if err == nil {
		t.Fatalf("expected creator not found error")
	}
	if rec.count(EventScalingTaskExecutorManagerCreatorNotFound) != 1 {
		t.Fatalf("expected creator not found event")
	}
}

func TestClusterScalerManagerRegisterDuplicate(t *testing.T) {
	rec := &testReceiver{}
	inMgr, _ := NewDefaultInputProviderManager(rec)
	outMgr, _ := NewDefaultOutputManager(rec)
	mgr, _ := NewClusterScalerManager(rec, inMgr, outMgr)

	meta := &ClusterScalerMeta{
		ScalerMeta: UnattachedScalerMeta{Type: "dup", InterfaceVersion: ClusterScalerInterfaceV1IdStr},
		InputMeta:  InputProviderMockEmptyMeta,
		OutputMeta: OutputMockEmptyMeta,
	}
	creator := func(EventReceiver, ClusterId, ConfigData, any, any) (any, error) {
		return NewClusterScalerMockEmpty(rec, "c1", NewInputProviderMockEmpty(rec), NewOutputMockEmpty(rec)), nil
	}

	if err := mgr.Register(meta, creator); err != nil {
		t.Fatalf("first register failed: %v", err)
	}
	if err := mgr.Register(meta, creator); err == nil {
		t.Fatalf("expected duplicate register error")
	}
	if rec.count(EventClusterScalerManagerRegisterCreatorDuplicated) != 1 {
		t.Fatalf("expected duplicated event")
	}
}

func TestClusterScalerManagerCreatorNotFound(t *testing.T) {
	rec := &testReceiver{}
	inMgr, _ := NewDefaultInputProviderManager(rec)
	outMgr, _ := NewDefaultOutputManager(rec)
	mgr, _ := NewClusterScalerManager(rec, inMgr, outMgr)

	meta := &ClusterScalerMeta{
		ScalerMeta: UnattachedScalerMeta{Type: "missing", InterfaceVersion: ClusterScalerInterfaceV1IdStr},
		InputMeta:  InputProviderMockEmptyMeta,
		OutputMeta: OutputMockEmptyMeta,
	}
	_, _, _, err := mgr.NewClusterScaler(meta, "c1", nil, nil, nil)
	if err == nil {
		t.Fatalf("expected creator not found error")
	}
	if rec.count(EventClusterScalerManagerCreatorNotFound) != 1 {
		t.Fatalf("expected creator not found event")
	}
}

// ==================== V2 Interface Validation Tests ====================

// stubInputV2 implements InputInterfaceV2 for testing.
type stubInputV2 struct {
	InputProviderMockEmpty // embeds V1
}

func (stubInputV2) GetRangeCPUUtilization(_ context.Context, _ ClusterId, _ TimeRange) ([]CPUDataPoint, error) {
	return nil, nil
}
func (stubInputV2) GetPerNodeDiskInfo(_ context.Context, _ string) ([]NodeDiskInfo, error) {
	return nil, nil
}
func (stubInputV2) GetDiskSnapshot(_ context.Context, _ string) (*TiKVUsageSnapshot, error) {
	return &TiKVUsageSnapshot{}, nil
}
func (stubInputV2) GetEBSCooldownState(_ context.Context, _ string) (*VolumeCooldownState, error) {
	return &VolumeCooldownState{}, nil
}
func (stubInputV2) GetCurrentDiskSizeGB(_ context.Context, _ string) (uint32, error) { return 0, nil }
func (stubInputV2) GetTiKVMaxNodeCount(_ context.Context, _ string) (uint32, error)  { return 0, nil }
func (stubInputV2) GetAutoscaleFeatureFlags(_ context.Context, _ string) (*TiKVAutoscaleFeatureFlags, error) {
	return &TiKVAutoscaleFeatureFlags{}, nil
}
func (stubInputV2) GetDiskIOMetrics(_ context.Context, _ string) (*DiskIOMetrics, error) {
	return &DiskIOMetrics{}, nil
}
func (stubInputV2) GetRangeDiskIOMetrics(_ context.Context, _ string, _ TimeRange) ([]DiskIODataPoint, error) {
	return nil, nil
}
func (stubInputV2) GetProvisionedDiskIO(_ context.Context, _ string) (*ProvisionedDiskIO, error) {
	return &ProvisionedDiskIO{}, nil
}
func (stubInputV2) GetCollectInterval() time.Duration { return 15 * time.Second }

// stubOutputV2 implements OutputInterfaceV2 for testing.
type stubOutputV2 struct {
	OutputMockEmpty // embeds V1
}

func (stubOutputV2) ExecuteAction(_ ClusterId, _ *ScaleAction) error { return nil }
func (stubOutputV2) SetPendingAction(_ string, _ *ScaleAction)       {}

func TestInputProviderManagerV2Validation(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewInputProviderManager(rec)

	v2Meta := InputProviderMeta{Type: "v2test", InterfaceVersion: InputInterfaceV2IdStr}

	// V1-only object registered under V2 meta → should fail validation.
	if err := mgr.Register(v2Meta, func(er EventReceiver, _ ConfigData) (any, error) {
		return NewInputProviderMockEmpty(er), nil // V1 only
	}); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	_, err := mgr.NewInput(v2Meta, nil)
	if err == nil {
		t.Fatal("expected error: V1-only input should not pass V2 validation")
	}
	if !strings.Contains(err.Error(), "InputInterfaceV2") {
		t.Fatalf("error should mention InputInterfaceV2, got: %v", err)
	}

	// V2 object registered under V2 meta → should pass.
	mgr2 := NewInputProviderManager(rec)
	if err := mgr2.Register(v2Meta, func(er EventReceiver, _ ConfigData) (any, error) {
		return &stubInputV2{InputProviderMockEmpty: *NewInputProviderMockEmpty(er)}, nil
	}); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	input, err := mgr2.NewInput(v2Meta, nil)
	if err != nil {
		t.Fatalf("unexpected error for V2 input: %v", err)
	}
	if _, ok := input.(InputInterfaceV2); !ok {
		t.Fatal("returned input should implement InputInterfaceV2")
	}
}

func TestOutputManagerV1Validation(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewOutputManager(rec)

	v1Meta := OutputMeta{Type: "v1test", InterfaceVersion: OutputInterfaceV1IdStr}

	// Non-OutputInterfaceV1 object → should fail.
	if err := mgr.Register(v1Meta, func(_ EventReceiver, _ ConfigData) (any, error) {
		return "not-an-output", nil
	}); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	_, err := mgr.NewOutput(v1Meta, nil)
	if err == nil {
		t.Fatal("expected error: string should not pass V1 validation")
	}
	if !strings.Contains(err.Error(), "OutputInterfaceV1") {
		t.Fatalf("error should mention OutputInterfaceV1, got: %v", err)
	}
}

func TestOutputManagerV2Validation(t *testing.T) {
	rec := &testReceiver{}

	v2Meta := OutputMeta{Type: "v2test", InterfaceVersion: OutputInterfaceV2IdStr}

	// V1-only object registered under V2 meta → should fail.
	mgr := NewOutputManager(rec)
	if err := mgr.Register(v2Meta, func(er EventReceiver, _ ConfigData) (any, error) {
		return NewOutputMockEmpty(er), nil // V1 only
	}); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	_, err := mgr.NewOutput(v2Meta, nil)
	if err == nil {
		t.Fatal("expected error: V1-only output should not pass V2 validation")
	}
	if !strings.Contains(err.Error(), "OutputInterfaceV2") {
		t.Fatalf("error should mention OutputInterfaceV2, got: %v", err)
	}

	// V2 object registered under V2 meta → should pass.
	mgr2 := NewOutputManager(rec)
	if err := mgr2.Register(v2Meta, func(er EventReceiver, _ ConfigData) (any, error) {
		return &stubOutputV2{OutputMockEmpty: *NewOutputMockEmpty(er)}, nil
	}); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	output, err := mgr2.NewOutput(v2Meta, nil)
	if err != nil {
		t.Fatalf("unexpected error for V2 output: %v", err)
	}
	if _, ok := output.(OutputInterfaceV2); !ok {
		t.Fatal("returned output should implement OutputInterfaceV2")
	}
}

// ==================== Config Manager Tests ====================

func TestInputProviderConfigManagerAddAndGet(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewInputProviderConfigManager(rec)

	cfg := InputProviderConfig{
		InputProviderMeta: InputProviderMockEmptyMeta,
		UniqName:          "test",
	}
	if ok := mgr.Add(cfg, false); !ok {
		t.Fatalf("expected add success")
	}
	if ok := mgr.Add(cfg, false); ok {
		t.Fatalf("expected duplicate add failure")
	}
	if ok := mgr.Add(cfg, true); !ok {
		t.Fatalf("expected replace success")
	}

	got, ok := mgr.Get("test")
	if !ok {
		t.Fatalf("expected to get config")
	}
	if got.UniqName != "test" {
		t.Fatalf("expected correct config")
	}
}

func TestInputProviderConfigManagerAutoGenUniqName(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewInputProviderConfigManager(rec)

	cfg := InputProviderConfig{
		InputProviderMeta: InputProviderMockEmptyMeta,
		UniqName:          "",
	}
	if ok := mgr.Add(cfg, false); !ok {
		t.Fatalf("expected add success")
	}

	expectedKey := InputProviderMockEmptyMeta.String()
	_, ok := mgr.Get(expectedKey)
	if !ok {
		t.Fatalf("expected auto-generated key: %s", expectedKey)
	}
}

func TestOutputConfigManagerAddAndGet(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewOutputConfigManager(rec)

	cfg := OutputConfig{
		OutputMeta: OutputMockEmptyMeta,
		UniqName:   "test",
	}
	if ok := mgr.Add(cfg, false); !ok {
		t.Fatalf("expected add success")
	}
	if ok := mgr.Add(cfg, false); ok {
		t.Fatalf("expected duplicate add failure")
	}

	got, ok := mgr.Get("test")
	if !ok {
		t.Fatalf("expected to get config")
	}
	if got.UniqName != "test" {
		t.Fatalf("expected correct config")
	}
}

func TestOutputConfigManagerAutoGenUniqName(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewOutputConfigManager(rec)

	cfg := OutputConfig{
		OutputMeta: OutputMockEmptyMeta,
		UniqName:   "",
	}
	if ok := mgr.Add(cfg, false); !ok {
		t.Fatalf("expected add success")
	}

	expectedKey := OutputMockEmptyMeta.String()
	_, ok := mgr.Get(expectedKey)
	if !ok {
		t.Fatalf("expected auto-generated key: %s", expectedKey)
	}
}

func TestScalerConfigManagerAddAndGet(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewScalerConfigManager(rec)

	cfg := &ScalerConfig{
		UniqName:   "test",
		ScalerMeta: UnattachedScalerMeta{Type: "s", InterfaceVersion: "v"},
	}
	if ok := mgr.Add(cfg, false); !ok {
		t.Fatalf("expected add success")
	}
	if ok := mgr.Add(cfg, false); ok {
		t.Fatalf("expected duplicate add failure")
	}

	got, ok := mgr.Get("test")
	if !ok {
		t.Fatalf("expected to get config")
	}
	if got.UniqName != "test" {
		t.Fatalf("expected correct config")
	}
}

func TestScalerConfigManagerAutoGenUniqName(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewScalerConfigManager(rec)

	cfg := &ScalerConfig{
		UniqName:       "",
		ScalerMeta:     UnattachedScalerMeta{Type: "s", InterfaceVersion: "v"},
		InputUniqName:  "in",
		OutputUniqName: "out",
	}
	if ok := mgr.Add(cfg, false); !ok {
		t.Fatalf("expected add success")
	}

	expectedKey := cfg.autoGenUniqName()
	_, ok := mgr.Get(expectedKey)
	if !ok {
		t.Fatalf("expected auto-generated key: %s", expectedKey)
	}
}

func TestScalerConfigString(t *testing.T) {
	cfg := ScalerConfig{
		UniqName:       "test",
		ScalerMeta:     UnattachedScalerMeta{Type: "s", InterfaceVersion: "v"},
		InputUniqName:  "in",
		OutputUniqName: "out",
	}
	s := cfg.String()
	if !strings.Contains(s, "UniqName:test") || !strings.Contains(s, "InputUniqName:in") {
		t.Fatalf("unexpected scaler config string: %s", s)
	}
}

func TestOutputConfigString(t *testing.T) {
	cfg := OutputConfig{
		OutputMeta: OutputMockEmptyMeta,
		UniqName:   "test",
	}
	s := cfg.String()
	if !strings.Contains(s, "UniqName:test") {
		t.Fatalf("unexpected output config string: %s", s)
	}
}

func TestInputProviderConfigString(t *testing.T) {
	cfg := InputProviderConfig{
		InputProviderMeta: InputProviderMockEmptyMeta,
		UniqName:          "test",
	}
	s := cfg.String()
	if !strings.Contains(s, "UniqName:test") {
		t.Fatalf("unexpected input provider config string: %s", s)
	}
}

func TestClusterScalerConfigUniqName(t *testing.T) {
	cfg := ClusterScalerConfig{
		ScalerUniqName:  "s1",
		TargetClusterId: "c1",
		ExecutorName:    ScalerInterfaceV1ExecutorSyncLoopIdStr,
		ScalerConfig:    nil,
	}
	s := cfg.UniqName()
	if !strings.Contains(s, "ScalerName:s1") || !strings.Contains(s, "ClusterId:c1") {
		t.Fatalf("unexpected cluster scaler config uniq name: %s", s)
	}
}

// ==================== ClusterScalerExecutor Tests ====================

func TestClusterScalerExecutorCreateScalingExecutorScalerNotFound(t *testing.T) {
	rec := &testReceiver{}
	exec, err := NewClusterScalerExecutor(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}
	exec.LoadMockConfig()

	cfg := &ClusterScalerConfig{
		ScalerUniqName:  "nonexistent",
		TargetClusterId: "c1",
		ExecutorName:    ScalerInterfaceV1ExecutorSyncLoopIdStr,
	}
	_, err = exec.CreateScalingExecutor(cfg)
	if err == nil || !strings.Contains(err.Error(), "not found") {
		t.Fatalf("expected scaler not found error, got: %v", err)
	}
}

func TestClusterScalerExecutorCreateScalingExecutorInputNotFound(t *testing.T) {
	rec := &testReceiver{}
	exec, err := NewClusterScalerExecutor(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}
	exec.LoadMockConfig()

	cfg := &ClusterScalerConfig{
		ScalerUniqName:  ScalerConfigMockItem1.UniqName,
		TargetClusterId: "c1",
		ExecutorName:    ScalerInterfaceV1ExecutorSyncLoopIdStr,
	}
	exec.scalerConfigs.configs[ScalerConfigMockItem1.UniqName] = ScalerConfig{
		UniqName:       ScalerConfigMockItem1.UniqName,
		ScalerMeta:     ClusterScalerPhasedComponentScalerMockV1Meta.ScalerMeta,
		InputUniqName:  "nonexistent",
		OutputUniqName: OutputConfigMockItem0.UniqName,
	}
	_, err = exec.CreateScalingExecutor(cfg)
	if err == nil || !strings.Contains(err.Error(), "not found") {
		t.Fatalf("expected input not found error, got: %v", err)
	}
}

func TestClusterScalerExecutorCreateScalingExecutorOutputNotFound(t *testing.T) {
	rec := &testReceiver{}
	exec, err := NewClusterScalerExecutor(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}
	exec.LoadMockConfig()

	cfg := &ClusterScalerConfig{
		ScalerUniqName:  ScalerConfigMockItem1.UniqName,
		TargetClusterId: "c1",
		ExecutorName:    ScalerInterfaceV1ExecutorSyncLoopIdStr,
	}
	exec.scalerConfigs.configs[ScalerConfigMockItem1.UniqName] = ScalerConfig{
		UniqName:       ScalerConfigMockItem1.UniqName,
		ScalerMeta:     ClusterScalerPhasedComponentScalerMockV1Meta.ScalerMeta,
		InputUniqName:  InputProviderConfigMockItem1.UniqName,
		OutputUniqName: "nonexistent",
	}
	_, err = exec.CreateScalingExecutor(cfg)
	if err == nil || !strings.Contains(err.Error(), "not found") {
		t.Fatalf("expected output not found error, got: %v", err)
	}
}

func TestClusterScalerExecutorLoadConfigFromFile(t *testing.T) {
	rec := &testReceiver{}
	exec, err := NewClusterScalerExecutor(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}
	err = exec.LoadConfigFromFile()
	if err != nil {
		t.Fatalf("LoadConfigFromFile should not error: %v", err)
	}
}

// ==================== ClusterScalerPhasedComponentScalerV1 Tests ====================

func TestClusterScalerPhasedComponentScalerV1ConfigFillBlankValuesFromNilAnother(t *testing.T) {
	cfg := &ClusterScalerPhasedComponentScalerV1Config{ComponentName: "tidb"}
	merged := cfg.FillBlankValuesFrom(nil)
	if merged != cfg {
		t.Fatalf("expected same config when another is nil")
	}
}

func TestClusterScalerPhasedComponentScalerV1ConfigFillBlankValuesFromWrongType(t *testing.T) {
	cfg := &ClusterScalerPhasedComponentScalerV1Config{ComponentName: "tidb"}
	wrongType := &InputProviderMockConfigV1{Step: 1}
	merged := cfg.FillBlankValuesFrom(wrongType)
	if merged != cfg {
		t.Fatalf("expected same config when another is wrong type")
	}
}

func TestClusterScalerPhasedComponentScalerV1PullNewClusterTopologyModificationEvent(t *testing.T) {
	rec := &testReceiver{}
	input := NewInputProviderMockV1(rec, &InputProviderMockConfigV1{Step: 1})
	output := NewOutputMockEmpty(rec)
	// PhasedComponentScalerMockEmpty returns current.Replicas unchanged
	componentScaler := NewPhasedComponentScalerMockEmpty(rec, "c1", ComponentName(InstanceNameMock))

	s := NewClusterScalerPhasedComponentScalerV1(rec, "c1", input, output, componentScaler, ComponentName(InstanceNameMock))

	topology := ClusterTopologyV1{
		InstanceNameMock: ClusterComponentV1{
			Name:     InstanceNameMock,
			Type:     InstanceTypeMock,
			Replicas: 1,
		},
	}
	newTopology, err := s.PullNewClusterTopology(topology)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	// Mock empty returns current.Replicas unchanged, so no modification
	if newTopology[InstanceNameMock].Replicas != 1 {
		t.Fatalf("expected replicas unchanged with mock empty")
	}
	// No modification event since replicas didn't change
	if rec.count(EventClusterScalerPhasedComponentScalerV1PullNewClusterTopologyModification) != 0 {
		t.Fatalf("expected no modification event when replicas unchanged")
	}
}

// ==================== ScalerInterfaceV1ExecutorSyncLoop Tests ====================

func TestScalerInterfaceV1ExecutorSyncLoopConfigFillBlankValuesFromNilAnother(t *testing.T) {
	cfg := &ScalerInterfaceV1ExecutorSyncLoopConfig{PauseSecs: 5}
	merged := cfg.FillBlankValuesFrom(nil)
	if merged != cfg {
		t.Fatalf("expected same config when another is nil")
	}
}

func TestScalerInterfaceV1ExecutorSyncLoopConfigFillBlankValuesFromWrongType(t *testing.T) {
	cfg := &ScalerInterfaceV1ExecutorSyncLoopConfig{PauseSecs: 5}
	wrongType := &InputProviderMockConfigV1{Step: 1}
	merged := cfg.FillBlankValuesFrom(wrongType)
	if merged != cfg {
		t.Fatalf("expected same config when another is wrong type")
	}
}

func TestScalerInterfaceV1ExecutorSyncLoopConfigUniqStr(t *testing.T) {
	cfg := &ScalerInterfaceV1ExecutorSyncLoopConfig{PauseSecs: 5}
	s := cfg.UniqStr()
	if s != "" {
		t.Fatalf("expected empty uniq string")
	}
}

// ==================== AsyncEventHub Tests ====================

func TestAsyncEventHubNoReceivers(t *testing.T) {
	hub := NewAsyncEventHub()
	hub.OnEvent(EventDebug, EventDataWithName("test", CloneableString("data")))
}

func TestAsyncEventHubMultipleReceivers(t *testing.T) {
	rec1 := &testReceiver{}
	rec2 := &testReceiver{}
	hub := NewAsyncEventHub()
	hub.RegisterReceiver(rec1)
	hub.RegisterReceiver(rec2)

	hub.OnEvent(EventDebug, EventDataWithName("test", CloneableString("data")))

	if rec1.count(EventDebug) != 1 {
		t.Fatalf("expected rec1 to receive event")
	}
	if rec2.count(EventDebug) != 1 {
		t.Fatalf("expected rec2 to receive event")
	}
}

// ==================== GeneralLogger Tests ====================

func TestGeneralLoggerEmptyPrefix(t *testing.T) {
	buf := bytes.NewBuffer(nil)
	logger := NewGeneralLogger("", buf)
	logger.OnEvent(EventDebug)
	out := buf.String()
	if !strings.Contains(out, "debug") {
		t.Fatalf("expected event name in output: %q", out)
	}
}

func TestGeneralLoggerWithData(t *testing.T) {
	buf := bytes.NewBuffer(nil)
	logger := NewGeneralLogger("test", buf)
	logger.OnEvent(EventDebug,
		EventDataWithName("key", CloneableString("value")),
		EventDataWithNameType("num", EventDataInt, CloneableInt(42)))
	out := buf.String()
	if !strings.Contains(out, "'key'") || !strings.Contains(out, "'num'") {
		t.Fatalf("expected data names in output: %q", out)
	}
}

// ==================== EventFilters Tests ====================

func TestEventAllowFilterMultipleEvents(t *testing.T) {
	rec := &testReceiver{}
	filter := NewEventAllowFilter(rec, EventDebug, EventAny)

	filter.OnEvent(EventDebug)
	filter.OnEvent(EventAny)
	filter.OnEvent("other")

	if rec.count(EventDebug) != 1 {
		t.Fatalf("expected debug event")
	}
	if rec.count(EventAny) != 1 {
		t.Fatalf("expected any event")
	}
	if rec.count("other") != 0 {
		t.Fatalf("expected no other event")
	}
}

func TestEventDenyFilterMultipleEvents(t *testing.T) {
	rec := &testReceiver{}
	filter := NewEventDenyFilter(rec, EventAny)

	filter.OnEvent(EventDebug)
	filter.OnEvent(EventAny)
	filter.OnEvent("other")

	if rec.count(EventDebug) != 1 {
		t.Fatalf("expected debug event")
	}
	if rec.count(EventAny) != 0 {
		t.Fatalf("expected no any event")
	}
	if rec.count("other") != 1 {
		t.Fatalf("expected other event")
	}
}

// ==================== EventsCounter Tests ====================

func TestEventsCounterMultipleEvents(t *testing.T) {
	counter := NewEventsCounter(EventDebug, EventAny)
	counter.OnEvent(EventDebug)
	counter.OnEvent(EventDebug)
	counter.OnEvent(EventAny)

	if counter.GetCount(EventDebug) != 2 {
		t.Fatalf("expected debug count 2")
	}
	if counter.GetCount(EventAny) != 1 {
		t.Fatalf("expected any count 1")
	}
}

func TestEventsCounterUnknownEvent(t *testing.T) {
	counter := NewEventsCounter(EventDebug)
	counter.OnEvent("unknown")

	if counter.GetCount("unknown") != 1 {
		t.Fatalf("expected unknown count 1")
	}
}

// ==================== SyncEventHub Tests ====================

func TestSyncEventHubMultipleReceivers(t *testing.T) {
	rec1 := &testReceiver{}
	rec2 := &testReceiver{}
	hub := NewSyncEventHub()
	hub.RegisterReceiver(rec1)
	hub.RegisterReceiver(rec2)

	hub.OnEvent(EventDebug, EventDataWithName("test", CloneableString("data")))

	if rec1.count(EventDebug) != 1 {
		t.Fatalf("expected rec1 to receive event")
	}
	if rec2.count(EventDebug) != 1 {
		t.Fatalf("expected rec2 to receive event")
	}
}

// ==================== DragSlow Tests ====================

func TestDragSlowNoSleepOnNonMatchedEvents(t *testing.T) {
	d := NewDragSlow(0)
	start := time.Now()
	d.OnEvent(EventDebug)
	if time.Since(start) > 10*time.Millisecond {
		t.Fatalf("drag slow slept too long")
	}
}

// ==================== ClusterScalerMockEmpty Tests ====================

func TestClusterScalerMockEmptyWithNilTopology(t *testing.T) {
	rec := &testReceiver{}
	input := NewInputProviderMockEmpty(rec)
	output := NewOutputMockEmpty(rec)
	s := NewClusterScalerMockEmpty(rec, "c1", input, output)

	// nil topology is returned as-is
	top, err := s.PullNewClusterTopology(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if top != nil {
		t.Fatalf("expected nil topology to be returned as-is")
	}
}

// ==================== PhasedComponentScalerMockEmpty Tests ====================

func TestPhasedComponentScalerMockEmpty(t *testing.T) {
	rec := &testReceiver{}
	scaler := NewPhasedComponentScalerMockEmpty(rec, "c1", "tidb")

	newReplicas, err := scaler.PullNewComponentModification(ClusterComponentV1{
		Name:     "tidb",
		Type:     "8C16G",
		Replicas: 2,
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if newReplicas != 2 {
		t.Fatalf("expected unchanged replicas")
	}
}

// ==================== InputProviderMockV1Config Tests ====================

func TestInputProviderMockConfigV1FillBlankValuesFromNilAnother(t *testing.T) {
	cfg := &InputProviderMockConfigV1{Step: 5}
	merged := cfg.FillBlankValuesFrom(nil)
	if merged != cfg {
		t.Fatalf("expected same config when another is nil")
	}
}

func TestInputProviderMockConfigV1FillBlankValuesFromWrongType(t *testing.T) {
	cfg := &InputProviderMockConfigV1{Step: 5}
	wrongType := &ScalerInterfaceV1ExecutorSyncLoopConfig{PauseSecs: 1}
	merged := cfg.FillBlankValuesFrom(wrongType)
	if merged != cfg {
		t.Fatalf("expected same config when another is wrong type")
	}
}

// ==================== ClusterScalerConfigManager Tests ====================

func TestClusterScalerConfigManagerIterateEarlyStop(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewClusterScalerConfigManager(rec)
	cfg1 := &ClusterScalerConfig{
		ScalerUniqName:  "s1",
		TargetClusterId: "c1",
		ExecutorName:    ScalerInterfaceV1ExecutorSyncLoopIdStr,
	}
	cfg2 := &ClusterScalerConfig{
		ScalerUniqName:  "s2",
		TargetClusterId: "c2",
		ExecutorName:    ScalerInterfaceV1ExecutorSyncLoopIdStr,
	}
	mgr.Add(cfg1, false)
	mgr.Add(cfg2, false)

	count := 0
	mgr.Iterate(func(config *ClusterScalerConfig) bool {
		count++
		return false
	})
	if count != 1 {
		t.Fatalf("expected iterate to stop early")
	}
}

// ==================== TimeRange Tests ====================

func TestTimeRangeStringDifferentTimezones(t *testing.T) {
	r := NewTimeRangeLatest(time.Hour)
	s := r.String()
	if !strings.Contains(s, "[") || !strings.Contains(s, "~") {
		t.Fatalf("unexpected range format: %s", s)
	}
}

// ==================== CloneableError Tests ====================

func TestCloneableErrorNil(t *testing.T) {
	ce := NewCloneableError(nil)
	if ce != nil {
		t.Fatalf("expected nil cloneable error for nil input")
	}
}

// ==================== EventData Tests ====================

func TestEventDataCloneNilTypeHint(t *testing.T) {
	ed := EventData{DataName: "test", TypeHint: EventDataUnknown, Object: CloneableString("value")}
	cloned := ed.Clone()
	if cloned.DataName != "test" || cloned.TypeHint != EventDataUnknown {
		t.Fatalf("expected cloned data to match")
	}
}

// ==================== Default Manager Initialization Tests ====================

func TestNewDefaultInputProviderManagerErrorOnRegister(t *testing.T) {
	rec := &testReceiver{}
	_, err := NewDefaultInputProviderManager(rec)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestNewDefaultOutputManagerErrorOnRegister(t *testing.T) {
	rec := &testReceiver{}
	_, err := NewDefaultOutputManager(rec)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestNewDefaultClusterScalerManagerErrorOnScalerRegister(t *testing.T) {
	rec := &testReceiver{}
	_, err := NewDefaultClusterScalerManager(rec)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
}

// ==================== ClusterScalerPhasedComponentScalerV1Meta Tests ====================

func TestClusterScalerPhasedComponentScalerV1Meta(t *testing.T) {
	if ClusterScalerPhasedComponentScalerV1Meta.ScalerMeta.Type != ClusterScalerPhasedComponentScalerIdStr {
		t.Fatalf("unexpected meta type")
	}
	if ClusterScalerPhasedComponentScalerV1Meta.InputMeta.InterfaceVersion != InputInterfaceV1IdStr {
		t.Fatalf("unexpected input interface version")
	}
}

// ==================== ConfigData Merge Tests ====================

func TestMergeConfigDataBothNil(t *testing.T) {
	result := MergeConfigData(nil, nil)
	if result != nil {
		t.Fatalf("expected nil result")
	}
}

// ==================== Comprehensive Manager Duplicate Detection Tests ====================

// TestInputProviderManager_Register_DuplicateDetection tests all field combinations
// Key fields: Type, InterfaceVersion (2 fields)
func TestInputProviderManager_Register_DuplicateDetection(t *testing.T) {
	baseType := InputProviderType("mock-input")
	baseVer := InputInterfaceVersion("v1")
	altType := InputProviderType("mock-input-alt")
	altVer := InputInterfaceVersion("v2")

	tests := []struct {
		name            string
		firstMeta       InputProviderMeta
		secondMeta      InputProviderMeta
		expectDuplicate bool
	}{
		{
			name: "identical_all_fields",
			firstMeta: InputProviderMeta{
				Type:             baseType,
				InterfaceVersion: baseVer,
			},
			secondMeta: InputProviderMeta{
				Type:             baseType,
				InterfaceVersion: baseVer,
			},
			expectDuplicate: true,
		},
		{
			name: "different_type_only",
			firstMeta: InputProviderMeta{
				Type:             baseType,
				InterfaceVersion: baseVer,
			},
			secondMeta: InputProviderMeta{
				Type:             altType,
				InterfaceVersion: baseVer,
			},
			expectDuplicate: false,
		},
		{
			name: "different_interface_version_only",
			firstMeta: InputProviderMeta{
				Type:             baseType,
				InterfaceVersion: baseVer,
			},
			secondMeta: InputProviderMeta{
				Type:             baseType,
				InterfaceVersion: altVer,
			},
			expectDuplicate: false,
		},
		{
			name: "different_all_fields",
			firstMeta: InputProviderMeta{
				Type:             baseType,
				InterfaceVersion: baseVer,
			},
			secondMeta: InputProviderMeta{
				Type:             altType,
				InterfaceVersion: altVer,
			},
			expectDuplicate: false,
		},
	}

	dummyCreator := func(EventReceiver, ConfigData) (any, error) { return nil, nil } //nolint:nilnil // dummy creator for testing

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rec := &testReceiver{}
			mgr := NewInputProviderManager(rec)

			err := mgr.Register(tt.firstMeta, dummyCreator)
			if err != nil {
				t.Fatalf("first register failed: %v", err)
			}

			err = mgr.Register(tt.secondMeta, dummyCreator)
			if tt.expectDuplicate {
				if err == nil {
					t.Fatal("expected duplicate error, got nil")
				}
				if rec.count(EventInputProviderManagerRegisterCreatorDuplicated) != 1 {
					t.Fatal("expected duplicated event")
				}
				if rec.count(EventInputProviderManagerRegisterCreatorOk) != 1 {
					t.Fatal("expected exactly one ok event from first register")
				}
			} else {
				if err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if rec.count(EventInputProviderManagerRegisterCreatorOk) != 2 {
					t.Fatal("expected two ok events")
				}
			}
		})
	}
}

// TestOutputManager_Register_DuplicateDetection tests all field combinations
// Key fields: Type, InterfaceVersion (2 fields)
func TestOutputManager_Register_DuplicateDetection(t *testing.T) {
	baseType := OutputType("mock-output")
	baseVer := OutputInterfaceVersion("v1")
	altType := OutputType("mock-output-alt")
	altVer := OutputInterfaceVersion("v2")

	tests := []struct {
		name            string
		firstMeta       OutputMeta
		secondMeta      OutputMeta
		expectDuplicate bool
	}{
		{
			name: "identical_all_fields",
			firstMeta: OutputMeta{
				Type:             baseType,
				InterfaceVersion: baseVer,
			},
			secondMeta: OutputMeta{
				Type:             baseType,
				InterfaceVersion: baseVer,
			},
			expectDuplicate: true,
		},
		{
			name: "different_type_only",
			firstMeta: OutputMeta{
				Type:             baseType,
				InterfaceVersion: baseVer,
			},
			secondMeta: OutputMeta{
				Type:             altType,
				InterfaceVersion: baseVer,
			},
			expectDuplicate: false,
		},
		{
			name: "different_interface_version_only",
			firstMeta: OutputMeta{
				Type:             baseType,
				InterfaceVersion: baseVer,
			},
			secondMeta: OutputMeta{
				Type:             baseType,
				InterfaceVersion: altVer,
			},
			expectDuplicate: false,
		},
		{
			name: "different_all_fields",
			firstMeta: OutputMeta{
				Type:             baseType,
				InterfaceVersion: baseVer,
			},
			secondMeta: OutputMeta{
				Type:             altType,
				InterfaceVersion: altVer,
			},
			expectDuplicate: false,
		},
	}

	dummyCreator := func(EventReceiver, ConfigData) (any, error) { return nil, nil } //nolint:nilnil // dummy creator for testing

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rec := &testReceiver{}
			mgr := NewOutputManager(rec)

			err := mgr.Register(tt.firstMeta, dummyCreator)
			if err != nil {
				t.Fatalf("first register failed: %v", err)
			}

			err = mgr.Register(tt.secondMeta, dummyCreator)
			if tt.expectDuplicate {
				if err == nil {
					t.Fatal("expected duplicate error, got nil")
				}
				if rec.count(EventOutputManagerRegisterCreatorDuplicated) != 1 {
					t.Fatal("expected duplicated event")
				}
				if rec.count(EventOutputManagerRegisterCreatorOk) != 1 {
					t.Fatal("expected exactly one ok event from first register")
				}
			} else {
				if err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if rec.count(EventOutputManagerRegisterCreatorOk) != 2 {
					t.Fatal("expected two ok events")
				}
			}
		})
	}
}

// TestPhasedComponentScalerManager_Register_DuplicateDetection tests all field combinations
// Key fields: scalerType (1 field - simple string)
func TestPhasedComponentScalerManager_Register_DuplicateDetection(t *testing.T) {
	tests := []struct {
		name            string
		firstType       PhasedComponentScalerType
		secondType      PhasedComponentScalerType
		expectDuplicate bool
	}{
		{
			name:            "identical_type",
			firstType:       "scaler-type-a",
			secondType:      "scaler-type-a",
			expectDuplicate: true,
		},
		{
			name:            "different_type",
			firstType:       "scaler-type-a",
			secondType:      "scaler-type-b",
			expectDuplicate: false,
		},
		{
			name:            "empty_string_type",
			firstType:       "",
			secondType:      "",
			expectDuplicate: true,
		},
		{
			name:            "empty_vs_nonempty",
			firstType:       "",
			secondType:      "nonempty",
			expectDuplicate: false,
		},
	}

	dummyCreator := func(EventReceiver, ClusterId, ComponentName, ConfigData, any, any) (any, error) {
		return nil, nil //nolint:nilnil // dummy creator for testing
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rec := &testReceiver{}
			mgr := NewPhasedComponentScalerManager(rec)

			err := mgr.Register(tt.firstType, dummyCreator)
			if err != nil {
				t.Fatalf("first register failed: %v", err)
			}

			err = mgr.Register(tt.secondType, dummyCreator)
			if tt.expectDuplicate {
				if err == nil {
					t.Fatal("expected duplicate error, got nil")
				}
				if rec.count(EventPhasedComponentScalerManagerRegisterCreatorDuplicated) != 1 {
					t.Fatal("expected duplicated event")
				}
				if rec.count(EventPhasedComponentScalerManagerRegisterCreatorOk) != 1 {
					t.Fatal("expected exactly one ok event from first register")
				}
			} else {
				if err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if rec.count(EventPhasedComponentScalerManagerRegisterCreatorOk) != 2 {
					t.Fatal("expected two ok events")
				}
			}
		})
	}
}

// TestClusterScalerManager_Register_DuplicateDetection tests all field combinations
// Key fields: ScalerMeta.Type, ScalerMeta.InterfaceVersion, InputMeta.InterfaceVersion, OutputMeta.InterfaceVersion
// NOTE: InputMeta.Type and OutputMeta.Type are NOT part of the key!
func TestClusterScalerManager_Register_DuplicateDetection(t *testing.T) {
	baseScalerType := ClusterScalerType("scaler-a")
	altScalerType := ClusterScalerType("scaler-b")
	baseScalerVer := ClusterScalerInterfaceVersion("sv1")
	altScalerVer := ClusterScalerInterfaceVersion("sv2")
	baseInputVer := InputInterfaceVersion("iv1")
	altInputVer := InputInterfaceVersion("iv2")
	baseOutputVer := OutputInterfaceVersion("ov1")
	altOutputVer := OutputInterfaceVersion("ov2")
	baseInputType := InputProviderType("input-a")
	altInputType := InputProviderType("input-b")
	baseOutputType := OutputType("output-a")
	altOutputType := OutputType("output-b")

	tests := []struct {
		name            string
		firstMeta       *ClusterScalerMeta
		secondMeta      *ClusterScalerMeta
		expectDuplicate bool
	}{
		{
			name: "identical_all_key_fields",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			expectDuplicate: true,
		},
		{
			name: "different_input_type_only_not_a_key_field",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: altInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			expectDuplicate: true,
		},
		{
			name: "different_output_type_only_not_a_key_field",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: altOutputType, InterfaceVersion: baseOutputVer},
			},
			expectDuplicate: true,
		},
		{
			name: "different_input_and_output_type_not_key_fields",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: altInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: altOutputType, InterfaceVersion: baseOutputVer},
			},
			expectDuplicate: true,
		},
		{
			name: "different_scaler_type_only",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: altScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			expectDuplicate: false,
		},
		{
			name: "different_scaler_interface_version_only",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: altScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			expectDuplicate: false,
		},
		{
			name: "different_input_interface_version_only",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: altInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			expectDuplicate: false,
		},
		{
			name: "different_output_interface_version_only",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: altOutputVer},
			},
			expectDuplicate: false,
		},
		{
			name: "different_two_key_fields_scaler_type_and_input_ver",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: altScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: altInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			expectDuplicate: false,
		},
		{
			name: "different_three_key_fields",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: altScalerType, InterfaceVersion: altScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: altInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			expectDuplicate: false,
		},
		{
			name: "different_all_four_key_fields",
			firstMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: baseScalerType, InterfaceVersion: baseScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: baseInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: baseOutputVer},
			},
			secondMeta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: altScalerType, InterfaceVersion: altScalerVer},
				InputMeta:  InputProviderMeta{Type: baseInputType, InterfaceVersion: altInputVer},
				OutputMeta: OutputMeta{Type: baseOutputType, InterfaceVersion: altOutputVer},
			},
			expectDuplicate: false,
		},
	}

	dummyCreator := func(EventReceiver, ClusterId, ConfigData, any, any) (any, error) {
		return nil, nil //nolint:nilnil // dummy creator for testing
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rec := &testReceiver{}
			inMgr, _ := NewDefaultInputProviderManager(rec)
			outMgr, _ := NewDefaultOutputManager(rec)
			mgr, _ := NewClusterScalerManager(rec, inMgr, outMgr)

			err := mgr.Register(tt.firstMeta, dummyCreator)
			if err != nil {
				t.Fatalf("first register failed: %v", err)
			}

			err = mgr.Register(tt.secondMeta, dummyCreator)
			if tt.expectDuplicate {
				if err == nil {
					t.Fatal("expected duplicate error, got nil")
				}
				if rec.count(EventClusterScalerManagerRegisterCreatorDuplicated) != 1 {
					t.Fatal("expected duplicated event")
				}
				if rec.count(EventClusterScalerManagerRegisterCreatorOk) != 1 {
					t.Fatal("expected exactly one ok event from first register")
				}
			} else {
				if err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if rec.count(EventClusterScalerManagerRegisterCreatorOk) != 2 {
					t.Fatal("expected two ok events")
				}
			}
		})
	}
}

// TestScalingTaskExecutorManager_Register_DuplicateDetection tests all field combinations
// Key fields: ScalerType, ScalerInterfaceVersion, InputInterfaceVersion, OutputInterfaceVersion, ExecutorName (5 fields)
func TestScalingTaskExecutorManager_Register_DuplicateDetection(t *testing.T) {
	baseScalerType := ClusterScalerType("scaler-a")
	altScalerType := ClusterScalerType("scaler-b")
	baseScalerVer := ClusterScalerInterfaceVersion("sv1")
	altScalerVer := ClusterScalerInterfaceVersion("sv2")
	baseInputVer := InputInterfaceVersion("iv1")
	altInputVer := InputInterfaceVersion("iv2")
	baseOutputVer := OutputInterfaceVersion("ov1")
	altOutputVer := OutputInterfaceVersion("ov2")
	baseExecName := ScalingTaskExecutorName("exec-a")
	altExecName := ScalingTaskExecutorName("exec-b")

	tests := []struct {
		name            string
		firstMeta       *ScalingTaskExecutorMeta
		secondMeta      *ScalingTaskExecutorMeta
		expectDuplicate bool
	}{
		{
			name: "identical_all_five_fields",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			expectDuplicate: true,
		},
		{
			name: "different_scaler_type_only",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             altScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			expectDuplicate: false,
		},
		{
			name: "different_scaler_interface_version_only",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: altScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			expectDuplicate: false,
		},
		{
			name: "different_input_interface_version_only",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  altInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			expectDuplicate: false,
		},
		{
			name: "different_output_interface_version_only",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: altOutputVer,
				ExecutorName:           baseExecName,
			},
			expectDuplicate: false,
		},
		{
			name: "different_executor_name_only",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           altExecName,
			},
			expectDuplicate: false,
		},
		{
			name: "different_two_fields_scaler_type_and_scaler_ver",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             altScalerType,
				ScalerInterfaceVersion: altScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			expectDuplicate: false,
		},
		{
			name: "different_two_fields_input_ver_and_output_ver",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  altInputVer,
				OutputInterfaceVersion: altOutputVer,
				ExecutorName:           baseExecName,
			},
			expectDuplicate: false,
		},
		{
			name: "different_three_fields",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             altScalerType,
				ScalerInterfaceVersion: altScalerVer,
				InputInterfaceVersion:  altInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			expectDuplicate: false,
		},
		{
			name: "different_four_fields",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             altScalerType,
				ScalerInterfaceVersion: altScalerVer,
				InputInterfaceVersion:  altInputVer,
				OutputInterfaceVersion: altOutputVer,
				ExecutorName:           baseExecName,
			},
			expectDuplicate: false,
		},
		{
			name: "different_all_five_fields",
			firstMeta: &ScalingTaskExecutorMeta{
				ScalerType:             baseScalerType,
				ScalerInterfaceVersion: baseScalerVer,
				InputInterfaceVersion:  baseInputVer,
				OutputInterfaceVersion: baseOutputVer,
				ExecutorName:           baseExecName,
			},
			secondMeta: &ScalingTaskExecutorMeta{
				ScalerType:             altScalerType,
				ScalerInterfaceVersion: altScalerVer,
				InputInterfaceVersion:  altInputVer,
				OutputInterfaceVersion: altOutputVer,
				ExecutorName:           altExecName,
			},
			expectDuplicate: false,
		},
	}

	dummyCreator := func(EventReceiver, ClusterId, any, any, any, ConfigData) (ScalingTaskExecutor, error) {
		return nil, nil //nolint:nilnil // dummy creator for testing
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rec := &testReceiver{}
			mgr := NewScalingTaskExecutorManager(rec)

			err := mgr.Register(tt.firstMeta, dummyCreator)
			if err != nil {
				t.Fatalf("first register failed: %v", err)
			}

			err = mgr.Register(tt.secondMeta, dummyCreator)
			if tt.expectDuplicate {
				if err == nil {
					t.Fatal("expected duplicate error, got nil")
				}
				if rec.count(EventScalingTaskExecutorManagerRegisterCreatorDuplicated) != 1 {
					t.Fatal("expected duplicated event")
				}
				if rec.count(EventScalingTaskExecutorManagerRegisterCreatorOk) != 1 {
					t.Fatal("expected exactly one ok event from first register")
				}
			} else {
				if err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if rec.count(EventScalingTaskExecutorManagerRegisterCreatorOk) != 2 {
					t.Fatal("expected two ok events")
				}
			}
		})
	}
}

// TestScalingTaskExecutorManager_Register_MultipleDistinct tests registering multiple distinct executors
func TestScalingTaskExecutorManager_Register_MultipleDistinct(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewScalingTaskExecutorManager(rec)

	dummyCreator := func(EventReceiver, ClusterId, any, any, any, ConfigData) (ScalingTaskExecutor, error) {
		return nil, nil //nolint:nilnil // dummy creator for testing
	}

	metas := []*ScalingTaskExecutorMeta{
		{ScalerType: "type-a", ScalerInterfaceVersion: "sv1", InputInterfaceVersion: "iv1", OutputInterfaceVersion: "ov1", ExecutorName: "exec1"},
		{ScalerType: "type-a", ScalerInterfaceVersion: "sv1", InputInterfaceVersion: "iv1", OutputInterfaceVersion: "ov1", ExecutorName: "exec2"},
		{ScalerType: "type-a", ScalerInterfaceVersion: "sv1", InputInterfaceVersion: "iv2", OutputInterfaceVersion: "ov1", ExecutorName: "exec1"},
		{ScalerType: "type-b", ScalerInterfaceVersion: "sv1", InputInterfaceVersion: "iv1", OutputInterfaceVersion: "ov1", ExecutorName: "exec1"},
		{ScalerType: "type-a", ScalerInterfaceVersion: "sv2", InputInterfaceVersion: "iv1", OutputInterfaceVersion: "ov1", ExecutorName: "exec1"},
		{ScalerType: "type-a", ScalerInterfaceVersion: "sv1", InputInterfaceVersion: "iv1", OutputInterfaceVersion: "ov2", ExecutorName: "exec1"},
	}

	for i, meta := range metas {
		err := mgr.Register(meta, dummyCreator)
		if err != nil {
			t.Fatalf("register %d failed: %v", i, err)
		}
	}

	if rec.count(EventScalingTaskExecutorManagerRegisterCreatorOk) != len(metas) {
		t.Fatalf("expected %d ok events, got %d", len(metas), rec.count(EventScalingTaskExecutorManagerRegisterCreatorOk))
	}
	if rec.count(EventScalingTaskExecutorManagerRegisterCreatorDuplicated) != 0 {
		t.Fatal("expected no duplicated events")
	}
}

// TestClusterScalerManager_Register_MultipleDistinct tests registering multiple distinct scalers
func TestClusterScalerManager_Register_MultipleDistinct(t *testing.T) {
	rec := &testReceiver{}
	inMgr, _ := NewDefaultInputProviderManager(rec)
	outMgr, _ := NewDefaultOutputManager(rec)
	mgr, _ := NewClusterScalerManager(rec, inMgr, outMgr)

	dummyCreator := func(EventReceiver, ClusterId, ConfigData, any, any) (any, error) {
		return nil, nil //nolint:nilnil //for test only
	}

	metas := []*ClusterScalerMeta{
		{ScalerMeta: UnattachedScalerMeta{Type: "s1", InterfaceVersion: "sv1"}, InputMeta: InputProviderMeta{Type: "i1", InterfaceVersion: "iv1"}, OutputMeta: OutputMeta{Type: "o1", InterfaceVersion: "ov1"}},
		{ScalerMeta: UnattachedScalerMeta{Type: "s2", InterfaceVersion: "sv1"}, InputMeta: InputProviderMeta{Type: "i1", InterfaceVersion: "iv1"}, OutputMeta: OutputMeta{Type: "o1", InterfaceVersion: "ov1"}},
		{ScalerMeta: UnattachedScalerMeta{Type: "s1", InterfaceVersion: "sv2"}, InputMeta: InputProviderMeta{Type: "i1", InterfaceVersion: "iv1"}, OutputMeta: OutputMeta{Type: "o1", InterfaceVersion: "ov1"}},
		{ScalerMeta: UnattachedScalerMeta{Type: "s1", InterfaceVersion: "sv1"}, InputMeta: InputProviderMeta{Type: "i1", InterfaceVersion: "iv2"}, OutputMeta: OutputMeta{Type: "o1", InterfaceVersion: "ov1"}},
		{ScalerMeta: UnattachedScalerMeta{Type: "s1", InterfaceVersion: "sv1"}, InputMeta: InputProviderMeta{Type: "i1", InterfaceVersion: "iv1"}, OutputMeta: OutputMeta{Type: "o1", InterfaceVersion: "ov2"}},
	}

	for i, meta := range metas {
		err := mgr.Register(meta, dummyCreator)
		if err != nil {
			t.Fatalf("register %d failed: %v", i, err)
		}
	}

	if rec.count(EventClusterScalerManagerRegisterCreatorOk) != len(metas) {
		t.Fatalf("expected %d ok events, got %d", len(metas), rec.count(EventClusterScalerManagerRegisterCreatorOk))
	}
	if rec.count(EventClusterScalerManagerRegisterCreatorDuplicated) != 0 {
		t.Fatal("expected no duplicated events")
	}
}

// TestInputProviderManager_Register_MultipleDistinct tests registering multiple distinct input providers
func TestInputProviderManager_Register_MultipleDistinct(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewInputProviderManager(rec)

	dummyCreator := func(EventReceiver, ConfigData) (any, error) { return nil, nil } //nolint:nilnil // dummy creator for testing

	metas := []InputProviderMeta{
		{Type: "type-a", InterfaceVersion: "v1"},
		{Type: "type-a", InterfaceVersion: "v2"},
		{Type: "type-b", InterfaceVersion: "v1"},
		{Type: "type-b", InterfaceVersion: "v2"},
	}

	for i, meta := range metas {
		err := mgr.Register(meta, dummyCreator)
		if err != nil {
			t.Fatalf("register %d failed: %v", i, err)
		}
	}

	if rec.count(EventInputProviderManagerRegisterCreatorOk) != len(metas) {
		t.Fatalf("expected %d ok events, got %d", len(metas), rec.count(EventInputProviderManagerRegisterCreatorOk))
	}
	if rec.count(EventInputProviderManagerRegisterCreatorDuplicated) != 0 {
		t.Fatal("expected no duplicated events")
	}
}

// TestOutputManager_Register_MultipleDistinct tests registering multiple distinct outputs
func TestOutputManager_Register_MultipleDistinct(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewOutputManager(rec)

	dummyCreator := func(EventReceiver, ConfigData) (any, error) { return nil, nil } //nolint:nilnil // dummy creator for testing

	metas := []OutputMeta{
		{Type: "type-a", InterfaceVersion: "v1"},
		{Type: "type-a", InterfaceVersion: "v2"},
		{Type: "type-b", InterfaceVersion: "v1"},
		{Type: "type-b", InterfaceVersion: "v2"},
	}

	for i, meta := range metas {
		err := mgr.Register(meta, dummyCreator)
		if err != nil {
			t.Fatalf("register %d failed: %v", i, err)
		}
	}

	if rec.count(EventOutputManagerRegisterCreatorOk) != len(metas) {
		t.Fatalf("expected %d ok events, got %d", len(metas), rec.count(EventOutputManagerRegisterCreatorOk))
	}
	if rec.count(EventOutputManagerRegisterCreatorDuplicated) != 0 {
		t.Fatal("expected no duplicated events")
	}
}

// TestPhasedComponentScalerManager_Register_MultipleDistinct tests registering multiple distinct component scalers
func TestPhasedComponentScalerManager_Register_MultipleDistinct(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewPhasedComponentScalerManager(rec)

	dummyCreator := func(EventReceiver, ClusterId, ComponentName, ConfigData, any, any) (any, error) {
		return nil, nil //nolint:nilnil // dummy creator for testing
	}

	types := []PhasedComponentScalerType{"scaler-a", "scaler-b", "scaler-c"}

	for i, scalerType := range types {
		err := mgr.Register(scalerType, dummyCreator)
		if err != nil {
			t.Fatalf("register %d failed: %v", i, err)
		}
	}

	if rec.count(EventPhasedComponentScalerManagerRegisterCreatorOk) != len(types) {
		t.Fatalf("expected %d ok events, got %d", len(types), rec.count(EventPhasedComponentScalerManagerRegisterCreatorOk))
	}
	if rec.count(EventPhasedComponentScalerManagerRegisterCreatorDuplicated) != 0 {
		t.Fatal("expected no duplicated events")
	}
}

// TestScalingTaskExecutorMeta_ValueSemantics tests that map uses value semantics for struct key
func TestScalingTaskExecutorMeta_ValueSemantics(t *testing.T) {
	rec := &testReceiver{}
	mgr := NewScalingTaskExecutorManager(rec)

	dummyCreator := func(EventReceiver, ClusterId, any, any, any, ConfigData) (ScalingTaskExecutor, error) {
		return nil, nil //nolint:nilnil //for test only
	}

	meta1 := &ScalingTaskExecutorMeta{
		ScalerType:             "test",
		ScalerInterfaceVersion: "sv1",
		InputInterfaceVersion:  "iv1",
		OutputInterfaceVersion: "ov1",
		ExecutorName:           "exec",
	}

	meta2 := &ScalingTaskExecutorMeta{
		ScalerType:             "test",
		ScalerInterfaceVersion: "sv1",
		InputInterfaceVersion:  "iv1",
		OutputInterfaceVersion: "ov1",
		ExecutorName:           "exec",
	}

	err := mgr.Register(meta1, dummyCreator)
	if err != nil {
		t.Fatalf("first register failed: %v", err)
	}

	err = mgr.Register(meta2, dummyCreator)
	if err == nil {
		t.Fatal("expected duplicate error: different pointer but same value should be duplicate")
	}
}

// TestClusterScalerCreatorUniqId tests the unique ID generation for ClusterScalerManager
func TestClusterScalerCreatorUniqId(t *testing.T) {
	tests := []struct {
		name     string
		meta     *ClusterScalerMeta
		expected string
	}{
		{
			name: "basic_meta",
			meta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: "scaler", InterfaceVersion: "sv1"},
				InputMeta:  InputProviderMeta{Type: "input", InterfaceVersion: "iv1"},
				OutputMeta: OutputMeta{Type: "output", InterfaceVersion: "ov1"},
			},
			expected: "Type:scaler;InterfaceVersion:sv1;InputInterfaceVersion:iv1;OutputInterfaceVersion:ov1",
		},
		{
			name: "empty_strings",
			meta: &ClusterScalerMeta{
				ScalerMeta: UnattachedScalerMeta{Type: "", InterfaceVersion: ""},
				InputMeta:  InputProviderMeta{Type: "", InterfaceVersion: ""},
				OutputMeta: OutputMeta{Type: "", InterfaceVersion: ""},
			},
			expected: "Type:;InterfaceVersion:;InputInterfaceVersion:;OutputInterfaceVersion:",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			uniqId := getClusterScalerCreatorUniqId(tt.meta)
			if string(uniqId) != tt.expected {
				t.Fatalf("expected %q, got %q", tt.expected, uniqId)
			}
		})
	}
}

// TestClusterScalerManager_InputOutputTypeNotInKey verifies InputMeta.Type and OutputMeta.Type don't affect uniqueness
func TestClusterScalerManager_InputOutputTypeNotInKey(t *testing.T) {
	rec := &testReceiver{}
	inMgr, _ := NewDefaultInputProviderManager(rec)
	outMgr, _ := NewDefaultOutputManager(rec)
	mgr, _ := NewClusterScalerManager(rec, inMgr, outMgr)

	dummyCreator := func(EventReceiver, ClusterId, ConfigData, any, any) (any, error) {
		return nil, nil //nolint:nilnil //for test only
	}

	baseMeta := &ClusterScalerMeta{
		ScalerMeta: UnattachedScalerMeta{Type: "scaler", InterfaceVersion: "sv1"},
		InputMeta:  InputProviderMeta{Type: "input-a", InterfaceVersion: "iv1"},
		OutputMeta: OutputMeta{Type: "output-a", InterfaceVersion: "ov1"},
	}

	err := mgr.Register(baseMeta, dummyCreator)
	if err != nil {
		t.Fatalf("first register failed: %v", err)
	}

	differentInputTypeMeta := &ClusterScalerMeta{
		ScalerMeta: UnattachedScalerMeta{Type: "scaler", InterfaceVersion: "sv1"},
		InputMeta:  InputProviderMeta{Type: "input-b", InterfaceVersion: "iv1"},
		OutputMeta: OutputMeta{Type: "output-a", InterfaceVersion: "ov1"},
	}

	err = mgr.Register(differentInputTypeMeta, dummyCreator)
	if err == nil {
		t.Fatal("expected duplicate error: input type differs but should not matter")
	}

	differentOutputTypeMeta := &ClusterScalerMeta{
		ScalerMeta: UnattachedScalerMeta{Type: "scaler", InterfaceVersion: "sv1"},
		InputMeta:  InputProviderMeta{Type: "input-a", InterfaceVersion: "iv1"},
		OutputMeta: OutputMeta{Type: "output-b", InterfaceVersion: "ov1"},
	}

	mgr2, _ := NewClusterScalerManager(rec, inMgr, outMgr)
	_ = mgr2.Register(baseMeta, dummyCreator)
	err = mgr2.Register(differentOutputTypeMeta, dummyCreator)
	if err == nil {
		t.Fatal("expected duplicate error: output type differs but should not matter")
	}
}

// ==================== RunOne Tests ====================

func TestRunOne_StartsAndCancels(t *testing.T) {
	rec := &testReceiver{}
	exec, err := NewClusterScalerExecutor(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}
	exec.LoadMockConfig()

	cancel, _, err := exec.RunOne(ClusterScalerConfigMockItem1)
	if err != nil {
		t.Fatalf("RunOne failed: %v", err)
	}
	if cancel == nil {
		t.Fatal("expected non-nil cancel func")
	}

	// Cancel should not panic.
	cancel()
}

func TestRunOne_ErrorOnMissingScaler(t *testing.T) {
	rec := &testReceiver{}
	exec, err := NewClusterScalerExecutor(rec)
	if err != nil {
		t.Fatalf("init failed: %v", err)
	}

	_, _, err = exec.RunOne(&ClusterScalerConfig{ScalerUniqName: "nonexistent"})
	if err == nil {
		t.Fatal("expected error for missing scaler")
	}
}

// ==================== V1 Executor Context Cancellation ====================

func TestV1ExecutorSyncLoop_ContextCancel(t *testing.T) {
	input := &testInputV1{
		topology: ClusterTopologyV1{"comp": {Name: "comp", Replicas: 1}},
	}
	scaler := &testScalerV1{topology: input.topology}
	output := &testOutputV1{}
	rec := &testReceiver{}
	cfg := NewDefaultScalerInterfaceV1ExecutorSyncLoopConfig()
	cfg.PauseSecs = 1

	executor := NewScalerInterfaceV1ExecutorSyncLoop(rec, "c1", scaler, input, output, cfg)
	ctx, cancel := context.WithCancel(context.Background())

	done := make(chan error, 1)
	go func() { done <- executor.Run(ctx) }()

	cancel()

	select {
	case err := <-done:
		if !errors.Is(err, context.Canceled) {
			t.Errorf("expected context.Canceled, got %v", err)
		}
	case <-time.After(3 * time.Second):
		t.Fatal("executor.Run did not exit after context cancel")
	}
}

// ==================== V2 Executor RunOnce Tests ====================

type testScalerV2 struct {
	result *ClusterScalerResult
	err    error
}

func (s *testScalerV2) PullClusterDecision(current ClusterTopologyV1) (*ClusterScalerResult, error) {
	if s.err != nil {
		return nil, s.err
	}
	r := *s.result
	r.DesiredTopology = current
	return &r, nil
}

type testOutputV2ForExec struct {
	setTopoCalled    bool
	executeActCalled bool
	pendingAction    *ScaleAction
	setTopoErr       error
	executeActErr    error
}

func (o *testOutputV2ForExec) SetNewClusterTopology(_ ClusterId, _, _ ClusterTopologyV1) error {
	o.setTopoCalled = true
	return o.setTopoErr
}

func (o *testOutputV2ForExec) ExecuteAction(_ ClusterId, a *ScaleAction) error {
	o.executeActCalled = true
	return o.executeActErr
}

func (o *testOutputV2ForExec) SetPendingAction(_ string, a *ScaleAction) {
	o.pendingAction = a
}

// testInputV2ForExec implements InputInterfaceV2 for executor tests.
type testInputV2ForExec struct {
	InputProviderMockEmpty // embeds V1 (GetRangeMetrics, GetClusterTopology)
	topology               ClusterTopologyV1
	err                    error
}

func (i *testInputV2ForExec) GetClusterTopology(_ ClusterId) (ClusterTopologyV1, error) {
	return i.topology, i.err
}

func (i *testInputV2ForExec) GetRangeCPUUtilization(_ context.Context, _ ClusterId, _ TimeRange) ([]CPUDataPoint, error) {
	return nil, nil
}
func (i *testInputV2ForExec) GetPerNodeDiskInfo(_ context.Context, _ string) ([]NodeDiskInfo, error) {
	return nil, nil
}
func (i *testInputV2ForExec) GetDiskSnapshot(_ context.Context, _ string) (*TiKVUsageSnapshot, error) {
	return &TiKVUsageSnapshot{}, nil
}
func (i *testInputV2ForExec) GetEBSCooldownState(_ context.Context, _ string) (*VolumeCooldownState, error) {
	return &VolumeCooldownState{}, nil
}
func (i *testInputV2ForExec) GetCurrentDiskSizeGB(_ context.Context, _ string) (uint32, error) {
	return 0, nil
}
func (i *testInputV2ForExec) GetTiKVMaxNodeCount(_ context.Context, _ string) (uint32, error) {
	return 0, nil
}
func (i *testInputV2ForExec) GetAutoscaleFeatureFlags(_ context.Context, _ string) (*TiKVAutoscaleFeatureFlags, error) {
	return &TiKVAutoscaleFeatureFlags{}, nil
}
func (i *testInputV2ForExec) GetDiskIOMetrics(_ context.Context, _ string) (*DiskIOMetrics, error) {
	return &DiskIOMetrics{}, nil
}
func (i *testInputV2ForExec) GetRangeDiskIOMetrics(_ context.Context, _ string, _ TimeRange) ([]DiskIODataPoint, error) {
	return nil, nil
}
func (i *testInputV2ForExec) GetProvisionedDiskIO(_ context.Context, _ string) (*ProvisionedDiskIO, error) {
	return &ProvisionedDiskIO{}, nil
}
func (i *testInputV2ForExec) GetCollectInterval() time.Duration { return 15 * time.Second }

func TestV2RunOnce_NoChange(t *testing.T) {
	input := &testInputV2ForExec{topology: ClusterTopologyV1{"tikv": {Name: "tikv", Replicas: 3}}}
	scaler := &testScalerV2{result: &ClusterScalerResult{}}
	output := &testOutputV2ForExec{}
	rec := &testReceiver{}
	cfg := NewDefaultScalerInterfaceV1ExecutorSyncLoopConfig()

	exec := NewScalerInterfaceV2ExecutorSyncLoop(rec, "c1", scaler, input, output, cfg)
	reason, err := exec.RunOnce()
	if err != nil {
		t.Fatalf("unexpected error: %v (reason: %s)", err, reason)
	}
	if output.setTopoCalled || output.executeActCalled {
		t.Error("expected no output calls when nothing changed")
	}
}

func TestV2RunOnce_TopologyChanged(t *testing.T) {
	input := &testInputV2ForExec{topology: ClusterTopologyV1{"tikv": {Name: "tikv", Replicas: 3}}}
	scaler := &testScalerV2{result: &ClusterScalerResult{
		TopologyChanged: true,
	}}
	output := &testOutputV2ForExec{}
	rec := &testReceiver{}
	cfg := NewDefaultScalerInterfaceV1ExecutorSyncLoopConfig()

	exec := NewScalerInterfaceV2ExecutorSyncLoop(rec, "c1", scaler, input, output, cfg)
	reason, err := exec.RunOnce()
	if err != nil {
		t.Fatalf("unexpected error: %v (reason: %s)", err, reason)
	}
	if !output.setTopoCalled {
		t.Error("expected SetNewClusterTopology to be called")
	}
}

func TestV2RunOnce_ActionOnly(t *testing.T) {
	input := &testInputV2ForExec{topology: ClusterTopologyV1{"tikv": {Name: "tikv", Replicas: 3}}}
	action := &ScaleAction{Type: ScaleActionExpandDisk, DiskExpandGB: 500}
	scaler := &testScalerV2{result: &ClusterScalerResult{
		Action: action,
	}}
	output := &testOutputV2ForExec{}
	rec := &testReceiver{}
	cfg := NewDefaultScalerInterfaceV1ExecutorSyncLoopConfig()

	exec := NewScalerInterfaceV2ExecutorSyncLoop(rec, "c1", scaler, input, output, cfg)
	reason, err := exec.RunOnce()
	if err != nil {
		t.Fatalf("unexpected error: %v (reason: %s)", err, reason)
	}
	if !output.executeActCalled {
		t.Error("expected ExecuteAction to be called for action-only")
	}
	if output.setTopoCalled {
		t.Error("expected SetNewClusterTopology NOT called for action-only")
	}
}

func TestV2RunOnce_TopologyChangedWithAction(t *testing.T) {
	input := &testInputV2ForExec{topology: ClusterTopologyV1{"tikv": {Name: "tikv", Replicas: 3}}}
	action := &ScaleAction{Type: ScaleActionAddNodes}
	scaler := &testScalerV2{result: &ClusterScalerResult{
		TopologyChanged: true,
		Action:          action,
	}}
	output := &testOutputV2ForExec{}
	rec := &testReceiver{}
	cfg := NewDefaultScalerInterfaceV1ExecutorSyncLoopConfig()

	exec := NewScalerInterfaceV2ExecutorSyncLoop(rec, "c1", scaler, input, output, cfg)
	reason, err := exec.RunOnce()
	if err != nil {
		t.Fatalf("unexpected error: %v (reason: %s)", err, reason)
	}
	if !output.setTopoCalled {
		t.Error("expected SetNewClusterTopology to be called")
	}
	if output.pendingAction != action {
		t.Error("expected SetPendingAction to be called with the action")
	}
}

func TestV2RunOnce_InputError(t *testing.T) {
	input := &testInputV2ForExec{err: errors.New("input failed")}
	scaler := &testScalerV2{result: &ClusterScalerResult{}}
	output := &testOutputV2ForExec{}
	rec := &testReceiver{}
	cfg := NewDefaultScalerInterfaceV1ExecutorSyncLoopConfig()

	exec := NewScalerInterfaceV2ExecutorSyncLoop(rec, "c1", scaler, input, output, cfg)
	reason, err := exec.RunOnce()
	if err == nil {
		t.Fatal("expected error")
	}
	if reason != "GetClusterTopology" {
		t.Errorf("expected reason GetClusterTopology, got %s", reason)
	}
}

func TestV2RunOnce_ScalerError(t *testing.T) {
	input := &testInputV2ForExec{topology: ClusterTopologyV1{"tikv": {Name: "tikv", Replicas: 3}}}
	scaler := &testScalerV2{err: errors.New("scaler failed")}
	output := &testOutputV2ForExec{}
	rec := &testReceiver{}
	cfg := NewDefaultScalerInterfaceV1ExecutorSyncLoopConfig()

	exec := NewScalerInterfaceV2ExecutorSyncLoop(rec, "c1", scaler, input, output, cfg)
	reason, err := exec.RunOnce()
	if err == nil {
		t.Fatal("expected error")
	}
	if reason != "PullClusterDecision" {
		t.Errorf("expected reason PullClusterDecision, got %s", reason)
	}
}

func TestV2ExecutorSyncLoop_ContextCancel(t *testing.T) {
	input := &testInputV2ForExec{topology: ClusterTopologyV1{"tikv": {Name: "tikv", Replicas: 3}}}
	scaler := &testScalerV2{result: &ClusterScalerResult{}}
	output := &testOutputV2ForExec{}
	rec := &testReceiver{}
	cfg := NewDefaultScalerInterfaceV1ExecutorSyncLoopConfig()
	cfg.PauseSecs = 1

	exec := NewScalerInterfaceV2ExecutorSyncLoop(rec, "c1", scaler, input, output, cfg)
	ctx, cancel := context.WithCancel(context.Background())

	done := make(chan error, 1)
	go func() { done <- exec.Run(ctx) }()

	cancel()

	select {
	case err := <-done:
		if !errors.Is(err, context.Canceled) {
			t.Errorf("expected context.Canceled, got %v", err)
		}
	case <-time.After(3 * time.Second):
		t.Fatal("V2 executor.Run did not exit after context cancel")
	}
}
