package service

import (
	"context"
	"errors"
	"fmt"
	"net"
	"runtime"
	"sync"
	"testing"
	"time"

	toki "git.toki-labs.com/toki/proto-socket/go"
	"google.golang.org/protobuf/proto"
	edgeevents "iop/apps/edge/internal/events"
	edgenode "iop/apps/edge/internal/node"
	"iop/packages/go/config"
	iop "iop/proto/gen/iop"
)

func TestRunIDRemainsUniqueAtSameClockTick(t *testing.T) {
	const count = 128
	ids := make(chan string, count)
	var wg sync.WaitGroup
	wg.Add(count)
	for i := 0; i < count; i++ {
		go func() {
			defer wg.Done()
			ids <- newRunIDAt(1)
		}()
	}
	wg.Wait()
	close(ids)

	seen := make(map[string]struct{}, count)
	for id := range ids {
		if _, exists := seen[id]; exists {
			t.Fatalf("duplicate run id %q", id)
		}
		seen[id] = struct{}{}
	}
	if len(seen) != count {
		t.Fatalf("unique run ids = %d, want %d", len(seen), count)
	}
}

// TestRefreshProviderCapacityAffectsNextDispatch verifies that a provider
// capacity change applied via SetRuntimeConfig is reflected in the candidate
// capacity used for the next dispatch admission, since candidates are rebuilt
// from the live runtime snapshot on each request.
func TestRefreshProviderCapacityAffectsNextDispatch(t *testing.T) {
	reg := edgenode.NewRegistry()
	reg.Register(&edgenode.NodeEntry{NodeID: "node-cap"})
	svc := New(reg, nil)

	newStore := func(capacity int) *edgenode.NodeStore {
		store := edgenode.NewNodeStore()
		store.Add(&edgenode.NodeRecord{
			ID: "node-cap",
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{
				{
					ID:       "prov-a",
					Type:     "vllm",
					Category: config.CategoryAPI,
					Adapter:  "vllm-gpu",
					Models:   []string{"served-a"},
					Health:   "available",
					Capacity: capacity,
				},
			},
		})
		return store
	}
	catalog := []config.ModelCatalogEntry{{
		ID:        "qwen3.6:35b",
		Providers: map[string]string{"prov-a": "served-a"},
	}}

	req := SubmitRunRequest{ModelGroupKey: "qwen3.6:35b", ProviderPool: true}

	svc.SetRuntimeConfig(newStore(2), catalog, groupPolicy{})
	store, cat, _ := svc.runtimeConfigSnapshot()
	cands, _, err := svc.resolveProviderPoolCandidates(req, store, cat)
	if err != nil {
		t.Fatalf("resolve before refresh: %v", err)
	}
	if len(cands) != 1 || cands[0].capacity != 2 {
		t.Fatalf("before refresh: got candidates=%+v, want one with capacity=2", cands)
	}

	// Refresh raises capacity to 8; the next candidate build must observe it.
	svc.SetRuntimeConfig(newStore(8), catalog, groupPolicy{})
	store, cat, _ = svc.runtimeConfigSnapshot()
	cands, _, err = svc.resolveProviderPoolCandidates(req, store, cat)
	if err != nil {
		t.Fatalf("resolve after refresh: %v", err)
	}
	if len(cands) != 1 || cands[0].capacity != 8 {
		t.Fatalf("after refresh: got candidates=%+v, want one with capacity=8", cands)
	}
}

// TestRefreshProviderLongCapacityAffectsCandidate verifies that a provider
// long_context_capacity change applied via SetRuntimeConfig is reflected in the
// candidate longContextCapacity used for the next long-context admission.
func TestRefreshProviderLongCapacityAffectsCandidate(t *testing.T) {
	reg := edgenode.NewRegistry()
	reg.Register(&edgenode.NodeEntry{NodeID: "node-cap"})
	svc := New(reg, nil)

	newStore := func(longCapacity int) *edgenode.NodeStore {
		store := edgenode.NewNodeStore()
		store.Add(&edgenode.NodeRecord{
			ID: "node-cap",
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{
				{
					ID:                  "prov-a",
					Type:                "vllm",
					Category:            config.CategoryAPI,
					Adapter:             "vllm-gpu",
					Models:              []string{"served-a"},
					Health:              "available",
					Capacity:            4,
					TotalContextTokens:  524288,
					LongContextCapacity: longCapacity,
				},
			},
		})
		return store
	}
	catalog := []config.ModelCatalogEntry{{
		ID:                  "qwen3.6:35b",
		ContextWindowTokens: 262144,
		Providers:           map[string]string{"prov-a": "served-a"},
	}}

	req := SubmitRunRequest{ModelGroupKey: "qwen3.6:35b", ProviderPool: true, ContextClass: "long"}

	svc.SetRuntimeConfig(newStore(1), catalog, groupPolicy{})
	store, cat, _ := svc.runtimeConfigSnapshot()
	cands, _, err := svc.resolveProviderPoolCandidates(req, store, cat)
	if err != nil {
		t.Fatalf("resolve before refresh: %v", err)
	}
	if len(cands) != 1 || cands[0].longContextCapacity != 1 {
		t.Fatalf("before refresh: got candidates=%+v, want one with longContextCapacity=1", cands)
	}

	// Refresh raises long capacity to 2; the next candidate build must observe it.
	svc.SetRuntimeConfig(newStore(2), catalog, groupPolicy{})
	store, cat, _ = svc.runtimeConfigSnapshot()
	cands, _, err = svc.resolveProviderPoolCandidates(req, store, cat)
	if err != nil {
		t.Fatalf("resolve after refresh: %v", err)
	}
	if len(cands) != 1 || cands[0].longContextCapacity != 2 {
		t.Fatalf("after refresh: got candidates=%+v, want one with longContextCapacity=2", cands)
	}
}

func TestRuntimeConfigSnapshotConcurrentReplace(t *testing.T) {
	reg := edgenode.NewRegistry()
	reg.Register(&edgenode.NodeEntry{NodeID: "node-refresh"})
	svc := New(reg, nil)

	newStore := func(providerID, servedModel string, capacity int) *edgenode.NodeStore {
		store := edgenode.NewNodeStore()
		store.Add(&edgenode.NodeRecord{
			ID:      "node-refresh",
			Runtime: config.RuntimeConf{Concurrency: capacity},
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{
				{
					ID:       providerID,
					Type:     "vllm",
					Category: config.CategoryAPI,
					Adapter:  "vllm-gpu",
					Models:   []string{servedModel},
					Health:   "available",
					Capacity: capacity,
				},
			},
		})
		return store
	}
	newCatalog := func(providerID, servedModel string) []config.ModelCatalogEntry {
		return []config.ModelCatalogEntry{
			{
				ID: "qwen3.6:35b",
				Providers: map[string]string{
					providerID: servedModel,
				},
			},
		}
	}

	storeA := newStore("prov-a", "served-a", 2)
	catalogA := newCatalog("prov-a", "served-a")
	policyA := NewGroupPolicy(3, 500*time.Millisecond)
	storeB := newStore("prov-b", "served-b", 4)
	catalogB := newCatalog("prov-b", "served-b")
	policyB := NewGroupPolicy(7, 2*time.Second)
	svc.SetRuntimeConfig(storeA, catalogA, policyA)

	const readers = 8
	const iterations = 200
	start := make(chan struct{})
	errCh := make(chan error, readers)
	var wg sync.WaitGroup

	for i := 0; i < readers; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			<-start
			for j := 0; j < iterations; j++ {
				store, catalog, policy := svc.runtimeConfigSnapshot()
				// Snapshot must return a self-consistent (store, catalog, policy) tuple.
				// A reader must never observe storeA with policyB, or storeB with policyA.
				snapProviders := store.All()
				if len(snapProviders) != 1 {
					errCh <- fmt.Errorf("snapshot store has %d providers, want 1", len(snapProviders))
					return
				}
				providerID := snapProviders[0].Providers[0].ID
				// Each snapshot must pair the correct policy with the correct provider.
				if providerID == "prov-a" && (policy.maxQueue != policyA.maxQueue || policy.queueTimeout != policyA.queueTimeout) {
					errCh <- fmt.Errorf("A snapshot has B policy: store=%s policy=(max=%d,timeout=%v) want (max=%d,timeout=%v)",
						providerID, policy.maxQueue, policy.queueTimeout, policyA.maxQueue, policyA.queueTimeout)
					return
				}
				if providerID == "prov-b" && (policy.maxQueue != policyB.maxQueue || policy.queueTimeout != policyB.queueTimeout) {
					errCh <- fmt.Errorf("B snapshot has A policy: store=%s policy=(max=%d,timeout=%v) want (max=%d,timeout=%v)",
						providerID, policy.maxQueue, policy.queueTimeout, policyB.maxQueue, policyB.queueTimeout)
					return
				}
				// Policy must always be one of the two known policies (never a stale or mixed value).
				if policy.maxQueue != policyA.maxQueue && policy.maxQueue != policyB.maxQueue {
					errCh <- fmt.Errorf("snapshot has unknown policy maxQueue=%d (want %d or %d)",
						policy.maxQueue, policyA.maxQueue, policyB.maxQueue)
					return
				}
				candidates, _, err := svc.resolveProviderPoolCandidates(SubmitRunRequest{
					ModelGroupKey: "qwen3.6:35b",
					ProviderPool:  true,
				}, store, catalog)
				if err != nil {
					errCh <- err
					return
				}
				if len(candidates) != 1 {
					errCh <- errors.New("expected exactly one provider candidate")
					return
				}
				_ = svc.ListNodeSnapshots()
			}
		}()
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		<-start
		for i := 0; i < iterations; i++ {
			svc.SetRuntimeConfig(storeA, catalogA, policyA)
			svc.SetRuntimeConfig(storeB, catalogB, policyB)
		}
	}()

	close(start)
	wg.Wait()
	close(errCh)
	for err := range errCh {
		if err != nil {
			t.Fatalf("runtime config snapshot reader observed inconsistent state: %v", err)
		}
	}
}

