"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TERMINAL_REMINDER_RANDOM_SHOW_THRESHOLD = exports.REMINDER_GRACE_PERIOD_MILLISECONDS = exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT = void 0;
exports.advanceReminderGracePeriod = advanceReminderGracePeriod;
exports.evaluateTerminalReminderCadence = evaluateTerminalReminderCadence;
exports.applyReminderCadenceStateUpdate = applyReminderCadenceStateUpdate;
exports.evaluateGraceStatus = evaluateGraceStatus;
const node_crypto_1 = require("node:crypto");
const command_write_summary_1 = require("./command-write-summary");
const reminder_storage_1 = require("./reminder-storage");
exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT = 3;
exports.REMINDER_GRACE_PERIOD_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
exports.TERMINAL_REMINDER_RANDOM_SHOW_THRESHOLD = 0.2;
function advanceReminderGracePeriod(options) {
    const commandCompletedAt = (0, reminder_storage_1.utcTimestampValue)(options.commandCompletedAt);
    if (commandCompletedAt === undefined) {
        throw new TypeError('commandCompletedAt must be a UTC RFC 3339 timestamp');
    }
    if (!options.isEligibleCommand) {
        const graceStatus = evaluateGraceStatus(options.state, commandCompletedAt);
        return {
            state: options.state,
            reason: 'ineligible_command_suppression',
            suppressionReasons: ['ineligible_command'],
            graceActive: graceStatus.graceActive,
            terminalReminderSuppressed: true,
            persistedReminderSuppressed: true,
            postGraceCadenceEligible: false,
            elapsedMillisecondsSinceFirstEligibleCommand: graceStatus.elapsedMilliseconds,
        };
    }
    const firstEligibleCommandAt = options.state.eligibleCommandCount === 0 ? commandCompletedAt : options.state.firstEligibleCommandAt;
    const elapsedMilliseconds = (0, reminder_storage_1.timestampMilliseconds)(commandCompletedAt) - (0, reminder_storage_1.timestampMilliseconds)(firstEligibleCommandAt);
    const eligibleCommandCount = options.state.eligibleCommandCount + 1;
    const state = {
        ...options.state,
        firstEligibleCommandAt,
        eligibleCommandCount,
    };
    if (state.lastReminderAt !== null) {
        return postGraceEvaluation(state, 'count_ended_grace', elapsedMilliseconds);
    }
    const eligibleCommandThresholdReached = eligibleCommandCount >= exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT;
    const elapsedTimeThresholdReached = elapsedMilliseconds >= exports.REMINDER_GRACE_PERIOD_MILLISECONDS;
    if (eligibleCommandThresholdReached && elapsedTimeThresholdReached) {
        return postGraceEvaluation(state, options.state.eligibleCommandCount < exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT
            ? 'count_ended_grace'
            : 'elapsed_time_ended_grace', elapsedMilliseconds);
    }
    return {
        state,
        reason: 'grace_suppression',
        suppressionReasons: [
            ...(eligibleCommandThresholdReached ? [] : ['eligible_command_count_below_threshold']),
            ...(elapsedTimeThresholdReached ? [] : ['elapsed_time_below_threshold']),
        ],
        graceActive: true,
        terminalReminderSuppressed: true,
        persistedReminderSuppressed: true,
        postGraceCadenceEligible: false,
        elapsedMillisecondsSinceFirstEligibleCommand: elapsedMilliseconds,
    };
}
function evaluateTerminalReminderCadence(options) {
    if (!options.graceEvaluation.postGraceCadenceEligible) {
        return cadenceDecision({
            state: options.state,
            reason: 'grace_suppression',
            cadenceBucket: 'grace',
            terminalReminderVisible: false,
            persistedReminderEligible: false,
            postGraceCadenceEligible: false,
            cadenceWouldShowTerminalReminder: false,
            eligibleCommandOrdinalSinceLastReminder: null,
            pseudoRandomValue: null,
            randomShowThreshold: null,
        });
    }
    const baseDecision = evaluatePostGraceTerminalCadence(options);
    if (!baseDecision.terminalReminderVisible || options.terminalReminderOutputEligible) {
        return baseDecision;
    }
    return cadenceDecision({
        ...baseDecision,
        reason: 'output_mode_suppression',
        terminalReminderVisible: false,
    });
}
function applyReminderCadenceStateUpdate(options) {
    const commandCompletedAt = (0, reminder_storage_1.utcTimestampValue)(options.commandCompletedAt);
    if (commandCompletedAt === undefined) {
        throw new TypeError('commandCompletedAt must be a UTC RFC 3339 timestamp');
    }
    const resolvedPath = (0, reminder_storage_1.resolveReminderStatePath)(options);
    const commandReminderEligibility = options.commandWriteSummary === undefined
        ? null
        : (0, command_write_summary_1.classifyReminderEligibilityFromCommandSummary)(options.commandWriteSummary);
    const isEligibleCommand = commandReminderEligibility?.terminalCadenceContributor ?? options.isEligibleCommand;
    const terminalReminderOutputEligible = commandReminderEligibility?.terminalEligible ?? options.terminalReminderOutputEligible;
    const commandPersistedReminderEligible = commandReminderEligibility?.persistedReminderEligible ?? true;
    if (options.suppressReminders) {
        return suppressedCadenceStateUpdateResult({
            resolvedPath,
            reason: 'license_status_suppression',
            writeSuppressedReason: 'license_status_suppression',
            commandReminderEligibility,
        });
    }
    if (!isEligibleCommand) {
        return suppressedCadenceStateUpdateResult({
            resolvedPath,
            reason: 'ineligible_command_suppression',
            writeSuppressedReason: 'ineligible_command',
            commandReminderEligibility,
        });
    }
    const stateInput = loadCadenceUpdateState(options, commandCompletedAt);
    const graceEvaluation = advanceReminderGracePeriod({
        state: stateInput.state,
        commandCompletedAt,
        isEligibleCommand: true,
    });
    const terminalCadenceDecision = evaluateTerminalReminderCadence({
        state: graceEvaluation.state,
        graceEvaluation,
        terminalReminderOutputEligible,
        persistedReminderEligible: commandPersistedReminderEligible,
        pseudoRandomValue: options.pseudoRandomValue,
    });
    const mutation = applyTerminalCadenceStateMutation(graceEvaluation.state, terminalCadenceDecision, commandCompletedAt);
    const writtenPath = (0, reminder_storage_1.writeReminderState)(mutation.state, options);
    return {
        state: mutation.state,
        statePath: writtenPath.statePath,
        stateDirectory: writtenPath.stateDirectory,
        stateDirectorySource: writtenPath.stateDirectorySource,
        reason: terminalCadenceDecision.reason,
        stateWritten: true,
        graceEvaluation,
        terminalCadenceDecision,
        commandReminderEligibility,
        terminalReminderVisible: terminalCadenceDecision.terminalReminderVisible,
        persistedReminderEligible: terminalCadenceDecision.persistedReminderEligible,
        prngSeedAdvanced: mutation.prngSeedAdvanced,
        diagnostic: createCadenceStateUpdateDiagnostic({
            resolvedPath: writtenPath,
            readStatus: stateInput.readStatus,
            writeSuppressedReason: null,
            stateWritten: true,
            state: mutation.state,
            seedSource: stateInput.seedSource,
            prngSeedAdvanced: mutation.prngSeedAdvanced,
        }),
    };
}
function evaluateGraceStatus(state, commandCompletedAt) {
    if (state.lastReminderAt !== null) {
        return {
            graceActive: false,
            elapsedMilliseconds: null,
        };
    }
    if (state.eligibleCommandCount > 0) {
        const elapsedMilliseconds = (0, reminder_storage_1.timestampMilliseconds)(commandCompletedAt) - (0, reminder_storage_1.timestampMilliseconds)(state.firstEligibleCommandAt);
        return {
            graceActive: state.eligibleCommandCount < exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT ||
                elapsedMilliseconds < exports.REMINDER_GRACE_PERIOD_MILLISECONDS,
            elapsedMilliseconds,
        };
    }
    return {
        graceActive: true,
        elapsedMilliseconds: null,
    };
}
function postGraceEvaluation(state, reason, elapsedMilliseconds) {
    return {
        state,
        reason,
        suppressionReasons: [],
        graceActive: false,
        terminalReminderSuppressed: false,
        persistedReminderSuppressed: false,
        postGraceCadenceEligible: true,
        elapsedMillisecondsSinceFirstEligibleCommand: elapsedMilliseconds,
    };
}
function evaluatePostGraceTerminalCadence(options) {
    const persistedReminderEligible = options.persistedReminderEligible ?? true;
    if (options.state.lastReminderAt === null) {
        return cadenceDecision({
            state: options.state,
            reason: 'first_reminder_show',
            cadenceBucket: 'first_reminder',
            terminalReminderVisible: true,
            persistedReminderEligible,
            postGraceCadenceEligible: true,
            cadenceWouldShowTerminalReminder: true,
            eligibleCommandOrdinalSinceLastReminder: null,
            pseudoRandomValue: null,
            randomShowThreshold: null,
        });
    }
    const eligibleCommandOrdinalSinceLastReminder = options.state.eligibleCommandsSinceLastReminder + 1;
    if (eligibleCommandOrdinalSinceLastReminder <= 2) {
        return cadenceDecision({
            state: options.state,
            reason: 'post_reminder_window_suppression',
            cadenceBucket: 'post_reminder_suppression_window',
            terminalReminderVisible: false,
            persistedReminderEligible,
            postGraceCadenceEligible: true,
            cadenceWouldShowTerminalReminder: false,
            eligibleCommandOrdinalSinceLastReminder,
            pseudoRandomValue: null,
            randomShowThreshold: null,
        });
    }
    if (eligibleCommandOrdinalSinceLastReminder <= 4) {
        const pseudoRandomValue = options.pseudoRandomValue ?? deriveTerminalReminderPseudoRandomValue(options.state);
        assertValidPseudoRandomValue(pseudoRandomValue);
        const terminalReminderVisible = pseudoRandomValue < exports.TERMINAL_REMINDER_RANDOM_SHOW_THRESHOLD;
        return cadenceDecision({
            state: options.state,
            reason: terminalReminderVisible ? 'random_show' : 'random_suppression',
            cadenceBucket: 'random_chance',
            terminalReminderVisible,
            persistedReminderEligible,
            postGraceCadenceEligible: true,
            cadenceWouldShowTerminalReminder: terminalReminderVisible,
            eligibleCommandOrdinalSinceLastReminder,
            pseudoRandomValue,
            randomShowThreshold: exports.TERMINAL_REMINDER_RANDOM_SHOW_THRESHOLD,
        });
    }
    return cadenceDecision({
        state: options.state,
        reason: 'forced_show',
        cadenceBucket: 'forced_fifth_command',
        terminalReminderVisible: true,
        persistedReminderEligible,
        postGraceCadenceEligible: true,
        cadenceWouldShowTerminalReminder: true,
        eligibleCommandOrdinalSinceLastReminder,
        pseudoRandomValue: null,
        randomShowThreshold: null,
    });
}
function cadenceDecision(decision) {
    return {
        ...decision,
        terminalReminderSuppressed: !decision.terminalReminderVisible,
    };
}
function deriveTerminalReminderPseudoRandomValue(state) {
    const digest = (0, node_crypto_1.createHash)('sha256')
        .update(state.prngSeed)
        .update('\0')
        .update(state.lastReminderAt ?? '')
        .update('\0')
        .update(String(state.eligibleCommandsSinceLastReminder + 1))
        .digest();
    return digest.readUInt32BE(0) / 0x100000000;
}
function loadCadenceUpdateState(options, commandCompletedAt) {
    if (options.state !== undefined) {
        return {
            state: options.state,
            readStatus: 'provided-state',
            seedSource: 'provided-state',
        };
    }
    const readResult = (0, reminder_storage_1.readReminderState)({
        ...options,
        newState: (0, reminder_storage_1.createReminderStateInitializationOptions)({
            firstEligibleCommandAt: commandCompletedAt,
            prngSeed: options.prngSeed,
            env: options.env,
            allowTestSeedFromEnvironment: options.allowTestSeedFromEnvironment,
            randomSeedBytes: options.randomSeedBytes,
        }),
    });
    return {
        state: readResult.state,
        readStatus: readResult.status,
        seedSource: readResult.loadDiagnostic.seed_source,
    };
}
function applyTerminalCadenceStateMutation(state, decision, commandCompletedAt) {
    if (!decision.postGraceCadenceEligible) {
        return {
            state,
            prngSeedAdvanced: false,
        };
    }
    const prngSeedAdvanced = decision.pseudoRandomValue !== null;
    const lastReminderAt = decision.terminalReminderVisible ? commandCompletedAt : state.lastReminderAt;
    const eligibleCommandsSinceLastReminder = nextEligibleCommandsSinceLastReminder(state, decision);
    return {
        state: {
            ...state,
            lastReminderAt,
            eligibleCommandsSinceLastReminder,
            prngSeed: prngSeedAdvanced ? deriveNextTerminalReminderPrngSeed(state, decision) : state.prngSeed,
        },
        prngSeedAdvanced,
    };
}
function nextEligibleCommandsSinceLastReminder(state, decision) {
    if (decision.terminalReminderVisible) {
        return 0;
    }
    if (state.lastReminderAt === null) {
        return state.eligibleCommandsSinceLastReminder;
    }
    return state.eligibleCommandsSinceLastReminder + 1;
}
function deriveNextTerminalReminderPrngSeed(state, decision) {
    return (0, node_crypto_1.createHash)('sha256')
        .update(state.prngSeed)
        .update('\0next-terminal-reminder-seed\0')
        .update(state.lastReminderAt ?? '')
        .update('\0')
        .update(String(decision.eligibleCommandOrdinalSinceLastReminder ?? ''))
        .digest('base64url');
}
function suppressedCadenceStateUpdateResult(options) {
    return {
        state: null,
        statePath: options.resolvedPath.statePath,
        stateDirectory: options.resolvedPath.stateDirectory,
        stateDirectorySource: options.resolvedPath.stateDirectorySource,
        reason: options.reason,
        stateWritten: false,
        graceEvaluation: null,
        terminalCadenceDecision: null,
        commandReminderEligibility: options.commandReminderEligibility,
        terminalReminderVisible: false,
        persistedReminderEligible: false,
        prngSeedAdvanced: false,
        diagnostic: createCadenceStateUpdateDiagnostic({
            resolvedPath: options.resolvedPath,
            readStatus: 'not-read',
            writeSuppressedReason: options.writeSuppressedReason,
            stateWritten: false,
            state: null,
            seedSource: null,
            prngSeedAdvanced: false,
        }),
    };
}
function createCadenceStateUpdateDiagnostic(options) {
    return {
        state_path: options.resolvedPath.statePath,
        state_directory_source: options.resolvedPath.stateDirectorySource,
        read_status: options.readStatus,
        write_suppressed_reason: options.writeSuppressedReason,
        state_written: options.stateWritten,
        first_eligible_command_at: options.state?.firstEligibleCommandAt ?? null,
        eligible_command_count: options.state?.eligibleCommandCount ?? null,
        last_reminder_at: options.state?.lastReminderAt ?? null,
        eligible_commands_since_last_reminder: options.state?.eligibleCommandsSinceLastReminder ?? null,
        last_invalid_license_warning_at: options.state?.lastInvalidLicenseWarningAt ?? null,
        seed_source: options.seedSource,
        prng_seed_advanced: options.prngSeedAdvanced,
    };
}
function assertValidPseudoRandomValue(value) {
    if (!Number.isFinite(value) || value < 0 || value >= 1) {
        throw new TypeError('pseudoRandomValue must be greater than or equal to 0 and less than 1');
    }
}
