/*
    lft_pathwatch.h — J1 path-change alert heuristic for watch mode (-W)

    Pure state machine: no ncurses, no session, no network dependencies.
    Classifies per-TTL hop observations across watch cycles into path-change
    verdicts with a learning gate, 2-cycle hysteresis, and multipath (ECMP)
    tolerance.  See Output/LFT/j1-path-change-heuristic-design.md.

    This file is part of LFT.
*/
#ifndef LFT_PATHWATCH_H
#define LFT_PATHWATCH_H

#define PW_KNOWN_MAX    8   /* remembered IPs per TTL (ECMP set)            */
#define PW_IPLEN       64   /* matches watch-mode host[64] strings          */
#define PW_ECMP_LOG_MAX 3   /* ECMP-shift log lines per hop before suppress */

typedef struct {
    char known_ips[PW_KNOWN_MAX][PW_IPLEN]; /* IPs ever seen at this TTL    */
    int  n_known;
    char pending_ip[PW_IPLEN];  /* candidate change awaiting hysteresis     */
    int  pending_cycles;        /* observed sightings of pending_ip         */
    int  ecmp_logged;           /* ECMP-shift lines emitted for this hop    */
} watch_pathwatch_t;

typedef enum {
    PW_IGNORE = 0,      /* cloaked/empty on either side — no action         */
    PW_STABLE,          /* cur == confirmed, nothing pending                */
    PW_DISCOVERY,       /* pre-arm or new-slot observation, learned quietly */
    PW_PENDING,         /* new IP, hysteresis started/continuing            */
    PW_FLAP,            /* pending reverted to confirmed — quiet log        */
    PW_ECMP_SHIFT,      /* known multipath member — quiet log (rate-capped) */
    PW_CONFIRMED        /* change persisted 2 observed sightings — ALERT    */
} pw_verdict_t;

/* Classify one hop observation.  Mutates pw and confirmed (in-out buffer of
 * PW_IPLEN).  flap_was (out, PW_IPLEN, may be NULL) receives the reverted
 * pending IP on PW_FLAP.  On PW_CONFIRMED the known set is reset to {cur}
 * so a later reversion to the old path alerts (route restore) instead of
 * reading as an ECMP shift.  Pending-match is tested before set membership
 * — that ordering is load-bearing (see design doc).                        */
pw_verdict_t pw_observe(watch_pathwatch_t *pw, char *confirmed,
                        const char *cur, int armed, char *flap_was);

/* Add an IP to the known set without classification (alt_hosts feeding,
 * discovery).  Deduplicating; silently drops when the set is full.         */
void pw_note_ip(watch_pathwatch_t *pw, const char *ip);

/* Arming latch: returns 1 once stable_cycles >= 3 or cycle >= 8, and never
 * returns 0 after having returned 1 (pass the prior value back as armed).  */
int pw_update_armed(int armed, int stable_cycles, int cycle);

/* Clear all per-hop heuristic state (watch reset key).                     */
void pw_reset(watch_pathwatch_t *pw);

#endif /* LFT_PATHWATCH_H */