func TestServiceRuntimeConfigRefreshPreservesInflight(t *testing.T) {
	reg := edgenode.NewRegistry()
	reg.Register(&edgenode.NodeEntry{NodeID: "node-ref-pres"})
	bus := edgeevents.NewBus()
	svc := New(reg, bus)

	newStore := func(capacity, longCapacity int) *edgenode.NodeStore {
		store := edgenode.NewNodeStore()
		store.Add(&edgenode.NodeRecord{
			ID: "node-ref-pres",
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{
				{
					ID:                  "prov-a",
					Type:                "vllm",
					Category:            config.CategoryAPI,
					Adapter:             "vllm-gpu",
					Models:              []string{"served-a"},
					Health:              "available",
					Capacity:            capacity,
					LongContextCapacity: longCapacity,
				},
			},
		})
		return store
	}

	catalog := []config.ModelCatalogEntry{{
		ID:        "qwen3.6:35b",
		Providers: map[string]string{"prov-a": "served-a"},
	}}

	svc.SetRuntimeConfig(newStore(4, 2), catalog, groupPolicy{})

	// Simulate admission by reserving slot
	svc.queue.mu.Lock()
	key := providerResourceKey{nodeID: "node-ref-pres", providerID: "prov-a"}
	res := svc.queue.resources[key]
	if res == nil {
		svc.queue.mu.Unlock()
		t.Fatal("expected provider resource state to exist")
	}
	res.inFlight = 3
	res.longInFlight = 2
	svc.queue.mu.Unlock()

	// Update store (reduce capacity and long capacity)
	svc.SetRuntimeConfig(newStore(2, 1), catalog, groupPolicy{})

	// Verify capacity changed but inflight counts preserved
	svc.queue.mu.Lock()
	if res.capacity != 2 {
		t.Errorf("expected capacity updated to 2, got %d", res.capacity)
	}
	if res.longCapacity != 1 {
		t.Errorf("expected longCapacity updated to 1, got %d", res.longCapacity)
	}
	if res.inFlight != 3 {
		t.Errorf("expected inFlight preserved at 3, got %d", res.inFlight)
	}
	if res.longInFlight != 2 {
		t.Errorf("expected longInFlight preserved at 2, got %d", res.longInFlight)
	}
	svc.queue.mu.Unlock()
}

// refreshWakeupFixture builds a service whose single provider can be reconfigured
// between calls, plus the candidate list a queued request would carry for it.
func refreshWakeupFixture(t *testing.T, nodeID, providerID, servedModel string) (*Service, func(capacity int, enabled bool), []candidateNode) {
	t.Helper()

	reg := edgenode.NewRegistry()
	entry := &edgenode.NodeEntry{NodeID: nodeID}
	reg.Register(entry)
	svc := New(reg, edgeevents.NewBus())

	catalog := []config.ModelCatalogEntry{{
		ID:        "group-refresh",
		Providers: map[string]string{providerID: servedModel},
	}}

	applyConfig := func(capacity int, enabled bool) {
		enabledFlag := enabled
		store := edgenode.NewNodeStore()
		store.Add(&edgenode.NodeRecord{
			ID: nodeID,
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{{
				ID:       providerID,
				Type:     "vllm",
				Adapter:  "vllm-gpu",
				Models:   []string{servedModel},
				Health:   "available",
				Capacity: capacity,
				Enabled:  &enabledFlag,
			}},
		})
		svc.SetRuntimeConfig(store, catalog, groupPolicy{})
	}

	applyConfig(1, true)
	cands := []candidateNode{{entry: entry, capacity: 1, providerID: providerID, servedTarget: servedModel}}
	return svc, applyConfig, cands
}

// TestGlobalPumpCapacityRefreshWakesWaiter verifies that raising provider
// capacity through a runtime config refresh wakes an already-queued request.
// A capacity increase frees room that no lease release will ever announce, so
// without pumping on refresh the waiter would sleep until its queue timeout.
func TestGlobalPumpCapacityRefreshWakesWaiter(t *testing.T) {
	svc, applyConfig, cands := refreshWakeupFixture(t, "node-cap-wake", "prov-cap-wake", "served-cap-wake")

	// Fill the provider's only slot.
	if _, err := svc.queue.admit(t.Context(), "g-cap-wake-a", "", "", cands, groupPolicy{}, nil, false, false); err != nil {
		t.Fatalf("initial admit: %v", err)
	}

	// A request in a different group queues behind it.
	waiter := queueItemForTest(cands, false)
	enqueueForTest(svc.queue, "g-cap-wake-b", waiter, nil)

	// Raising capacity must dispatch the waiter immediately.
	applyConfig(2, true)

	select {
	case res := <-waiter.waitCh:
		if res.err != nil {
			t.Fatalf("waiter expected dispatch after capacity refresh, got error: %v", res.err)
		}
		if res.candidate == nil || res.candidate.capacity != 2 {
			t.Fatalf("waiter must be dispatched with the refreshed capacity, got %+v", res.candidate)
		}
	default:
		t.Fatal("capacity refresh did not wake the queued waiter")
	}

	// Over-capacity must still be refused: both slots are now taken.
	inFlight, _ := providerResourceCounts(svc.queue, "node-cap-wake", "prov-cap-wake")
	if inFlight != 2 {
		t.Fatalf("expected 2 in-flight after the refresh dispatch, got %d", inFlight)
	}
	extra := queueItemForTest(cands, false)
	enqueueForTest(svc.queue, "g-cap-wake-b", extra, nil)
	svc.queue.mu.Lock()
	svc.queue.pumpAllLocked()
	svc.queue.mu.Unlock()
	select {
	case <-extra.waitCh:
		t.Fatal("pump dispatched past the refreshed capacity")
	default:
	}
}

