import { MapDef, pruneSetToMax } from "@lodestar/utils";
import { prettyPrintPeerId } from "../../util.js";
import { DEFAULT_SCORE, MAX_ENTRIES, MAX_SCORE, MIN_SCORE, SCORE_THRESHOLD } from "./constants.js";
import { PeerAction } from "./interface.js";
import { MaxScore, RealScore } from "./score.js";
import { scoreToState } from "./utils.js";
const peerActionScore = {
    [PeerAction.Fatal]: -(MAX_SCORE - MIN_SCORE),
    [PeerAction.LowToleranceError]: -10,
    [PeerAction.MidToleranceError]: -5,
    [PeerAction.HighToleranceError]: -1,
};
/**
 * A peer's score (perceived potential usefulness).
 * This simplistic version consists of a global score per peer which decays to 0 over time.
 * The decay rate applies equally to positive and negative scores.
 * Peer cool-down period will be checked before dialing and will only be dialed if score is not waiting to decay
 */
export class PeerRpcScoreStore {
    scores;
    metrics;
    logger;
    // TODO: Persist scores, at least BANNED status to disk
    constructor(opts = {}, metrics = null, logger = null) {
        this.metrics = metrics;
        this.logger = logger;
        this.scores = opts.disablePeerScoring ? new MapDef(() => new MaxScore()) : new MapDef(() => new RealScore());
    }
    getScore(peer) {
        return this.scores.get(peer.toString())?.getScore() ?? DEFAULT_SCORE;
    }
    getGossipScore(peer) {
        return this.scores.get(peer.toString())?.getGossipScore() ?? DEFAULT_SCORE;
    }
    getScoreState(peer) {
        return scoreToState(this.getScore(peer));
    }
    isCoolingDown(peerIdStr) {
        return this.scores.get(peerIdStr)?.isCoolingDown() ?? false;
    }
    dumpPeerScoreStats() {
        return Array.from(this.scores.entries()).map(([peerId, peerScore]) => ({ peerId, ...peerScore.getStat() }));
    }
    applyAction(peer, action, actionName) {
        const peerScore = this.scores.getOrDefault(peer.toString());
        const scoreChange = peerActionScore[action];
        const newScore = peerScore.add(scoreChange);
        this.logger?.debug("peer score adjusted", { scoreChange, newScore, peerId: prettyPrintPeerId(peer), actionName });
        this.metrics?.peersReportPeerCount.inc({ reason: actionName });
    }
    /**
     * Apply a reconnection cool-down period to prevent automatic reconnection. Sets peer
     * banning period and updates gossip score to -1 so next update removes the negative
     * score
     */
    applyReconnectionCoolDown(peer, reason) {
        const peerScore = this.scores.getOrDefault(peer);
        return peerScore.applyReconnectionCoolDown(reason);
    }
    update() {
        // Bound size of data structures
        pruneSetToMax(this.scores, MAX_ENTRIES);
        for (const [peerIdStr, peerScore] of this.scores) {
            const newScore = peerScore.update();
            // Prune scores below threshold
            if (Math.abs(newScore) < SCORE_THRESHOLD) {
                this.scores.delete(peerIdStr);
            }
        }
    }
    updateGossipsubScore(peerId, newScore, ignore) {
        const peerScore = this.scores.getOrDefault(peerId);
        peerScore.updateGossipsubScore(newScore, ignore);
    }
}
//# sourceMappingURL=store.js.map