/**
 * Cycle detection for loop monitors.
 *
 * Tracks step execution history and detects when a specific cycle
 * of steps has been repeated a configured number of times (threshold).
 *
 * Example:
 *   cycle: [ai_review, ai_fix], threshold: 3
 *   History: ai_review → ai_fix → ai_review → ai_fix → ai_review → ai_fix
 *                                                                     ↑
 *                                                              3 cycles → trigger
 */
/**
 * Tracks step execution history and detects cyclic patterns
 * as defined by loop_monitors configuration.
 */
export class CycleDetector {
    /** Step execution history (names in order) */
    history = [];
    monitors;
    constructor(monitors = []) {
        this.monitors = monitors;
    }
    /**
     * Record a step completion and check if any cycle threshold is reached.
     *
     * The detection logic works as follows:
     * 1. The step name is appended to the history
     * 2. For each monitor, we check if the cycle pattern has been completed
     *    by looking at the tail of the history
     * 3. A cycle is "completed" when the last N entries in history match
     *    the cycle pattern repeated `threshold` times
     *
     * @param stepName The name of the step that just completed
     * @returns CycleCheckResult indicating if any monitor was triggered
     */
    recordAndCheck(stepName) {
        this.history.push(stepName);
        for (const monitor of this.monitors) {
            const result = this.checkMonitor(monitor);
            if (result.triggered) {
                return result;
            }
        }
        return { triggered: false, cycleCount: 0 };
    }
    /**
     * Check a single monitor against the current history.
     *
     * A cycle is detected when the last element of the history matches the
     * last element of the cycle, and looking backwards we can find exactly
     * `threshold` complete cycles.
     */
    checkMonitor(monitor) {
        const { cycle, threshold } = monitor;
        const cycleLen = cycle.length;
        // The cycle's last step must match the most recent step
        const lastStep = cycle[cycleLen - 1];
        if (this.history[this.history.length - 1] !== lastStep) {
            return { triggered: false, cycleCount: 0 };
        }
        // Need at least threshold * cycleLen entries to check
        const requiredLen = threshold * cycleLen;
        if (this.history.length < requiredLen) {
            return { triggered: false, cycleCount: 0 };
        }
        // Count complete cycles from the end of history backwards
        let cycleCount = 0;
        let pos = this.history.length;
        while (pos >= cycleLen) {
            // Check if the last cycleLen entries match the cycle pattern
            let matches = true;
            for (let i = 0; i < cycleLen; i++) {
                if (this.history[pos - cycleLen + i] !== cycle[i]) {
                    matches = false;
                    break;
                }
            }
            if (matches) {
                cycleCount++;
                pos -= cycleLen;
            }
            else {
                break;
            }
        }
        if (cycleCount >= threshold) {
            return { triggered: true, cycleCount, monitor };
        }
        return { triggered: false, cycleCount };
    }
    /**
     * Reset the history after a judge intervention.
     * This prevents the same cycle from immediately triggering again.
     */
    reset() {
        this.history = [];
    }
    /**
     * Get the current step history (for debugging/testing).
     */
    getHistory() {
        return this.history;
    }
}
//# sourceMappingURL=cycle-detector.js.map