// TestGlobalPumpDisabledProviderIsNotDispatched verifies that dispatch re-reads
// the enabled switch at hand-off time. A request queued while the provider was
// enabled must not be handed the provider after a refresh disabled it, even
// though its candidate snapshot still says the provider is usable — and it must
// resume once the provider comes back.
func TestGlobalPumpDisabledProviderIsNotDispatched(t *testing.T) {
	svc, applyConfig, cands := refreshWakeupFixture(t, "node-dis", "prov-dis", "served-dis")

	admitted, err := svc.queue.admit(t.Context(), "g-dis-a", "", "", cands, groupPolicy{}, nil, false, false)
	if err != nil || admitted == nil {
		t.Fatalf("initial admit: %v", err)
	}

	waiter := queueItemForTest(cands, false)
	enqueueForTest(svc.queue, "g-dis-b", waiter, nil)

	// Disable the provider, then free the in-flight slot. The freed capacity must
	// not be handed to the waiter: the provider is no longer dispatchable.
	applyConfig(1, false)
	svc.queue.releaseLease(admitted.leaseID, "complete")

	select {
	case res := <-waiter.waitCh:
		t.Fatalf("waiter was dispatched to a disabled provider: %+v", res)
	default:
	}
	if got := pendingItemCount(svc.queue); got != 1 {
		t.Fatalf("expected the waiter to stay queued, got %d pending items", got)
	}

	// Re-enabling must wake it through the same refresh pump.
	applyConfig(1, true)
	select {
	case res := <-waiter.waitCh:
		if res.err != nil {
			t.Fatalf("waiter expected dispatch after re-enable, got error: %v", res.err)
		}
	default:
		t.Fatal("re-enabling the provider did not wake the queued waiter")
	}
}

// TestGlobalPumpReenabledProviderAbsentAtEnqueueWakesWaiter verifies the
// required regression: a provider that was disabled at enqueue time is absent
// from the candidate snapshot, so the original snapshot-only pump could never
// discover it after re-enable. With the live resolver the waiter must wake
// and dispatch to the re-enabled provider without any lease release.
func TestGlobalPumpReenabledProviderAbsentAtEnqueueWakesWaiter(t *testing.T) {
	parserMap := toki.ParserMap{
		toki.TypeNameOf(&iop.RunRequest{}): func(b []byte) (proto.Message, error) {
			m := &iop.RunRequest{}
			return m, proto.Unmarshal(b, m)
		},
	}

	edgeConn1, nodeConn1 := net.Pipe()
	edgeConn2, nodeConn2 := net.Pipe()
	defer edgeConn1.Close()
	defer nodeConn1.Close()
	defer edgeConn2.Close()
	defer nodeConn2.Close()

	edgeClient1 := toki.NewTcpClient(edgeConn1, 0, 0, parserMap)
	edgeClient2 := toki.NewTcpClient(edgeConn2, 0, 0, parserMap)
	_ = toki.NewTcpClient(nodeConn1, 0, 0, parserMap)
	_ = toki.NewTcpClient(nodeConn2, 0, 0, parserMap)

	reg := edgenode.NewRegistry()
	reg.Register(&edgenode.NodeEntry{NodeID: "node-re-a", Client: edgeClient1})
	reg.Register(&edgenode.NodeEntry{NodeID: "node-re-b", Client: edgeClient2})
	svc := New(reg, edgeevents.NewBus())

	catalog := []config.ModelCatalogEntry{{
		ID:        "group-re",
		Providers: map[string]string{"prov-a": "served-a", "prov-b": "served-b"},
	}}

	applyConfig := func(capA, capB int, enabledB bool) {
		enabledFlag := true
		if !enabledB {
			enabledFlag = false
		}
		store := edgenode.NewNodeStore()
		store.Add(&edgenode.NodeRecord{
			ID: "node-re-a",
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{{
				ID: "prov-a", Type: "vllm", Adapter: "vllm-gpu",
				Models: []string{"served-a"}, Health: "available",
				Capacity: capA,
			}},
		})
		store.Add(&edgenode.NodeRecord{
			ID: "node-re-b",
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8001/v1"},
				},
			},
			Providers: []config.NodeProviderConf{{
				ID: "prov-b", Type: "vllm", Adapter: "vllm-gpu",
				Models: []string{"served-b"}, Health: "available",
				Capacity: capB, Enabled: &enabledFlag,
			}},
		})
		svc.SetRuntimeConfig(store, catalog, groupPolicy{})
	}

	// Fill prov-a's only slot so prov-b is the only candidate for the waiter.
	applyConfig(1, 1, true)
	admitted, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
		ModelGroupKey: "group-re", ProviderPool: true, Background: true,
	})
	if err != nil || admitted == nil {
		t.Fatalf("initial admit: %v", err)
	}
	defer admitted.Close()

	// Disable prov-b and enqueue a waiter — prov-b must be absent from the
	// resolver's candidate list at enqueue time.
	applyConfig(1, 1, false)

	type submitResult struct {
		run RunResult
		err error
	}
	resultCh := make(chan submitResult, 1)
	go func() {
		run, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
			ModelGroupKey: "group-re", ProviderPool: true, Background: true,
		})
		resultCh <- submitResult{run: run, err: err}
	}()

	// Verify the waiter is queued and prov-b is NOT in its stored enqueue snapshot.
	requireQueued := func() {
		deadline := time.After(2 * time.Second)
		for {
			svc.queue.mu.Lock()
			group := svc.queue.groups["group-re"]
			count := 0
			if group != nil {
				count = len(group.queue)
			}
			svc.queue.mu.Unlock()
			if count == 1 {
				return
			}
			select {
			case <-deadline:
				t.Fatalf("waiter was not enqueued within timeout")
			default:
				runtime.Gosched()
			}
		}
	}
	requireQueued()
	svc.queue.mu.Lock()
	group := svc.queue.groups["group-re"]
	if group == nil || len(group.queue) != 1 {
		svc.queue.mu.Unlock()
		t.Fatalf("expected exactly one queued item")
	}
	item := group.queue[0]
	if item.resolveCandidates == nil {
		svc.queue.mu.Unlock()
		t.Fatal("expected live resolver on queued item")
	}
	for _, candidate := range item.candidates {
		if candidate.providerID == "prov-b" {
			svc.queue.mu.Unlock()
			t.Fatalf("prov-b must be absent from stored enqueue snapshot")
		}
	}
	svc.queue.mu.Unlock()

	// Re-enable prov-b — the live resolver must now include it and the pump
	// must dispatch the waiter to prov-b without any lease release.
	applyConfig(1, 1, true)

	var res3 RunResult
	var err3 error
	select {
	case got := <-resultCh:
		err3 = got.err
		res3 = got.run
	case <-time.After(500 * time.Millisecond):
		t.Fatal("re-enabling prov-b did not wake the queued waiter (provider absent-at-enqueue regression)")
	}

	if err3 != nil {
		t.Fatalf("waiter expected dispatch after re-enable, got error: %v", err3)
	}
	if res3 == nil {
		t.Fatal("waiter expected non-nil result")
	}
	defer res3.Close()
	if got := res3.Dispatch().ProviderID; got != "prov-b" {
		t.Fatalf("waiter dispatched to provider %q, want prov-b", got)
	}

	// Verify both providers have in-flight=1 after dispatch.
	preAInFlight, _ := providerResourceCounts(svc.queue, "node-re-a", "prov-a")
	preBInFlight, _ := providerResourceCounts(svc.queue, "node-re-b", "prov-b")
	if preAInFlight != 1 {
		t.Fatalf("expected prov-a in-flight=1 after dispatch, got %d", preAInFlight)
	}
	if preBInFlight != 1 {
		t.Fatalf("expected prov-b in-flight=1 after dispatch, got %d", preBInFlight)
	}

	// Release both runs through terminal lifecycle events and verify full drain.
	svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: admitted.Dispatch().RunID, Type: "complete"})
	svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: res3.Dispatch().RunID, Type: "complete"})
	aInFlight, aLong := providerResourceCounts(svc.queue, "node-re-a", "prov-a")
	bInFlight, bLong := providerResourceCounts(svc.queue, "node-re-b", "prov-b")
	if aInFlight != 0 || aLong != 0 {
		t.Fatalf("expected prov-a drained to 0, got inFlight=%d longInFlight=%d", aInFlight, aLong)
	}
	if bInFlight != 0 || bLong != 0 {
		t.Fatalf("expected prov-b drained to 0, got inFlight=%d longInFlight=%d", bInFlight, bLong)
	}
	if lc := leaseCount(svc.queue); lc != 0 {
		t.Fatalf("expected leaseCount=0 after terminal events, got %d", lc)
	}
}

