"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TERMINAL_REMINDER_RANDOM_SHOW_THRESHOLD = exports.REMINDER_GRACE_PERIOD_MILLISECONDS = exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT = exports.DAY_SHIFT_REMINDER_SEED_ENV = exports.REMINDER_STATE_FILE_NAME = exports.REMINDER_STATE_SCHEMA_VERSION = void 0;
exports.parseReminderStateContents = parseReminderStateContents;
exports.createUnreadableReminderStateDiagnostic = createUnreadableReminderStateDiagnostic;
exports.parseReminderStateValue = parseReminderStateValue;
exports.createMissingReminderState = createMissingReminderState;
exports.createReminderStateInitializationOptions = createReminderStateInitializationOptions;
exports.serializeReminderState = serializeReminderState;
exports.advanceReminderGracePeriod = advanceReminderGracePeriod;
exports.evaluateTerminalReminderCadence = evaluateTerminalReminderCadence;
exports.evaluateInvalidLicenseWarningPolicy = evaluateInvalidLicenseWarningPolicy;
exports.applyInvalidLicenseWarningStateUpdate = applyInvalidLicenseWarningStateUpdate;
exports.resolveReminderStatePath = resolveReminderStatePath;
exports.readReminderState = readReminderState;
exports.createReminderStatusDiagnostics = createReminderStatusDiagnostics;
exports.writeReminderState = writeReminderState;
exports.applyReminderCadenceStateUpdate = applyReminderCadenceStateUpdate;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const node_crypto_1 = require("node:crypto");
const path_resolution_1 = require("../runtime/path-resolution");
const command_write_summary_1 = require("./command-write-summary");
exports.REMINDER_STATE_SCHEMA_VERSION = 1;
exports.REMINDER_STATE_FILE_NAME = "reminder-state.json";
exports.DAY_SHIFT_REMINDER_SEED_ENV = "DAY_SHIFT_REMINDER_SEED";
exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT = 3;
exports.REMINDER_GRACE_PERIOD_MILLISECONDS = 30 * 60 * 1000;
exports.TERMINAL_REMINDER_RANDOM_SHOW_THRESHOLD = 0.4;
const REQUIRED_REMINDER_STATE_FIELDS = [
    "reminder_state_schema_version",
    "first_eligible_command_at",
    "eligible_command_count",
    "last_reminder_at",
    "eligible_commands_since_last_reminder",
    "prng_seed",
    "last_invalid_license_warning_at"
];
const UTC_RFC3339_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?Z$/;
const BASE64URL_SEED_PATTERN = /^[A-Za-z0-9_-]+$/;
function parseReminderStateContents(contents) {
    let decoded;
    try {
        decoded = JSON.parse(contents);
    }
    catch {
        return invalid("malformed_json");
    }
    return parseReminderStateValue(decoded);
}
function createUnreadableReminderStateDiagnostic() {
    return invalid("unreadable_state");
}
function parseReminderStateValue(value) {
    if (!isRecord(value)) {
        return invalid("malformed_state");
    }
    const missingField = REQUIRED_REMINDER_STATE_FIELDS.find((field) => !Object.hasOwn(value, field));
    if (missingField !== undefined) {
        return invalid("missing_required_field", missingField);
    }
    if (value.reminder_state_schema_version !== exports.REMINDER_STATE_SCHEMA_VERSION) {
        return invalid("unsupported_schema_version", "reminder_state_schema_version");
    }
    const firstEligibleCommandAt = utcTimestampValue(value.first_eligible_command_at);
    if (firstEligibleCommandAt === undefined) {
        return invalid("invalid_timestamp", "first_eligible_command_at");
    }
    const eligibleCommandCount = nonNegativeIntegerValue(value.eligible_command_count);
    if (eligibleCommandCount === undefined) {
        return invalid("invalid_counter", "eligible_command_count");
    }
    const lastReminderAt = nullableUtcTimestampValue(value.last_reminder_at);
    if (lastReminderAt === undefined) {
        return invalid("invalid_nullable_timestamp", "last_reminder_at");
    }
    const eligibleCommandsSinceLastReminder = nonNegativeIntegerValue(value.eligible_commands_since_last_reminder);
    if (eligibleCommandsSinceLastReminder === undefined) {
        return invalid("invalid_counter", "eligible_commands_since_last_reminder");
    }
    const prngSeed = prngSeedValue(value.prng_seed);
    if (prngSeed === undefined) {
        return invalid("invalid_prng_seed", "prng_seed");
    }
    const lastInvalidLicenseWarningAt = nullableUtcTimestampValue(value.last_invalid_license_warning_at);
    if (lastInvalidLicenseWarningAt === undefined) {
        return invalid("invalid_nullable_timestamp", "last_invalid_license_warning_at");
    }
    return {
        ok: true,
        state: {
            reminderStateSchemaVersion: exports.REMINDER_STATE_SCHEMA_VERSION,
            firstEligibleCommandAt,
            eligibleCommandCount,
            lastReminderAt,
            eligibleCommandsSinceLastReminder,
            prngSeed,
            lastInvalidLicenseWarningAt
        }
    };
}
function createMissingReminderState(options) {
    const firstEligibleCommandAt = utcTimestampValue(options.firstEligibleCommandAt);
    if (firstEligibleCommandAt === undefined) {
        throw new TypeError("firstEligibleCommandAt must be a UTC RFC 3339 timestamp");
    }
    const prngSeed = prngSeedValue(options.prngSeed);
    if (prngSeed === undefined) {
        throw new TypeError("prngSeed must be non-empty base64url seed material");
    }
    return {
        reminderStateSchemaVersion: exports.REMINDER_STATE_SCHEMA_VERSION,
        firstEligibleCommandAt,
        eligibleCommandCount: 0,
        lastReminderAt: null,
        eligibleCommandsSinceLastReminder: 0,
        prngSeed,
        lastInvalidLicenseWarningAt: null
    };
}
function createReminderStateInitializationOptions(options) {
    const explicitSeed = normalizeOptionalSeed(options.prngSeed);
    if (explicitSeed !== undefined) {
        assertValidPrngSeed(explicitSeed);
        return {
            firstEligibleCommandAt: options.firstEligibleCommandAt,
            prngSeed: explicitSeed,
            prngSeedSource: "test-option"
        };
    }
    const environmentSeed = options.allowTestSeedFromEnvironment === true
        ? normalizeOptionalSeed((options.env ?? process.env)[exports.DAY_SHIFT_REMINDER_SEED_ENV])
        : undefined;
    if (environmentSeed !== undefined) {
        assertValidPrngSeed(environmentSeed);
        return {
            firstEligibleCommandAt: options.firstEligibleCommandAt,
            prngSeed: environmentSeed,
            prngSeedSource: "test-environment"
        };
    }
    const seedBytes = options.randomSeedBytes?.() ?? (0, node_crypto_1.randomBytes)(32);
    return {
        firstEligibleCommandAt: options.firstEligibleCommandAt,
        prngSeed: Buffer.from(seedBytes).toString("base64url"),
        prngSeedSource: "local-generated"
    };
}
function serializeReminderState(state) {
    return {
        reminder_state_schema_version: exports.REMINDER_STATE_SCHEMA_VERSION,
        first_eligible_command_at: state.firstEligibleCommandAt,
        eligible_command_count: state.eligibleCommandCount,
        last_reminder_at: state.lastReminderAt,
        eligible_commands_since_last_reminder: state.eligibleCommandsSinceLastReminder,
        prng_seed: state.prngSeed,
        last_invalid_license_warning_at: state.lastInvalidLicenseWarningAt
    };
}
function advanceReminderGracePeriod(options) {
    const commandCompletedAt = 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 = timestampMilliseconds(commandCompletedAt) - timestampMilliseconds(firstEligibleCommandAt);
    const eligibleCommandCount = options.state.eligibleCommandCount + 1;
    const state = {
        ...options.state,
        firstEligibleCommandAt,
        eligibleCommandCount
    };
    if (elapsedMilliseconds >= exports.REMINDER_GRACE_PERIOD_MILLISECONDS) {
        return postGraceEvaluation(state, "elapsed_time_ended_grace", elapsedMilliseconds);
    }
    if (eligibleCommandCount >= exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT) {
        return postGraceEvaluation(state, "count_ended_grace", elapsedMilliseconds);
    }
    return {
        state,
        reason: "grace_suppression",
        suppressionReasons: [
            "eligible_command_count_below_threshold",
            "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 evaluateInvalidLicenseWarningPolicy(options) {
    const commandCompletedAt = utcTimestampValue(options.commandCompletedAt);
    if (commandCompletedAt === undefined) {
        throw new TypeError("commandCompletedAt must be a UTC RFC 3339 timestamp");
    }
    const warning = createInvalidLicenseWarningDiagnostic(options.licenseStatus);
    const lastInvalidLicenseWarningAt = options.reminderState?.lastInvalidLicenseWarningAt
        ?? options.reminderStateLoadDiagnostic?.last_invalid_license_warning_at
        ?? null;
    if (warning === null) {
        const reason = noWarningReason(options.licenseStatus);
        return invalidLicenseWarningDecision({
            state: options.reminderState ?? null,
            reason,
            warningVisible: false,
            warningSuppressed: false,
            writeSuppressedReason: reason,
            commandCompletedAt,
            humanReadableWarningEligible: options.humanReadableWarningEligible,
            lastInvalidLicenseWarningAt,
            warning: null
        });
    }
    const warningWithState = {
        ...warning,
        last_invalid_license_warning_at: lastInvalidLicenseWarningAt
    };
    if (!options.humanReadableWarningEligible) {
        return invalidLicenseWarningDecision({
            state: options.reminderState ?? null,
            reason: "human_readable_output_suppression",
            warningVisible: false,
            warningSuppressed: true,
            writeSuppressedReason: "human_readable_output_suppression",
            commandCompletedAt,
            humanReadableWarningEligible: false,
            lastInvalidLicenseWarningAt,
            warning: warningWithState
        });
    }
    if (lastInvalidLicenseWarningAt !== null) {
        return invalidLicenseWarningDecision({
            state: options.reminderState ?? null,
            reason: "repeat_suppression",
            warningVisible: false,
            warningSuppressed: true,
            writeSuppressedReason: "repeat_suppression",
            commandCompletedAt,
            humanReadableWarningEligible: true,
            lastInvalidLicenseWarningAt,
            warning: warningWithState
        });
    }
    const state = options.reminderState === undefined || options.reminderState === null
        ? null
        : {
            ...options.reminderState,
            lastInvalidLicenseWarningAt: commandCompletedAt
        };
    const visibleWarning = {
        ...warning,
        last_invalid_license_warning_at: commandCompletedAt
    };
    return invalidLicenseWarningDecision({
        state,
        reason: warning.warning_source === "configured-license"
            ? "configured_license_warning"
            : "installed_license_warning",
        warningVisible: true,
        warningSuppressed: false,
        writeSuppressedReason: null,
        commandCompletedAt,
        humanReadableWarningEligible: true,
        lastInvalidLicenseWarningAt: commandCompletedAt,
        warning: visibleWarning
    });
}
function applyInvalidLicenseWarningStateUpdate(options) {
    const commandCompletedAt = utcTimestampValue(options.commandCompletedAt);
    if (commandCompletedAt === undefined) {
        throw new TypeError("commandCompletedAt must be a UTC RFC 3339 timestamp");
    }
    const resolvedPath = resolveReminderStatePath(options);
    const preflightDecision = evaluateInvalidLicenseWarningPolicy({
        licenseStatus: options.licenseStatus,
        reminderState: null,
        commandCompletedAt,
        humanReadableWarningEligible: options.humanReadableWarningEligible
    });
    if (preflightDecision.warningDiagnostic === null ||
        !options.humanReadableWarningEligible) {
        return invalidLicenseWarningStateUpdateResult({
            resolvedPath,
            readStatus: "not-read",
            stateWritten: false,
            state: null,
            decision: preflightDecision
        });
    }
    const stateInput = loadInvalidLicenseWarningUpdateState(options, commandCompletedAt);
    const decision = evaluateInvalidLicenseWarningPolicy({
        licenseStatus: options.licenseStatus,
        reminderState: stateInput.state,
        reminderStateLoadDiagnostic: stateInput.loadDiagnostic,
        commandCompletedAt,
        humanReadableWarningEligible: true
    });
    if (!decision.warningVisible || decision.state === null) {
        return invalidLicenseWarningStateUpdateResult({
            resolvedPath,
            readStatus: stateInput.readStatus,
            stateWritten: false,
            state: stateInput.state,
            decision
        });
    }
    const writtenPath = writeReminderState(decision.state, options);
    return invalidLicenseWarningStateUpdateResult({
        resolvedPath: writtenPath,
        readStatus: stateInput.readStatus,
        stateWritten: true,
        state: decision.state,
        decision
    });
}
function resolveReminderStatePath(options = {}) {
    const resolvedDirectory = (0, path_resolution_1.resolveDayShiftUserStateDirectory)(options);
    const statePath = node_path_1.default.join(resolvedDirectory.stateDirectory, exports.REMINDER_STATE_FILE_NAME);
    return {
        stateDirectory: resolvedDirectory.stateDirectory,
        stateDirectorySource: resolvedDirectory.source,
        statePath,
        isInsideRepository: options.repositoryRoot === undefined
            ? null
            : isPathInsideDirectory(statePath, options.repositoryRoot)
    };
}
function readReminderState(options) {
    const resolvedPath = resolveReminderStatePath(options);
    let contents;
    try {
        contents = node_fs_1.default.readFileSync(resolvedPath.statePath, "utf8");
    }
    catch (error) {
        if (isMissingFileError(error)) {
            return recoveredReadResult("initialized", resolvedPath, {
                reason: "missing_state"
            }, options.newState);
        }
        return recoveredReadResult("recovered", resolvedPath, {
            reason: "unreadable_state"
        }, options.newState);
    }
    const serializedSchemaVersion = readSerializedSchemaVersion(contents);
    const parsed = parseReminderStateContents(contents);
    if (!parsed.ok) {
        return recoveredReadResult("recovered", resolvedPath, parsed.diagnostic, options.newState, serializedSchemaVersion);
    }
    return {
        status: "loaded",
        state: parsed.state,
        statePath: resolvedPath.statePath,
        stateDirectory: resolvedPath.stateDirectory,
        stateDirectorySource: resolvedPath.stateDirectorySource,
        diagnostic: null,
        loadDiagnostic: createLoadDiagnostic({
            status: "loaded",
            resolvedPath,
            state: parsed.state,
            diagnostic: null,
            schemaVersion: parsed.state.reminderStateSchemaVersion,
            seedSource: "stored-state"
        })
    };
}
function createReminderStatusDiagnostics(options) {
    const commandCompletedAt = utcTimestampValue(options.commandCompletedAt);
    if (commandCompletedAt === undefined) {
        throw new TypeError("commandCompletedAt must be a UTC RFC 3339 timestamp");
    }
    if (options.licenseStatus.suppresses_reminders) {
        const warningDecision = evaluateInvalidLicenseWarningPolicy({
            licenseStatus: options.licenseStatus,
            reminderState: null,
            commandCompletedAt,
            humanReadableWarningEligible: true
        });
        return reminderStatusDiagnostics({
            readStatus: "not-read",
            stateDirectorySource: null,
            loadDiagnostic: null,
            state: null,
            gracePeriodState: "suppressed_by_license",
            nextReminderWindow: {
                kind: "suppressed_by_license",
                eligible_command_ordinal_since_last_reminder: null,
                eligible_commands_until_next_window: null,
                milliseconds_until_grace_ends: null,
                random_show_threshold: null
            },
            warningThrottle: warningDecision.diagnostic
        });
    }
    const readResult = readReminderState({
        ...options,
        newState: createReminderStateInitializationOptions({
            firstEligibleCommandAt: commandCompletedAt,
            prngSeed: options.prngSeed,
            env: options.env,
            allowTestSeedFromEnvironment: options.allowTestSeedFromEnvironment,
            randomSeedBytes: options.randomSeedBytes
        })
    });
    const graceStatus = evaluateGraceStatus(readResult.state, commandCompletedAt);
    const warningDecision = evaluateInvalidLicenseWarningPolicy({
        licenseStatus: options.licenseStatus,
        reminderState: readResult.state,
        reminderStateLoadDiagnostic: readResult.loadDiagnostic,
        commandCompletedAt,
        humanReadableWarningEligible: true
    });
    return reminderStatusDiagnostics({
        readStatus: readResult.status,
        stateDirectorySource: readResult.stateDirectorySource,
        loadDiagnostic: readResult.loadDiagnostic,
        state: readResult.state,
        gracePeriodState: graceStatus.graceActive ? "active" : "ended",
        nextReminderWindow: createReminderStatusNextWindow(readResult.state, graceStatus),
        warningThrottle: warningDecision.diagnostic
    });
}
function writeReminderState(state, options = {}) {
    const resolvedPath = resolveReminderStatePath(options);
    writeFileAtomically(resolvedPath.statePath, `${JSON.stringify(serializeReminderState(state), null, 2)}\n`);
    return resolvedPath;
}
function applyReminderCadenceStateUpdate(options) {
    const commandCompletedAt = utcTimestampValue(options.commandCompletedAt);
    if (commandCompletedAt === undefined) {
        throw new TypeError("commandCompletedAt must be a UTC RFC 3339 timestamp");
    }
    const resolvedPath = 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 = 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 invalid(reason, field) {
    return {
        ok: false,
        diagnostic: {
            reason,
            ...(field === undefined ? {} : { field })
        }
    };
}
function reminderStatusDiagnostics(options) {
    return {
        read_status: options.readStatus,
        state_directory_source: options.stateDirectorySource,
        schema_version: options.loadDiagnostic?.schema_version ?? null,
        recovery_reason: options.loadDiagnostic?.recovery_reason ?? null,
        ...(options.loadDiagnostic?.recovery_field === undefined
            ? {}
            : { recovery_field: options.loadDiagnostic.recovery_field }),
        grace_period_state: options.gracePeriodState,
        grace_eligible_command_limit: exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT,
        grace_period_milliseconds: exports.REMINDER_GRACE_PERIOD_MILLISECONDS,
        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,
        next_reminder_window: options.nextReminderWindow,
        warning_throttle: options.warningThrottle
    };
}
function createReminderStatusNextWindow(state, graceStatus) {
    if (graceStatus.graceActive) {
        return {
            kind: "grace_period",
            eligible_command_ordinal_since_last_reminder: null,
            eligible_commands_until_next_window: Math.max(exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT - state.eligibleCommandCount, 0),
            milliseconds_until_grace_ends: graceStatus.elapsedMilliseconds === null
                ? exports.REMINDER_GRACE_PERIOD_MILLISECONDS
                : Math.max(exports.REMINDER_GRACE_PERIOD_MILLISECONDS - graceStatus.elapsedMilliseconds, 0),
            random_show_threshold: null
        };
    }
    if (state.lastReminderAt === null) {
        return {
            kind: "first_reminder",
            eligible_command_ordinal_since_last_reminder: null,
            eligible_commands_until_next_window: 1,
            milliseconds_until_grace_ends: null,
            random_show_threshold: null
        };
    }
    const nextOrdinal = state.eligibleCommandsSinceLastReminder + 1;
    if (nextOrdinal <= 2) {
        return {
            kind: "post_reminder_suppression_window",
            eligible_command_ordinal_since_last_reminder: nextOrdinal,
            eligible_commands_until_next_window: 3 - state.eligibleCommandsSinceLastReminder,
            milliseconds_until_grace_ends: null,
            random_show_threshold: null
        };
    }
    if (nextOrdinal <= 4) {
        return {
            kind: "random_chance",
            eligible_command_ordinal_since_last_reminder: nextOrdinal,
            eligible_commands_until_next_window: 1,
            milliseconds_until_grace_ends: null,
            random_show_threshold: exports.TERMINAL_REMINDER_RANDOM_SHOW_THRESHOLD
        };
    }
    return {
        kind: "forced_fifth_command",
        eligible_command_ordinal_since_last_reminder: nextOrdinal,
        eligible_commands_until_next_window: 1,
        milliseconds_until_grace_ends: null,
        random_show_threshold: null
    };
}
function utcTimestampValue(value) {
    if (typeof value !== "string" || !isUtcRfc3339Timestamp(value)) {
        return undefined;
    }
    return value;
}
function nullableUtcTimestampValue(value) {
    if (value === null) {
        return null;
    }
    return utcTimestampValue(value);
}
function nonNegativeIntegerValue(value) {
    return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : undefined;
}
function prngSeedValue(value) {
    return typeof value === "string" && BASE64URL_SEED_PATTERN.test(value) ? value : undefined;
}
function assertValidPrngSeed(value) {
    if (prngSeedValue(value) === undefined) {
        throw new TypeError("prngSeed must be non-empty base64url seed material");
    }
}
function normalizeOptionalSeed(value) {
    if (value === undefined || value.trim() === "") {
        return undefined;
    }
    return value;
}
function evaluateGraceStatus(state, commandCompletedAt) {
    if (state.eligibleCommandCount >= exports.REMINDER_GRACE_ELIGIBLE_COMMAND_LIMIT) {
        return {
            graceActive: false,
            elapsedMilliseconds: null
        };
    }
    if (state.eligibleCommandCount > 0) {
        const elapsedMilliseconds = timestampMilliseconds(commandCompletedAt) - timestampMilliseconds(state.firstEligibleCommandAt);
        return {
            graceActive: 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 = readReminderState({
        ...options,
        newState: 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 loadInvalidLicenseWarningUpdateState(options, commandCompletedAt) {
    if (options.state !== undefined) {
        return {
            state: options.state,
            readStatus: "provided-state",
            loadDiagnostic: null
        };
    }
    const readResult = readReminderState({
        ...options,
        newState: createReminderStateInitializationOptions({
            firstEligibleCommandAt: commandCompletedAt,
            prngSeed: options.prngSeed,
            env: options.env,
            allowTestSeedFromEnvironment: options.allowTestSeedFromEnvironment,
            randomSeedBytes: options.randomSeedBytes
        })
    });
    return {
        state: readResult.state,
        readStatus: readResult.status,
        loadDiagnostic: readResult.loadDiagnostic
    };
}
function createInvalidLicenseWarningDiagnostic(licenseStatus) {
    if (licenseStatus.configured_license_diagnostic !== null) {
        const configuredDiagnostic = licenseStatus.configured_license_diagnostic;
        return {
            warning_source: "configured-license",
            reason_code: configuredDiagnostic.reason_code,
            configured_failure_kind: configuredDiagnostic.failure_kind,
            configured_source: configuredDiagnostic.source,
            configured_label: configuredDiagnostic.label,
            license_path: configuredDiagnostic.configured_path,
            fallback_mode: configuredDiagnostic.fallback_mode,
            suppresses_reminders: false,
            selected_source: licenseStatus.selected_source.source,
            selected_source_found: licenseStatus.selected_source.found,
            selected_source_readable: licenseStatus.selected_source.readable
        };
    }
    if (licenseStatus.effective_status !== "invalid-license" ||
        licenseStatus.suppresses_reminders ||
        licenseStatus.selected_suppressing_license_id !== null ||
        licenseStatus.reason_code === "missing") {
        return null;
    }
    return {
        warning_source: "installed-license",
        reason_code: licenseStatus.reason_code,
        configured_failure_kind: null,
        configured_source: null,
        configured_label: null,
        license_path: licenseStatus.license_path,
        fallback_mode: licenseStatus.fallback_mode,
        suppresses_reminders: false,
        selected_source: licenseStatus.selected_source.source,
        selected_source_found: licenseStatus.selected_source.found,
        selected_source_readable: licenseStatus.selected_source.readable
    };
}
function noWarningReason(licenseStatus) {
    if (licenseStatus.effective_status === "unlicensed" &&
        licenseStatus.reason_code === "missing" &&
        !licenseStatus.configured_source.configured) {
        return "missing_unconfigured_license";
    }
    if (licenseStatus.suppresses_reminders || licenseStatus.effective_status === "licensed") {
        return "license_status_suppression";
    }
    return "no_invalid_license_warning";
}
function invalidLicenseWarningDecision(options) {
    return {
        state: options.state,
        reason: options.reason,
        warningVisible: options.warningVisible,
        warningSuppressed: options.warningSuppressed,
        warningDiagnostic: options.warning,
        diagnostic: {
            warning_eligible: options.warning !== null,
            warning_visible: options.warningVisible,
            warning_suppressed: options.warningSuppressed,
            decision_reason: options.reason,
            write_suppressed_reason: options.writeSuppressedReason,
            command_completed_at: options.commandCompletedAt,
            human_readable_warning_eligible: options.humanReadableWarningEligible,
            last_invalid_license_warning_at: options.lastInvalidLicenseWarningAt,
            warning: options.warning
        }
    };
}
function invalidLicenseWarningStateUpdateResult(options) {
    return {
        state: options.state,
        statePath: options.resolvedPath.statePath,
        stateDirectory: options.resolvedPath.stateDirectory,
        stateDirectorySource: options.resolvedPath.stateDirectorySource,
        reason: options.decision.reason,
        stateWritten: options.stateWritten,
        warningVisible: options.decision.warningVisible,
        warningSuppressed: options.decision.warningSuppressed,
        warningDiagnostic: options.decision.warningDiagnostic,
        decision: options.decision,
        diagnostic: {
            state_path: options.resolvedPath.statePath,
            state_directory_source: options.resolvedPath.stateDirectorySource,
            read_status: options.readStatus,
            write_suppressed_reason: options.decision.diagnostic.write_suppressed_reason,
            state_written: options.stateWritten,
            last_invalid_license_warning_at: options.state?.lastInvalidLicenseWarningAt
                ?? options.decision.diagnostic.last_invalid_license_warning_at
        }
    };
}
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");
    }
}
function timestampMilliseconds(value) {
    return new Date(value).getTime();
}
function isUtcRfc3339Timestamp(value) {
    const match = UTC_RFC3339_PATTERN.exec(value);
    if (match === null) {
        return false;
    }
    const [, yearValue, monthValue, dayValue, hourValue, minuteValue, secondValue] = match;
    const year = Number(yearValue);
    const month = Number(monthValue);
    const day = Number(dayValue);
    const hour = Number(hourValue);
    const minute = Number(minuteValue);
    const second = Number(secondValue);
    if (month < 1 ||
        month > 12 ||
        hour > 23 ||
        minute > 59 ||
        second > 59) {
        return false;
    }
    const candidate = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
    return (candidate.getUTCFullYear() === year &&
        candidate.getUTCMonth() === month - 1 &&
        candidate.getUTCDate() === day &&
        candidate.getUTCHours() === hour &&
        candidate.getUTCMinutes() === minute &&
        candidate.getUTCSeconds() === second);
}
function recoveredReadResult(status, resolvedPath, diagnostic, newStateOptions, serializedSchemaVersion = null) {
    const state = createMissingReminderState(newStateOptions);
    return {
        status,
        state,
        statePath: resolvedPath.statePath,
        stateDirectory: resolvedPath.stateDirectory,
        stateDirectorySource: resolvedPath.stateDirectorySource,
        diagnostic,
        loadDiagnostic: createLoadDiagnostic({
            status: diagnosticStatus(status, diagnostic.reason),
            resolvedPath,
            state,
            diagnostic,
            schemaVersion: diagnostic.reason === "unsupported_schema_version"
                ? serializedSchemaVersion
                : null,
            seedSource: newStateOptions.prngSeedSource ?? "local-generated"
        })
    };
}
function readSerializedSchemaVersion(contents) {
    try {
        const parsed = JSON.parse(contents);
        if (isRecord(parsed) && typeof parsed.reminder_state_schema_version === "number") {
            return parsed.reminder_state_schema_version;
        }
    }
    catch {
        return null;
    }
    return null;
}
function createLoadDiagnostic(options) {
    return {
        status: options.status,
        state_path: options.resolvedPath.statePath,
        state_directory_source: options.resolvedPath.stateDirectorySource,
        schema_version: options.schemaVersion,
        recovery_reason: options.diagnostic?.reason ?? null,
        ...(options.diagnostic?.field === undefined ? {} : { recovery_field: options.diagnostic.field }),
        first_eligible_command_at: options.state.firstEligibleCommandAt,
        eligible_command_count: options.state.eligibleCommandCount,
        last_reminder_at: options.state.lastReminderAt,
        eligible_commands_since_last_reminder: options.state.eligibleCommandsSinceLastReminder,
        last_invalid_license_warning_at: options.state.lastInvalidLicenseWarningAt,
        seed_source: options.seedSource
    };
}
function diagnosticStatus(readStatus, reason) {
    if (readStatus === "initialized") {
        return "initialized-missing";
    }
    if (reason === "unreadable_state") {
        return "recovered-unreadable";
    }
    if (reason === "unsupported_schema_version") {
        return "recovered-unsupported-version";
    }
    return "recovered-malformed";
}
function writeFileAtomically(filePath, contents) {
    const directoryPath = node_path_1.default.dirname(filePath);
    node_fs_1.default.mkdirSync(directoryPath, { recursive: true });
    const tempFilePath = node_path_1.default.join(directoryPath, `.${node_path_1.default.basename(filePath)}.${process.pid}.${Date.now()}.${Math.random()
        .toString(16)
        .slice(2)}.tmp`);
    let fileDescriptor = null;
    try {
        fileDescriptor = node_fs_1.default.openSync(tempFilePath, "wx", 0o600);
        node_fs_1.default.writeFileSync(fileDescriptor, contents, "utf8");
        bestEffortFsync(fileDescriptor);
        node_fs_1.default.closeSync(fileDescriptor);
        fileDescriptor = null;
        node_fs_1.default.renameSync(tempFilePath, filePath);
        bestEffortDirectoryFsync(directoryPath);
    }
    catch (error) {
        if (fileDescriptor !== null) {
            try {
                node_fs_1.default.closeSync(fileDescriptor);
            }
            catch {
                // Ignore close failures while preserving the original write failure.
            }
        }
        try {
            node_fs_1.default.rmSync(tempFilePath, { force: true });
        }
        catch {
            // Temporary-file cleanup is best effort; the active state path is never the temp file.
        }
        throw error;
    }
}
function bestEffortFsync(fileDescriptor) {
    try {
        node_fs_1.default.fsyncSync(fileDescriptor);
    }
    catch {
        // Some filesystems do not support fsync for this target; writes still use rename replacement.
    }
}
function bestEffortDirectoryFsync(directoryPath) {
    let directoryDescriptor = null;
    try {
        directoryDescriptor = node_fs_1.default.openSync(directoryPath, "r");
        node_fs_1.default.fsyncSync(directoryDescriptor);
    }
    catch {
        // Directory fsync is not portable across all Node platforms and filesystems.
    }
    finally {
        if (directoryDescriptor !== null) {
            try {
                node_fs_1.default.closeSync(directoryDescriptor);
            }
            catch {
                // Directory sync cleanup is best effort.
            }
        }
    }
}
function isMissingFileError(error) {
    return isNodeError(error) && (error.code === "ENOENT" || error.code === "ENOTDIR");
}
function isNodeError(error) {
    return error instanceof Error && "code" in error;
}
function isPathInsideDirectory(candidatePath, directoryPath) {
    const relativePath = node_path_1.default.relative(node_path_1.default.resolve(directoryPath), node_path_1.default.resolve(candidatePath));
    return relativePath === "" || (relativePath !== ".." &&
        !relativePath.startsWith(`..${node_path_1.default.sep}`) &&
        !node_path_1.default.isAbsolute(relativePath));
}
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
