import type { SectionDef } from './types.js';
import { type ResolvedSection, type TraceEntry } from './evaluator.js';
import { type CliSpecV4, type FsmCondition } from './fsm-types.js';
export interface FsmClock {
    /** Wall-clock now (ms). Passed in so the evaluator stays pure. */
    now: number;
    /** When the current state was entered (ms). */
    stateEnteredAt: number;
    /** Last-changed timestamp per stable region. Numeric key = cursor_above
     *  value (0 / undefined → whole-screen, keyed as -1). String key =
     *  `section:<id>` for a section-scoped stable region. Missing key means
     *  "never observed changing" → treated as stable since stateEnteredAt. */
    regionLastChangedAt: Map<number | string, number>;
}
/** Per-condition evaluation detail — the debugging payload. */
export interface CondResult {
    kind: 'regex' | 'changed' | 'elapsed' | 'stable' | 'all' | 'any' | 'not';
    result: boolean;
    detail: string;
    /** Remaining ms until a time-based condition would flip to true. 0 if
     *  already true or not applicable. Lets the UI show a countdown. */
    remainingMs?: number;
    /** Debug-only: the actual substring a TRUE regex condition matched, so the
     *  snapshot shows WHAT text the rule fired on — not just which regex. Never
     *  read by the FSM; purely for the Spec Debug Snapshot. */
    matchedText?: string;
    children?: CondResult[];
}
/** One evaluated transition with its full reasoning. */
export interface TransitionEval {
    to: string;
    label: string;
    /** Did the `from` clause include the current state? (Always true for the
     *  transitions we return — kept for completeness.) */
    eligible: boolean;
    /** min_hold_ms guard satisfied? */
    holdSatisfied: boolean;
    holdRemainingMs: number;
    /** Guard condition result (true if no `when`). */
    condResult: boolean;
    cond?: CondResult;
    /** Overall: would this transition fire? */
    fires: boolean;
    priority: number;
}
export interface FsmEvaluation {
    /** Sections resolved from the screen (shared with v3 shape). */
    sections: ResolvedSection[];
    /** Every outgoing transition from the current state, priority-ordered,
     *  each annotated with why it fired or didn't. */
    transitions: TransitionEval[];
    /** The transition that fires (first in priority order with fires===true),
     *  or null to stay in the current state. */
    fired: TransitionEval | null;
    /** v3-compatible trace lines for the legacy inspector. */
    trace: TraceEntry[];
}
/** The `regionLastChangedAt` key for a stable condition. The key must capture
 *  everything that makes two stable clauses watch a DIFFERENT change signal:
 *  the geometric region (`section` wins over `cursor_above`, else whole-screen)
 *  AND the `ignore_lines` filter (two clauses on the same region but different
 *  ignore patterns see different "did it change" answers). Kept in one place so
 *  the driver (which populates the map) and the evaluator (which reads it)
 *  agree on the key.
 *
 *  Bare whole-screen with no ignore filter keeps its historical numeric key -1
 *  so existing callers/tests that seed `regionLastChangedAt` with [-1, ...]
 *  keep working unchanged. */
export declare function stableRegionKey(cond: {
    section?: string;
    cursor_above?: number;
    ignore_lines?: string;
}): number | string;
/**
 * Evaluate the FSM: given the current state id and screen, return every
 * outgoing transition annotated with why it fires/doesn't, plus the one that
 * fires (if any).
 */
export declare function evaluateFsm(spec: CliSpecV4, currentStateId: string, screenText: string, cursor: {
    row: number;
    col: number;
} | undefined, prevLines: string[] | undefined, clock: FsmClock): FsmEvaluation;
/**
 * Evaluate a single condition against a screen + section map — for the spec
 * editor's live preview ("does this regex match the current screen right
 * now?"). Time leaves (elapsed_ms/stable_ms) are evaluated against a synthetic
 * clock where the state was just entered, so they report their countdown
 * rather than firing; the preview is about screen-content match, not timing.
 */
export declare function evaluateConditionPreview(cond: FsmCondition, sections: Record<string, SectionDef> | undefined, screenText: string, cursor?: {
    row: number;
    col: number;
}): CondResult;