// TestGlobalPumpCapacityZeroProviderBecomesEligible verifies the required
// regression: a provider with capacity 0 at enqueue time is absent from the
// candidate snapshot, so the original snapshot-only pump could never discover
// it after capacity becomes positive. With the live resolver the waiter must
// wake and dispatch to the provider once capacity is raised.
func TestGlobalPumpCapacityZeroProviderBecomesEligible(t *testing.T) {
	parserMap := toki.ParserMap{
		toki.TypeNameOf(&iop.RunRequest{}): func(b []byte) (proto.Message, error) {
			m := &iop.RunRequest{}
			return m, proto.Unmarshal(b, m)
		},
	}

	edgeConn1, nodeConn1 := net.Pipe()
	edgeConn2, nodeConn2 := net.Pipe()
	defer edgeConn1.Close()
	defer nodeConn1.Close()
	defer edgeConn2.Close()
	defer nodeConn2.Close()

	edgeClient1 := toki.NewTcpClient(edgeConn1, 0, 0, parserMap)
	edgeClient2 := toki.NewTcpClient(edgeConn2, 0, 0, parserMap)
	_ = toki.NewTcpClient(nodeConn1, 0, 0, parserMap)
	_ = toki.NewTcpClient(nodeConn2, 0, 0, parserMap)

	reg := edgenode.NewRegistry()
	reg.Register(&edgenode.NodeEntry{NodeID: "node-cz-a", Client: edgeClient1})
	reg.Register(&edgenode.NodeEntry{NodeID: "node-cz-b", Client: edgeClient2})
	svc := New(reg, edgeevents.NewBus())

	catalog := []config.ModelCatalogEntry{{
		ID:        "group-cz",
		Providers: map[string]string{"prov-a": "served-a", "prov-b": "served-b"},
	}}

	applyConfig := func(capA, capB int) {
		store := edgenode.NewNodeStore()
		store.Add(&edgenode.NodeRecord{
			ID: "node-cz-a",
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{{
				ID: "prov-a", Type: "vllm", Adapter: "vllm-gpu",
				Models: []string{"served-a"}, Health: "available",
				Capacity: capA,
			}},
		})
		store.Add(&edgenode.NodeRecord{
			ID: "node-cz-b",
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8001/v1"},
				},
			},
			Providers: []config.NodeProviderConf{{
				ID: "prov-b", Type: "vllm", Adapter: "vllm-gpu",
				Models: []string{"served-b"}, Health: "available",
				Capacity: capB,
			}},
		})
		svc.SetRuntimeConfig(store, catalog, groupPolicy{})
	}

	// Fill prov-a's only slot so prov-b is the only candidate for the waiter.
	applyConfig(1, 1)
	admitted, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
		ModelGroupKey: "group-cz", ProviderPool: true, Background: true,
	})
	if err != nil || admitted == nil {
		t.Fatalf("initial admit: %v", err)
	}
	defer admitted.Close()

	// Set prov-b to capacity 0 and enqueue a waiter — prov-b must be absent
	// from the resolver's candidate list at enqueue time.
	applyConfig(1, 0)

	type submitResult struct {
		run RunResult
		err error
	}
	resultCh := make(chan submitResult, 1)
	go func() {
		run, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
			ModelGroupKey: "group-cz", ProviderPool: true, Background: true,
		})
		resultCh <- submitResult{run: run, err: err}
	}()

	// Verify the waiter is queued and prov-b is NOT in its stored enqueue snapshot.
	requireQueued := func() {
		deadline := time.After(2 * time.Second)
		for {
			svc.queue.mu.Lock()
			group := svc.queue.groups["group-cz"]
			count := 0
			if group != nil {
				count = len(group.queue)
			}
			svc.queue.mu.Unlock()
			if count == 1 {
				return
			}
			select {
			case <-deadline:
				t.Fatalf("waiter was not enqueued within timeout")
			default:
				runtime.Gosched()
			}
		}
	}
	requireQueued()
	svc.queue.mu.Lock()
	group := svc.queue.groups["group-cz"]
	if group == nil || len(group.queue) != 1 {
		svc.queue.mu.Unlock()
		t.Fatalf("expected exactly one queued item")
	}
	item := group.queue[0]
	if item.resolveCandidates == nil {
		svc.queue.mu.Unlock()
		t.Fatal("expected live resolver on queued item")
	}
	for _, candidate := range item.candidates {
		if candidate.providerID == "prov-b" {
			svc.queue.mu.Unlock()
			t.Fatalf("prov-b must be absent from stored enqueue snapshot")
		}
	}
	svc.queue.mu.Unlock()

	// Raise prov-b's capacity — the live resolver must now include it and the
	// pump must dispatch the waiter to prov-b without any lease release.
	applyConfig(1, 1)

	var res3 RunResult
	var err3 error
	select {
	case got := <-resultCh:
		err3 = got.err
		res3 = got.run
	case <-time.After(500 * time.Millisecond):
		t.Fatal("raising prov-b capacity did not wake the queued waiter (capacity-zero-at-enqueue regression)")
	}

	if err3 != nil {
		t.Fatalf("waiter expected dispatch after capacity raise, got error: %v", err3)
	}
	if res3 == nil {
		t.Fatal("waiter expected non-nil result")
	}
	defer res3.Close()
	if got := res3.Dispatch().ProviderID; got != "prov-b" {
		t.Fatalf("waiter dispatched to provider %q, want prov-b", got)
	}

	// Verify both providers have in-flight=1 after dispatch.
	preAInFlight, _ := providerResourceCounts(svc.queue, "node-cz-a", "prov-a")
	preBInFlight, _ := providerResourceCounts(svc.queue, "node-cz-b", "prov-b")
	if preAInFlight != 1 {
		t.Fatalf("expected prov-a in-flight=1 after dispatch, got %d", preAInFlight)
	}
	if preBInFlight != 1 {
		t.Fatalf("expected prov-b in-flight=1 after dispatch, got %d", preBInFlight)
	}

	// Release both runs through terminal lifecycle events and verify full drain.
	svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: admitted.Dispatch().RunID, Type: "complete"})
	svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: res3.Dispatch().RunID, Type: "complete"})
	aInFlight, aLong := providerResourceCounts(svc.queue, "node-cz-a", "prov-a")
	bInFlight, bLong := providerResourceCounts(svc.queue, "node-cz-b", "prov-b")
	if aInFlight != 0 || aLong != 0 {
		t.Fatalf("expected prov-a drained to 0, got inFlight=%d longInFlight=%d", aInFlight, aLong)
	}
	if bInFlight != 0 || bLong != 0 {
		t.Fatalf("expected prov-b drained to 0, got inFlight=%d longInFlight=%d", bInFlight, bLong)
	}
	if lc := leaseCount(svc.queue); lc != 0 {
		t.Fatalf("expected leaseCount=0 after terminal events, got %d", lc)
	}
}

// --- PROVIDER_POLICY_REFRESH-2: resource attribute refresh tests ---

