@startuml dht_discovery
title Découverte DHT : Provide/FindProviders + SelectByFillRate + dhtCache indexeur

participant "Indexer A\n(nouveau)" as IA
participant "DHT Network" as DHT
participant "Node B\n(bootstrap)" as NodeB
participant "Indexer A\n(existant)" as IAexist

== Inscription indexeur dans la DHT ==

note over IA: Démarrage IndexerService\nstartDHTProvide(fillRateFn)

IA -> IA: Attend adresse routable (max 60s)\nnon-loopback disponible

IA -> DHT: DHT.Bootstrap(ctx)\n→ routing table warmup

loop ticker RecommendedHeartbeatInterval (~20s)
    IA -> DHT: DHT.Provide(IndexerCID, true)\n← IndexerCID = CID(sha256("/opencloud/indexers"))
    note over DHT: L'indexeur est annoncé comme provider.\nTTL géré par libp2p-kad-dht.\nAuto-expire si Provide() s'arrête.
end

== Cache DHT passif de l'indexeur ==

note over IA: startDHTCacheRefresh()\ngoroutine arrière-plan

IA -> IA: Initial delay 30s (routing table warmup)

loop ticker 2min
    IA -> DHT: DiscoverIndexersFromDHT(h, dht, 30)\n← FindProviders(IndexerCID, max=30)
    DHT --> IA: []AddrInfo (jusqu'à 30 candidats)
    IA -> IA: Filtre self\nSelectByFillRate(filtered, nil, 10)\n→ diversité /24, prior f=0.5 (fill rates inconnus)
    IA -> IA: dhtCache = selected (max 10)\n→ utilisé pour Suggestions dans BuildHeartbeatResponse
end

== Découverte côté Node au bootstrap ==

NodeB -> NodeB: ConnectToIndexers → seeds ajoutés\nSendHeartbeat démarré

NodeB -> NodeB: goroutine proactive (après 5s warmup)

alt discoveryDHT == nil (node pur, pas d'IndexerService)
    NodeB -> DHT: initNodeDHT(h, seeds)\n← DHT client mode, bootstrappé sur seeds
end

NodeB -> DHT: DiscoverIndexersFromDHT(h, discoveryDHT, need+extra)
DHT --> NodeB: []AddrInfo candidats

NodeB -> NodeB: Filtre self\nSelectByFillRate(candidates, fillRates, need)\n→ pondération w(F) = F×(1-F)\n  F=0.2 → w=0.16 (très probable)\n  F=0.5 → w=0.25 (max)\n  F=0.8 → w=0.16 (peu probable)\n→ filtre diversité /24

loop Pour chaque candidat retenu
    NodeB -> NodeB: Indexers.SetAddr(key, &addrInfo)\nNudgeIt() → heartbeat immédiat
end

note over NodeB: Pool enrichi au-delà des seeds.\nScoring commence au premier heartbeat.\nSeeds restent IsSeed=true (stickiness).

@enduml
