/**
 * @license
 * Copyright 2025 Vybestack LLC
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * Manages raw mode lifecycle with automatic error handling
 */
export class StdinRawModeManager {
    options;
    errorHandler;
    isEnabled = false;
    wasRawBeforeEnable = false;
    debugModeEnabled = false;
    constructor(options) {
        this.options = options;
        this.errorHandler = this.createErrorHandler();
        this.debugModeEnabled = this.options?.debug ?? false;
    }
    /**
     * Create an error handler for stdin I/O errors
     */
    createErrorHandler() {
        const handler = (err) => {
            // Log the error in debug mode
            if (this.debugModeEnabled) {
                console.error('[stdin] I/O error (non-fatal):', err);
            }
            // Allow custom error handling
            if (this.options?.onError) {
                this.options.onError(err);
            }
            // Attempt to resume stdin if it was paused
            // This is a best-effort recovery attempt
            if (process.stdin.isPaused()) {
                try {
                    process.stdin.resume();
                }
                catch (resumeErr) {
                    // Ignore resume failures - we've already logged the original error
                    if (this.debugModeEnabled) {
                        console.error('[stdin] Failed to resume after error:', resumeErr);
                    }
                }
            }
            // The key: do NOT re-throw, preventing the process from crashing
            // This allows the app to continue operating or gracefully shut down
        };
        // Give the handler a name for easier debugging/removal
        Object.defineProperty(handler, 'name', {
            value: 'stdinErrorHandler',
            configurable: false,
            writable: false,
        });
        return handler;
    }
    /**
     * Enable raw mode with error handling
     *
     * @returns true if raw mode was enabled, false if it was already in raw mode
     */
    enable() {
        // Check if stdin is a TTY before setting raw mode
        if (!process.stdin.isTTY) {
            return false;
        }
        // Store the current raw state before enabling
        this.wasRawBeforeEnable = !!process.stdin.isRaw;
        // If already in raw mode and we didn't set it, don't change anything
        if (process.stdin.isRaw && !this.isEnabled) {
            // We're inheriting raw mode from somewhere else
            // Still install error handler for safety
            if (!this.hasErrorHandler()) {
                process.stdin.on('error', this.errorHandler);
                this.isEnabled = true;
            }
            return false;
        }
        // Set raw mode
        try {
            process.stdin.setRawMode(true);
        }
        catch (err) {
            // setRawMode can fail in some terminal configs
            // Log and continue anyway
            if (this.debugModeEnabled) {
                console.error('[stdin] Failed to set raw mode:', err);
            }
            return false;
        }
        // Install error handler if not already present
        if (!this.hasErrorHandler()) {
            process.stdin.on('error', this.errorHandler);
        }
        this.isEnabled = true;
        return true;
    }
    /**
     * Disable raw mode and clean up error handler
     *
     * @param restorePreviousState If true, restore stdin to the state it was in before enable() was called
     *                              If false, force disable raw mode
     */
    disable(restorePreviousState = true) {
        if (!this.isEnabled) {
            return;
        }
        // Remove our error handler
        const listeners = process.stdin.listeners('error');
        for (const listener of listeners) {
            if (listener === this.errorHandler) {
                process.stdin.removeListener('error', listener);
                break;
            }
        }
        // Restore raw mode state
        if (process.stdin.isTTY) {
            try {
                if (restorePreviousState) {
                    process.stdin.setRawMode(this.wasRawBeforeEnable);
                }
                else {
                    process.stdin.setRawMode(false);
                }
            }
            catch (_err) {
                // setRawMode can fail during cleanup
                // Ignore and continue
            }
        }
        this.isEnabled = false;
    }
    /**
     * Check if stdin already has our error handler installed
     */
    hasErrorHandler() {
        const listeners = process.stdin.listeners('error');
        return listeners.includes(this.errorHandler);
    }
    /**
     * Check if raw mode is currently managed by this instance
     */
    getManaged() {
        return this.isEnabled;
    }
    /**
     * Get the error handler function (useful for manual cleanup or testing)
     */
    getErrorHandler() {
        return this.errorHandler;
    }
}
// Global singleton handler for installStdinErrorHandler
let globalStdinErrorHandler = null;
/**
 * Legacy helper function for backward compatibility
 * Installs a global stdin error handler without managing raw mode
 *
 * @deprecated Use StdinRawModeManager instead for better lifecycle management
 *
 * @param options - Error handling options (only used on first call)
 * @returns The error handler function (for removal later)
 */
export function installStdinErrorHandler(options) {
    // Return existing handler if already installed (singleton pattern)
    if (globalStdinErrorHandler) {
        return globalStdinErrorHandler;
    }
    // Create and install the handler
    const manager = new StdinRawModeManager(options);
    const handler = manager.getErrorHandler();
    // Only install the handler if it's not already present
    if (!process.stdin.listeners('error').includes(handler)) {
        process.stdin.on('error', handler);
    }
    // Store as singleton for idempotency
    globalStdinErrorHandler = handler;
    return handler;
}
/**
 * Reset the global stdin error handler (for testing only)
 * @internal
 */
export function _resetGlobalStdinErrorHandler() {
    if (globalStdinErrorHandler) {
        // Remove all instances of this handler from the listeners
        try {
            process.stdin.removeListener('error', globalStdinErrorHandler);
        }
        catch (_err) {
            // Ignore if handler not found
        }
        globalStdinErrorHandler = null;
    }
}
/**
 * Helper to safely enable raw mode with error handling
 * Returns a cleanup function that should be called to restore the original state
 *
 * Note: If stdin is not a TTY or raw mode cannot be enabled, the wrapped
 * function still executes but no raw mode changes are made.
 *
 * @param options - Error handling options
 * @returns Cleanup function that restores stdin to its previous state
 */
export function withSafeRawMode(fn, options) {
    return () => {
        const manager = new StdinRawModeManager(options);
        const enabled = manager.enable();
        try {
            const result = fn();
            // Handle both sync and async functions
            if (result instanceof Promise) {
                return result.finally(() => {
                    if (enabled) {
                        manager.disable();
                    }
                });
            }
            else {
                if (enabled) {
                    manager.disable();
                }
                return result;
            }
        }
        catch (err) {
            if (enabled) {
                manager.disable();
            }
            throw err;
        }
    };
}
//# sourceMappingURL=stdinSafety.js.map