// helper for capacity/priority/disable rebase tests.
// Returns a service with a mock client pair so SubmitRun can dispatch.
func buildCapacityRebaseService(t *testing.T, nodeID, providerID, servedModel string, initialCap, initialLongCap int, enabled *bool) (*Service, func(capacity, longCapacity int, enabled bool)) {
	t.Helper()
	edgeConn, nodeConn := net.Pipe()
	t.Cleanup(func() {
		edgeConn.Close()
		nodeConn.Close()
	})
	parserMap := toki.ParserMap{
		toki.TypeNameOf(&iop.RunRequest{}): func(b []byte) (proto.Message, error) {
			m := &iop.RunRequest{}
			return m, proto.Unmarshal(b, m)
		},
	}
	edgeClient := toki.NewTcpClient(edgeConn, 0, 0, parserMap)
	_ = toki.NewTcpClient(nodeConn, 0, 0, parserMap)

	reg := edgenode.NewRegistry()
	reg.Register(&edgenode.NodeEntry{NodeID: nodeID, Client: edgeClient})
	svc := New(reg, edgeevents.NewBus())
	catalog := []config.ModelCatalogEntry{{
		ID:        "group-cap-rebase",
		Providers: map[string]string{providerID: servedModel},
	}}
	applyFn := func(capacity, longCapacity int, enabled bool) {
		enabledFlag := enabled
		store := edgenode.NewNodeStore()
		store.Add(&edgenode.NodeRecord{
			ID: nodeID,
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{{
				ID:                  providerID,
				Type:                "vllm",
				Adapter:             "vllm-gpu",
				Models:              []string{servedModel},
				Health:              "available",
				Capacity:            capacity,
				LongContextCapacity: longCapacity,
				Enabled:             &enabledFlag,
			}},
		})
		svc.SetRuntimeConfig(store, catalog, groupPolicy{maxQueue: 8, queueTimeout: 5 * time.Second})
	}
	applyFn(initialCap, initialLongCap, *enabled)
	return svc, applyFn
}

// buildPriorityService sets up two providers with separate mock clients.
func buildPriorityService(t *testing.T) (*Service, func(capA, capB, prioA, prioB int, enabled bool)) {
	t.Helper()
	edgeConnA, nodeConnA := net.Pipe()
	edgeConnB, nodeConnB := net.Pipe()
	t.Cleanup(func() {
		edgeConnA.Close()
		nodeConnA.Close()
		edgeConnB.Close()
		nodeConnB.Close()
	})
	parserMap := toki.ParserMap{
		toki.TypeNameOf(&iop.RunRequest{}): func(b []byte) (proto.Message, error) {
			m := &iop.RunRequest{}
			return m, proto.Unmarshal(b, m)
		},
	}
	edgeClientA := toki.NewTcpClient(edgeConnA, 0, 0, parserMap)
	_ = toki.NewTcpClient(nodeConnA, 0, 0, parserMap)
	edgeClientB := toki.NewTcpClient(edgeConnB, 0, 0, parserMap)
	_ = toki.NewTcpClient(nodeConnB, 0, 0, parserMap)

	reg := edgenode.NewRegistry()
	reg.Register(&edgenode.NodeEntry{NodeID: "node-prio-a", Client: edgeClientA})
	reg.Register(&edgenode.NodeEntry{NodeID: "node-prio-b", Client: edgeClientB})
	svc := New(reg, edgeevents.NewBus())
	catalog := []config.ModelCatalogEntry{{
		ID:        "group-prio",
		Providers: map[string]string{"prov-a": "served-a", "prov-b": "served-b"},
	}}
	applyFn := func(capA, capB, prioA, prioB int, enabled bool) {
		enabledFlag := enabled
		store := edgenode.NewNodeStore()
		store.Add(&edgenode.NodeRecord{
			ID: "node-prio-a",
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{{
				ID: "prov-a", Type: "vllm", Adapter: "vllm-gpu",
				Models: []string{"served-a"}, Health: "available",
				Capacity: capA, Priority: prioA, Enabled: &enabledFlag,
			}},
		})
		store.Add(&edgenode.NodeRecord{
			ID: "node-prio-b",
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8001/v1"},
				},
			},
			Providers: []config.NodeProviderConf{{
				ID: "prov-b", Type: "vllm", Adapter: "vllm-gpu",
				Models: []string{"served-b"}, Health: "available",
				Capacity: capB, Priority: prioB, Enabled: &enabledFlag,
			}},
		})
		svc.SetRuntimeConfig(store, catalog, groupPolicy{maxQueue: 8, queueTimeout: 5 * time.Second})
	}
	applyFn(1, 0, 0, 0, true)
	return svc, applyFn
}

// TestRefreshCapacityShrinkPreservesLeaseAndBlocksUntilRecovery verifies that
// shrinking capacity while in-flight leases exist preserves those leases and
// blocks new admission, and that raising capacity after a release immediately
// pumps the queued waiter.
func TestRefreshCapacityShrinkPreservesLeaseAndBlocksUntilRecovery(t *testing.T) {
	enabled := true
	svc, applyFn := buildCapacityRebaseService(t, "node-cap-shrink", "prov-shrink", "served-shrink", 2, 0, &enabled)

	admitted1, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
		ModelGroupKey: "group-cap-rebase", ProviderPool: true, Background: true,
	})
	if err != nil || admitted1 == nil {
		t.Fatalf("admit 1 failed: %v", err)
	}
	admitted2, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
		ModelGroupKey: "group-cap-rebase", ProviderPool: true, Background: true,
	})
	if err != nil || admitted2 == nil {
		admitted1.Close()
		t.Fatalf("admit 2 failed: %v", err)
	}

	svc.queue.mu.Lock()
	res := svc.queue.resources[providerResourceKey{nodeID: "node-cap-shrink", providerID: "prov-shrink"}]
	if res == nil || res.inFlight != 2 {
		svc.queue.mu.Unlock()
		admitted1.Close()
		admitted2.Close()
		t.Fatalf("expected inFlight=2 after admits, got %d", res.inFlight)
	}
	svc.queue.mu.Unlock()

	applyFn(1, 0, true)

	svc.queue.mu.Lock()
	if res.capacity != 1 {
		svc.queue.mu.Unlock()
		admitted1.Close()
		admitted2.Close()
		t.Fatalf("expected capacity=1 after shrink, got %d", res.capacity)
	}
	if res.inFlight != 2 {
		svc.queue.mu.Unlock()
		admitted1.Close()
		admitted2.Close()
		t.Fatalf("expected inFlight preserved at 2 after shrink, got %d", res.inFlight)
	}
	svc.queue.mu.Unlock()

	blockedCh := make(chan struct {
		run RunResult
		err error
	}, 1)
	go func() {
		run, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
			ModelGroupKey: "group-cap-rebase", ProviderPool: true, Background: true,
		})
		blockedCh <- struct {
			run RunResult
			err error
		}{run: run, err: err}
	}()

	waitForQueueLen(t, svc.queue, "group-cap-rebase", 1)
	if inFlight, _ := providerResourceCounts(svc.queue, "node-cap-shrink", "prov-shrink"); inFlight != 2 {
		admitted1.Close()
		admitted2.Close()
		t.Fatalf("blocked admission increased over-capacity in-flight to %d", inFlight)
	}

	svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: admitted1.Dispatch().RunID, Type: "complete"})
	applyFn(2, 0, true)

	select {
	case got := <-blockedCh:
		if got.err != nil {
			admitted2.Close()
			t.Fatalf("blocked waiter expected dispatch after raise, got error: %v", got.err)
		}
		if got.run == nil {
			admitted2.Close()
			t.Fatal("blocked waiter expected non-nil result")
		}
		got.run.Close()
		// Release the blocked waiter's lease.
		svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: got.run.Dispatch().RunID, Type: "complete"})
		got.run.Close()
		// Release admitted2's lease.
		svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: admitted2.Dispatch().RunID, Type: "complete"})
		admitted2.Close()
	case <-time.After(2 * time.Second):
		admitted2.Close()
		t.Fatal("raised capacity did not pump the blocked waiter")
	}

	aInFlight, aLong := providerResourceCounts(svc.queue, "node-cap-shrink", "prov-shrink")
	if aInFlight != 0 || aLong != 0 {
		t.Fatalf("expected full drain, got inFlight=%d longInFlight=%d", aInFlight, aLong)
	}
	if lc := leaseCount(svc.queue); lc != 0 {
		t.Fatalf("expected leaseCount=0, got %d", lc)
	}
}

