package dash

import (
	"testing"
	"time"

	"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"

	adminplugin "github.com/seaweedfs/seaweedfs/weed/admin/plugin"
)

// TestVacuumMonitorStateTransitions verifies the state transition logic that
// drives the vacuum monitor goroutine: the plugin registry correctly reports
// whether a vacuum-capable worker is present or absent.
func TestVacuumMonitorStateTransitions(t *testing.T) {
	t.Parallel()

	opts := adminplugin.Options{
		DataDir: "",
	}
	p, err := adminplugin.New(opts)
	if err != nil {
		t.Fatalf("failed to create plugin: %v", err)
	}
	defer p.Shutdown()

	// Initially no workers => no capable worker.
	if p.HasCapableWorker("vacuum") {
		t.Fatalf("expected no capable worker initially")
	}

	// Simulate state transition false -> true: worker connects.
	p.WorkerConnectForTest(&plugin_pb.WorkerHello{
		WorkerId: "vacuum-worker-1",
		Capabilities: []*plugin_pb.JobTypeCapability{
			{JobType: "vacuum", CanDetect: true, CanExecute: true},
		},
	})

	if !p.HasCapableWorker("vacuum") {
		t.Fatalf("expected capable worker after connect")
	}

	// Simulate state transition true -> false: worker disconnects.
	p.WorkerDisconnectForTest("vacuum-worker-1")

	// Give registry removal a moment (it's synchronous, but be safe).
	time.Sleep(10 * time.Millisecond)

	if p.HasCapableWorker("vacuum") {
		t.Fatalf("expected no capable worker after disconnect")
	}
}

// fakeToggler records disable/enable calls for testing.
type fakeToggler struct {
	disableCalls int
	enableCalls  int
}

func (f *fakeToggler) disableVacuum() error {
	f.disableCalls++
	return nil
}

func (f *fakeToggler) enableVacuum() error {
	f.enableCalls++
	return nil
}

func TestSyncVacuumState(t *testing.T) {
	t.Parallel()

	t.Run("no change when state matches", func(t *testing.T) {
		tog := &fakeToggler{}
		result, failed := syncVacuumState(false, false, tog, false)
		if result != false || failed {
			t.Error("expected false, no failure")
		}
		result, failed = syncVacuumState(true, true, tog, false)
		if result != true || failed {
			t.Error("expected true, no failure")
		}
		if tog.disableCalls != 0 || tog.enableCalls != 0 {
			t.Errorf("expected no calls, got disable=%d enable=%d", tog.disableCalls, tog.enableCalls)
		}
	})

	t.Run("worker connects triggers disable", func(t *testing.T) {
		tog := &fakeToggler{}
		result, failed := syncVacuumState(true, false, tog, false)
		if result != true || failed {
			t.Error("expected true after disable, no failure")
		}
		if tog.disableCalls != 1 {
			t.Errorf("expected 1 disable call, got %d", tog.disableCalls)
		}
		if tog.enableCalls != 0 {
			t.Errorf("expected 0 enable calls, got %d", tog.enableCalls)
		}
	})

	t.Run("worker disconnects triggers enable", func(t *testing.T) {
		tog := &fakeToggler{}
		result, failed := syncVacuumState(false, true, tog, false)
		if result != false || failed {
			t.Error("expected false after enable, no failure")
		}
		if tog.enableCalls != 1 {
			t.Errorf("expected 1 enable call, got %d", tog.enableCalls)
		}
		if tog.disableCalls != 0 {
			t.Errorf("expected 0 disable calls, got %d", tog.disableCalls)
		}
	})
}
