package discovery

import (
	"testing"
	"time"
)

func TestRegistryUpdateHeartbeatByKey_UpdatesOnlyTargetEntry(t *testing.T) {
	now := time.Date(2026, 2, 27, 12, 0, 0, 0, time.UTC)
	oldHeartbeat := now.Add(-45 * time.Second)
	registry := NewRegistry()

	oldVersion := RegistryEntry{
		ID:            "mcards",
		InstanceID:    "inst-1",
		Service:       "CARD",
		Rail:          RailCardPayout,
		Operations:    []string{OperationSend},
		Version:       "3.0.0-old",
		Status:        "ok",
		Health:        HealthParams{TimeoutSec: 30},
		LastHeartbeat: oldHeartbeat,
	}
	newVersion := RegistryEntry{
		ID:            "mcards",
		InstanceID:    "inst-1",
		Service:       "CARD",
		Rail:          RailCardPayout,
		Operations:    []string{OperationSend},
		Version:       "3.0.0-new",
		Status:        "ok",
		Health:        HealthParams{TimeoutSec: 30},
		LastHeartbeat: oldHeartbeat,
	}

	registry.UpsertEntry(oldVersion, now)
	registry.UpsertEntry(newVersion, now)

	oldKey := registryEntryKey(normalizeEntry(oldVersion))
	newKey := registryEntryKey(normalizeEntry(newVersion))

	result, found := registry.UpdateHeartbeatByKey(newKey, "ok", now, now)
	if !found {
		t.Fatalf("expected heartbeat target key to exist: %q", newKey)
	}
	if got := registryEntryKey(normalizeEntry(result.Entry)); got != newKey {
		t.Fatalf("unexpected updated key: got=%q want=%q", got, newKey)
	}

	entries := registry.List(now, false)
	if len(entries) != 2 {
		t.Fatalf("unexpected entries count: got=%d want=2", len(entries))
	}

	sawOld := false
	sawNew := false
	for _, entry := range entries {
		key := registryEntryKey(normalizeEntry(entry))
		switch key {
		case oldKey:
			sawOld = true
			if !entry.LastHeartbeat.Equal(oldHeartbeat) {
				t.Fatalf("old key heartbeat changed unexpectedly: got=%s want=%s", entry.LastHeartbeat, oldHeartbeat)
			}
			if entry.Healthy {
				t.Fatalf("old key should remain unhealthy")
			}
		case newKey:
			sawNew = true
			if !entry.LastHeartbeat.Equal(now) {
				t.Fatalf("new key heartbeat not updated: got=%s want=%s", entry.LastHeartbeat, now)
			}
			if !entry.Healthy {
				t.Fatalf("new key should be healthy after heartbeat")
			}
		}
	}
	if !sawOld {
		t.Fatalf("old key entry not found after update: %q", oldKey)
	}
	if !sawNew {
		t.Fatalf("new key entry not found after update: %q", newKey)
	}
}

func TestRegistryUpdateHeartbeat_LegacyFallbackUpdatesAllMatchingInstanceEntries(t *testing.T) {
	now := time.Date(2026, 2, 27, 12, 0, 0, 0, time.UTC)
	oldHeartbeat := now.Add(-45 * time.Second)
	registry := NewRegistry()

	entries := []RegistryEntry{
		{
			ID:            "mcards",
			InstanceID:    "inst-1",
			Service:       "CARD",
			Rail:          RailCardPayout,
			Operations:    []string{OperationSend},
			Version:       "3.0.0-old",
			Status:        "ok",
			Health:        HealthParams{TimeoutSec: 30},
			LastHeartbeat: oldHeartbeat,
		},
		{
			ID:            "mcards",
			InstanceID:    "inst-1",
			Service:       "CARD",
			Rail:          RailCardPayout,
			Operations:    []string{OperationSend},
			Version:       "3.0.0-new",
			Status:        "ok",
			Health:        HealthParams{TimeoutSec: 30},
			LastHeartbeat: oldHeartbeat,
		},
	}
	for _, entry := range entries {
		registry.UpsertEntry(entry, now)
	}

	results := registry.UpdateHeartbeat("mcards", "inst-1", "ok", now, now)
	if len(results) != 2 {
		t.Fatalf("unexpected updated count: got=%d want=2", len(results))
	}

	all := registry.List(now, false)
	for _, entry := range all {
		if !entry.LastHeartbeat.Equal(now) {
			t.Fatalf("entry heartbeat not updated: key=%s got=%s want=%s", registryEntryKey(normalizeEntry(entry)), entry.LastHeartbeat, now)
		}
		if !entry.Healthy {
			t.Fatalf("entry should be healthy after legacy heartbeat: key=%s", registryEntryKey(normalizeEntry(entry)))
		}
	}
}