// TestRefreshLongCapacityShrinkPreservesLeaseAndBlocksLongAdmission verifies
// that shrinking long context capacity preserves existing long leases and
// blocks new long admission until recovery.
func TestRefreshLongCapacityShrinkPreservesLeaseAndBlocksLongAdmission(t *testing.T) {
	enabled := true
	svc, applyFn := buildCapacityRebaseService(t, "node-lc-shrink", "prov-lc-shrink", "served-lc-shrink", 3, 2, &enabled)

	admitted1, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
		ModelGroupKey: "group-cap-rebase", ProviderPool: true, ContextClass: "long",
	})
	if err != nil || admitted1 == nil {
		t.Fatalf("admit long 1 failed: %v", err)
	}
	admitted2, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
		ModelGroupKey: "group-cap-rebase", ProviderPool: true, ContextClass: "long",
	})
	if err != nil || admitted2 == nil {
		admitted1.Close()
		t.Fatalf("admit long 2 failed: %v", err)
	}

	svc.queue.mu.Lock()
	res := svc.queue.resources[providerResourceKey{nodeID: "node-lc-shrink", providerID: "prov-lc-shrink"}]
	if res == nil || res.longInFlight != 2 {
		svc.queue.mu.Unlock()
		admitted1.Close()
		admitted2.Close()
		t.Fatalf("expected longInFlight=2, got %d", res.longInFlight)
	}
	svc.queue.mu.Unlock()

	applyFn(3, 1, true)

	svc.queue.mu.Lock()
	if res.longCapacity != 1 {
		svc.queue.mu.Unlock()
		admitted1.Close()
		admitted2.Close()
		t.Fatalf("expected longCapacity=1, got %d", res.longCapacity)
	}
	if res.longInFlight != 2 {
		svc.queue.mu.Unlock()
		admitted1.Close()
		admitted2.Close()
		t.Fatalf("expected longInFlight preserved at 2, got %d", res.longInFlight)
	}
	svc.queue.mu.Unlock()

	blockedCh := make(chan struct {
		run RunResult
		err error
	}, 1)
	go func() {
		run, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
			ModelGroupKey: "group-cap-rebase", ProviderPool: true, ContextClass: "long",
		})
		blockedCh <- struct {
			run RunResult
			err error
		}{run: run, err: err}
	}()

	waitForQueueLen(t, svc.queue, "group-cap-rebase", 1)
	inFlight, longInFlight := providerResourceCounts(svc.queue, "node-lc-shrink", "prov-lc-shrink")
	if inFlight != 2 || longInFlight != 2 {
		admitted1.Close()
		admitted2.Close()
		t.Fatalf("blocked long admission changed counts: inFlight=%d longInFlight=%d", inFlight, longInFlight)
	}

	svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: admitted1.Dispatch().RunID, Type: "complete"})
	applyFn(3, 2, true)

	select {
	case got := <-blockedCh:
		if got.err != nil {
			admitted2.Close()
			t.Fatalf("blocked long waiter expected dispatch, got: %v", got.err)
		}
		got.run.Close()
		// Release the blocked waiter's lease.
		svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: got.run.Dispatch().RunID, Type: "complete"})
		got.run.Close()
		// Release admitted2's lease.
		svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: admitted2.Dispatch().RunID, Type: "complete"})
		admitted2.Close()
	case <-time.After(2 * time.Second):
		admitted2.Close()
		t.Fatal("raised long capacity did not pump the blocked waiter")
	}

	aInFlight, aLong := providerResourceCounts(svc.queue, "node-lc-shrink", "prov-lc-shrink")
	if aInFlight != 0 || aLong != 0 {
		t.Fatalf("expected full drain, got inFlight=%d longInFlight=%d", aInFlight, aLong)
	}
	if lc := leaseCount(svc.queue); lc != 0 {
		t.Fatalf("expected leaseCount=0, got %d", lc)
	}
}

// TestRefreshPriorityChangesQueuedProviderSelection verifies that a priority
// refresh via SetRuntimeConfig is picked up by the live candidate resolver
// so that a queued waiter selects the higher-priority provider. The priority
// update itself leaves capacity and enabled state unchanged.
func TestRefreshPriorityChangesQueuedProviderSelection(t *testing.T) {
	svc, applyConfig := buildPriorityService(t)
	applyConfig(1, 1, 0, 10, true)

	first, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
		ModelGroupKey: "group-prio", ProviderPool: true, Background: true,
	})
	if err != nil {
		t.Fatalf("first SubmitRun: %v", err)
	}
	second, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
		ModelGroupKey: "group-prio", ProviderPool: true, Background: true,
	})
	if err != nil {
		t.Fatalf("second SubmitRun: %v", err)
	}
	if first.Dispatch().ProviderID != "prov-a" || second.Dispatch().ProviderID != "prov-b" {
		t.Fatalf("unexpected initial provider order: first=%q second=%q", first.Dispatch().ProviderID, second.Dispatch().ProviderID)
	}

	type submitResult struct {
		run RunResult
		err error
	}
	blockedCh := make(chan submitResult, 1)
	go func() {
		run, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
			ModelGroupKey: "group-prio", ProviderPool: true, Background: true,
		})
		blockedCh <- submitResult{run: run, err: err}
	}()
	waitForQueueLen(t, svc.queue, "group-prio", 1)

	// Change priority while both held slots are still occupied so the pump
	// cannot dispatch the waiter. Then release both held slots under the
	// manager lock so the waiter dispatches with the new priority ordering.
	applyConfig(1, 1, 10, 0, true)
	if pending := pendingItemCount(svc.queue); pending != 1 {
		t.Fatalf("priority-only refresh changed pending count to %d, want 1", pending)
	}

	// Release both runs while holding the queue lock so the pump does not
	// dispatch the waiter to the first-available provider on each release.
	svc.queue.mu.Lock()
	m := svc.queue
	m.releaseLeaseLocked(m.leaseByRun[first.Dispatch().RunID])
	m.releaseLeaseLocked(m.leaseByRun[second.Dispatch().RunID])
	m.pumpAllLocked()
	svc.queue.mu.Unlock()

	var third RunResult
	select {
	case got := <-blockedCh:
		if got.err != nil {
			t.Fatalf("queued SubmitRun: %v", got.err)
		}
		third = got.run
		if third.Dispatch().ProviderID != "prov-b" {
			t.Fatalf("priority refresh selected %q, want prov-b", third.Dispatch().ProviderID)
		}
	case <-time.After(2 * time.Second):
		t.Fatal("priority refresh did not pump the queued waiter")
	}

	svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: third.Dispatch().RunID, Type: "complete"})
	third.Close()
	if pending := pendingItemCount(svc.queue); pending != 0 {
		t.Fatalf("expected no pending items, got %d", pending)
	}
	if leases := leaseCount(svc.queue); leases != 0 {
		t.Fatalf("expected no live leases, got %d", leases)
	}
}

// TestRefreshDisableReenableReevaluatesAllWaiters verifies that a provider
// disable followed by re-enable re-evaluates ALL queued waiters through the
// live candidate resolver, not just the first.
func TestRefreshDisableReenableReevaluatesAllWaiters(t *testing.T) {
	enabled := true
	svc, applyConfig := buildCapacityRebaseService(t, "node-dre", "prov-dre", "served-dre", 2, 0, &enabled)

	held := make([]RunResult, 0, 2)
	for i := 0; i < 2; i++ {
		run, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
			ModelGroupKey: "group-cap-rebase", ProviderPool: true, Background: true,
		})
		if err != nil {
			t.Fatalf("fill capacity %d: %v", i, err)
		}
		held = append(held, run)
	}

	type submitResult struct {
		run RunResult
		err error
	}
	waiters := make(chan submitResult, 2)
	for i := 0; i < 2; i++ {
		go func() {
			run, err := svc.SubmitRun(t.Context(), SubmitRunRequest{
				ModelGroupKey: "group-cap-rebase", ProviderPool: true, Background: true,
			})
			waiters <- submitResult{run: run, err: err}
		}()
	}
	waitForQueueLen(t, svc.queue, "group-cap-rebase", 2)

	// Disable while the waiters are queued, then free both held slots. A stale
	// candidate would dispatch here; the live provider snapshot must keep both queued.
	applyConfig(2, 0, false)
	for _, run := range held {
		svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: run.Dispatch().RunID, Type: "complete"})
		run.Close()
	}
	if pending := pendingItemCount(svc.queue); pending != 2 {
		t.Fatalf("expected 2 pending while provider is disabled, got %d", pending)
	}
	if inFlight, _ := providerResourceCounts(svc.queue, "node-dre", "prov-dre"); inFlight != 0 {
		t.Fatalf("expected held slots to drain while disabled, got inFlight=%d", inFlight)
	}

	// Re-enable through SetRuntimeConfig. Both real SubmitRun waiters must be
	// re-evaluated and dispatched, not merely a synthetic queue item.
	applyConfig(2, 0, true)
	dispatched := make([]RunResult, 0, 2)
	for i := 0; i < 2; i++ {
		select {
		case got := <-waiters:
			if got.err != nil {
				t.Fatalf("waiter %d after re-enable: %v", i, got.err)
			}
			dispatched = append(dispatched, got.run)
		case <-time.After(2 * time.Second):
			t.Fatalf("waiter %d not dispatched after re-enable", i)
		}
	}
	for _, run := range dispatched {
		svc.HandleRunLifecycleEvent(&iop.RunEvent{RunId: run.Dispatch().RunID, Type: "complete"})
		run.Close()
	}

	aInFlight, aLong := providerResourceCounts(svc.queue, "node-dre", "prov-dre")
	if aInFlight != 0 || aLong != 0 {
		t.Fatalf("expected full drain, got inFlight=%d longInFlight=%d", aInFlight, aLong)
	}
	if lc := leaseCount(svc.queue); lc != 0 {
		t.Fatalf("expected leaseCount=0, got %d", lc)
	}
}

// --- PROVIDER_POLICY_REFRESH-3: concurrent refresh/admission/release race ---

// TestRefreshConcurrentAdmissionReleasePreservesProviderPolicyInvariants verifies
// refresh/admit/release/cancel races across normal and long-context requests.
func TestRefreshConcurrentAdmissionReleasePreservesProviderPolicyInvariants(t *testing.T) {
	const (
		nodeID   = "node-concurrent"
		provider = "prov-concurrent"
	)

	reg := edgenode.NewRegistry()
	entry := &edgenode.NodeEntry{NodeID: nodeID}
	reg.Register(entry)
	svc := New(reg, edgeevents.NewBus())

	catalog := []config.ModelCatalogEntry{{
		ID:        "group-concurrent",
		Providers: map[string]string{provider: "served-concurrent"},
	}}

	cands := []candidateNode{{entry: entry, capacity: 4, providerID: provider}}

	newStore := func(capacity, longCapacity int) *edgenode.NodeStore {
		store := edgenode.NewNodeStore()
		store.Add(&edgenode.NodeRecord{
			ID: nodeID,
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{{
				ID:                  provider,
				Type:                "vllm",
				Adapter:             "vllm-gpu",
				Models:              []string{"served-concurrent"},
				Health:              "available",
				Capacity:            capacity,
				LongContextCapacity: longCapacity,
			}},
		})
		return store
	}
	highPolicy := groupPolicy{maxQueue: 64, queueTimeout: 15 * time.Millisecond}
	svc.SetRuntimeConfig(newStore(4, 2), catalog, highPolicy)

	var (
		seenMu     sync.Mutex
		seenLeases = map[uint64]struct{}{}
		peak       int
		peakLong   int
		peakMu     sync.Mutex
	)
	observe := func() {
		inFlight, longInFlight := providerResourceCounts(svc.queue, nodeID, provider)
		peakMu.Lock()
		defer peakMu.Unlock()
		if inFlight > peak {
			peak = inFlight
		}
		if longInFlight > peakLong {
			peakLong = longInFlight
		}
	}

	errs := make(chan error, 2048)
	report := func(err error) {
		if err != nil {
			errs <- err
		}
	}
	start := make(chan struct{})
	var wg sync.WaitGroup
	var cancelWG sync.WaitGroup

	// Repeatedly shrink and expand while workers are admitting and releasing.
	// During each shrink interval occupancy may remain above the new limit due
	// to preserved leases, but it must never grow beyond the post-shrink value.
	wg.Add(1)
	go func() {
		defer wg.Done()
		<-start
		for i := 0; i < 80; i++ {
			svc.SetRuntimeConfig(newStore(2, 1), catalog, groupPolicy{
				maxQueue: 64, queueTimeout: time.Duration(i%2) * 15 * time.Millisecond,
			})
			shrinkInFlight, shrinkLong := providerResourceCounts(svc.queue, nodeID, provider)
			normalLimit := max(shrinkInFlight, 2)
			longLimit := max(shrinkLong, 1)
			for sample := 0; sample < 4; sample++ {
				runtime.Gosched()
				inFlight, longInFlight := providerResourceCounts(svc.queue, nodeID, provider)
				if inFlight > normalLimit {
					report(fmt.Errorf("post-shrink inFlight grew from %d to %d (limit %d)", shrinkInFlight, inFlight, normalLimit))
				}
				if longInFlight > longLimit {
					report(fmt.Errorf("post-shrink longInFlight grew from %d to %d (limit %d)", shrinkLong, longInFlight, longLimit))
				}
				if inFlight > 4 || longInFlight > 2 {
					report(fmt.Errorf("global capacity bound exceeded: inFlight=%d longInFlight=%d", inFlight, longInFlight))
				}
			}
			svc.SetRuntimeConfig(newStore(4, 2), catalog, highPolicy)
		}
	}()

	for worker := 0; worker < 8; worker++ {
		worker := worker
		wg.Add(1)
		go func() {
			defer wg.Done()
			<-start
			for attempt := 0; attempt < 24; attempt++ {
				ctx, cancel := context.WithTimeout(context.Background(), 80*time.Millisecond)
				if attempt%4 == 0 {
					cancelWG.Add(1)
					go func() {
						defer cancelWG.Done()
						runtime.Gosched()
						cancel()
					}()
				}
				candidate, err := svc.queue.admit(ctx, "group-concurrent", "", "", cands,
					highPolicy, nil, worker%2 == 0, true)
				cancel()
				if err != nil {
					if !errors.Is(err, context.DeadlineExceeded) &&
						!errors.Is(err, context.Canceled) &&
						!errors.Is(err, errQueueTimeout) {
						report(fmt.Errorf("worker %d admit %d: %w", worker, attempt, err))
					}
					continue
				}
				seenMu.Lock()
				if _, duplicate := seenLeases[candidate.leaseID]; duplicate {
					report(fmt.Errorf("lease %d handed out more than once", candidate.leaseID))
				}
				seenLeases[candidate.leaseID] = struct{}{}
				seenMu.Unlock()
				observe()
				runtime.Gosched()
				svc.queue.releaseLease(candidate.leaseID, "complete")
				svc.queue.releaseLease(candidate.leaseID, "duplicate_complete")
			}
		}()
	}

	close(start)
	wg.Wait()
	cancelWG.Wait()
	close(errs)
	for err := range errs {
		if err != nil {
			t.Error(err)
		}
	}

	svc.SetRuntimeConfig(newStore(4, 2), catalog, highPolicy)
	deadline := time.Now().Add(2 * time.Second)
	for time.Now().Before(deadline) {
		inFlight, longInFlight := providerResourceCounts(svc.queue, nodeID, provider)
		if inFlight == 0 && longInFlight == 0 && pendingItemCount(svc.queue) == 0 && leaseCount(svc.queue) == 0 {
			break
		}
		runtime.Gosched()
	}

	peakMu.Lock()
	defer peakMu.Unlock()
	if len(seenLeases) == 0 {
		t.Error("expected at least one successful concurrent admission")
	}
	if peak > 4 {
		t.Errorf("provider in-flight peaked at %d, above configured capacity 4", peak)
	}
	if peakLong > 2 {
		t.Errorf("long in-flight peaked at %d, above configured long capacity 2", peakLong)
	}

	inFlight, longInFlight := providerResourceCounts(svc.queue, nodeID, provider)
	pending := pendingItemCount(svc.queue)
	lc := leaseCount(svc.queue)
	if inFlight != 0 {
		t.Errorf("expected inFlight=0 at end, got %d", inFlight)
	}
	if longInFlight != 0 {
		t.Errorf("expected longInFlight=0 at end, got %d", longInFlight)
	}
	if pending != 0 {
		t.Errorf("expected pending=0 at end, got %d", pending)
	}
	if lc != 0 {
		t.Errorf("expected leaseCount=0 at end, got %d", lc)
	}
}

// resourceStateForTest returns the (present, orphan, generation, inFlight) view
// of a provider resource under the manager lock. Reconnect-activation tests use
// it to assert the orphan marker and generation transitions directly.
func resourceStateForTest(m *modelQueueManager, nodeID, providerID string) (present, orphan bool, generation uint64, inFlight int) {
	m.mu.Lock()
	defer m.mu.Unlock()
	res, ok := m.resources[providerResourceKey{nodeID: nodeID, providerID: providerID}]
	if !ok {
		return false, false, 0, 0
	}
	return true, res.orphan, res.generation, res.inFlight
}

// TestAcceptedReconnectActivatesCandidateAndPumpsWaiter verifies the S15
// reconnect-candidate-recovery contract at the service/queue boundary: with the
// only alternate provider full and the target node disconnected (its resource
// orphaned), a waiter is stranded; the accepted reconnect alone — via
// HandleNodeConnect — re-activates the node's resource for the new generation and
// pumps the waiter to it, with no new request, config refresh, or lease release.
// It also pins that a stale/absent-generation connect callback is a no-op.
func TestAcceptedReconnectActivatesCandidateAndPumpsWaiter(t *testing.T) {
	const (
		nodeA      = "node-recon-a"
		nodeB      = "node-recon-b"
		provA      = "prov-recon-a"
		provB      = "prov-recon-b"
		modelGroup = "group-recon"
		servedA    = "served-recon-a"
		servedB    = "served-recon-b"
	)

	reg := edgenode.NewRegistry()
	entryA := &edgenode.NodeEntry{NodeID: nodeA}
	entryB := &edgenode.NodeEntry{NodeID: nodeB}
	reg.Register(entryA)
	reg.Register(entryB)
	genA := entryA.ConnectionGeneration
	if genA == 0 {
		t.Fatal("node A must carry a non-zero connection generation")
	}

	svc := New(reg, edgeevents.NewBus())
	store := edgenode.NewNodeStore()
	newRecord := func(nodeID, providerID, served string) *edgenode.NodeRecord {
		return &edgenode.NodeRecord{
			ID: nodeID,
			Adapters: config.AdaptersConf{
				VllmInstances: []config.VllmInstanceConf{
					{Name: "vllm-gpu", Enabled: true, Endpoint: "http://127.0.0.1:8000/v1"},
				},
			},
			Providers: []config.NodeProviderConf{{
				ID: providerID, Type: "vllm", Adapter: "vllm-gpu",
				Models: []string{served}, Health: "available", Capacity: 1,
			}},
		}
	}
	store.Add(newRecord(nodeA, provA, servedA))
	store.Add(newRecord(nodeB, provB, servedB))
	catalog := []config.ModelCatalogEntry{{
		ID:        modelGroup,
		Providers: map[string]string{provA: servedA, provB: servedB},
	}}
	svc.SetRuntimeConfig(store, catalog, NewGroupPolicy(16, 3*time.Second))

	// Fill the alternate provider B so it cannot absorb the waiter. Hand-build the
	// candidate so the fill is forced onto B regardless of scheduler ordering.
	fillCand := []candidateNode{{
		entry: entryB, capacity: 1, providerID: provB,
		servedTarget: servedB, generation: entryB.ConnectionGeneration,
	}}
	filled, err := svc.queue.admit(t.Context(), modelGroup, "", "", fillCand, groupPolicy{}, nil, false, true)
	if err != nil || filled == nil {
		t.Fatalf("fill alternate provider B: %v", err)
	}
	if inFlight, _ := providerResourceCounts(svc.queue, nodeB, provB); inFlight != 1 {
		t.Fatalf("expected provB in-flight=1 after fill, got %d", inFlight)
	}

	// Disconnect node A (idle, holding no lease). The authoritative disconnect
	// marks provA's resource orphan/offline.
	reg.Unregister(nodeA)
	svc.HandleNodeDisconnect(nodeA, genA, "disconnected")
	present, orphan, _, _ := resourceStateForTest(svc.queue, nodeA, provA)
	if !present || !orphan {
		t.Fatalf("after disconnect provA resource present=%t orphan=%t, want true/true", present, orphan)
	}

	// A stale/absent-generation connect callback must not re-activate: node A is
	// no longer the registry owner (it is absent), so IsCurrentOwnerGeneration
	// fails and the callback is a no-op.
	svc.HandleNodeConnect(nodeA, genA)
	if _, stillOrphan, _, _ := resourceStateForTest(svc.queue, nodeA, provA); !stillOrphan {
		t.Fatal("stale connect callback re-activated the orphaned resource")
	}

	// Enqueue the waiter. At enqueue time node A is disconnected, so the only live
	// candidate is B, which is full — the request queues.
	req := SubmitRunRequest{ModelGroupKey: modelGroup, ProviderPool: true}
	type admitOutcome struct {
		candidate *candidateNode
		err       error
	}
	waiterCh := make(chan admitOutcome, 1)
	waiterCtx, cancelWaiter := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancelWaiter()
	go func() {
		enqCands, _, resolveErr := svc.resolveQueueCandidates(req)
		if resolveErr != nil {
			waiterCh <- admitOutcome{err: resolveErr}
			return
		}
		candidate, admitErr := svc.queue.admit(
			waiterCtx, modelGroup, "", "", enqCands, groupPolicy{},
			svc.resolveQueueCandidatesClosure(req), false, true,
		)
		waiterCh <- admitOutcome{candidate: candidate, err: admitErr}
	}()

	waitForQueueLen(t, svc.queue, modelGroup, 1)
	select {
	case outcome := <-waiterCh:
		t.Fatalf("waiter resolved before reconnect: candidate=%+v err=%v", outcome.candidate, outcome.err)
	default:
	}

	// Reconnect the same identity: a strictly higher generation is minted. The
	// accepted-connect hook alone must re-activate provA and dispatch the waiter —
	// no config refresh, no new request, no lease release is issued here.
	reconnectA := &edgenode.NodeEntry{NodeID: nodeA}
	reg.Register(reconnectA)
	newGen := reconnectA.ConnectionGeneration
	if newGen <= genA {
		t.Fatalf("reconnect generation %d must exceed the first %d", newGen, genA)
	}
	svc.HandleNodeConnect(nodeA, newGen)

	select {
	case outcome := <-waiterCh:
		if outcome.err != nil {
			t.Fatalf("waiter expected dispatch after reconnect, got error: %v", outcome.err)
		}
		if outcome.candidate == nil {
			t.Fatal("waiter expected a candidate after reconnect")
		}
		if outcome.candidate.providerID != provA || outcome.candidate.entry.NodeID != nodeA {
			t.Fatalf("waiter dispatched to (%q,%q), want (%q,%q)",
				outcome.candidate.entry.NodeID, outcome.candidate.providerID, nodeA, provA)
		}
		if outcome.candidate.generation != newGen {
			t.Fatalf("waiter candidate generation=%d, want the reconnect generation %d", outcome.candidate.generation, newGen)
		}
	case <-time.After(2 * time.Second):
		t.Fatal("accepted reconnect did not pump the stranded waiter")
	}

	// The activation re-enabled provA for the reconnect generation and the pump
	// reserved it. The alternate B's lease was never released, proving the pump was
	// triggered by the reconnect alone.
	present, orphan, gen, inFlightA := resourceStateForTest(svc.queue, nodeA, provA)
	if !present || orphan {
		t.Fatalf("after reconnect provA present=%t orphan=%t, want true/false", present, orphan)
	}
	if gen != newGen {
		t.Fatalf("provA resource generation=%d, want %d", gen, newGen)
	}
	if inFlightA != 1 {
		t.Fatalf("provA in-flight=%d after waiter dispatch, want 1", inFlightA)
	}
	if inFlightB, _ := providerResourceCounts(svc.queue, nodeB, provB); inFlightB != 1 {
		t.Fatalf("provB in-flight=%d, want 1 (its lease must not have been released)", inFlightB)
	}
	_ = filled
}
