"use strict";
/**
 * Gemini CLI tool implementation
 * Provides integration with Google's Gemini CLI in interactive mode
 *
 * @remarks Issue #368: Rewritten from non-interactive pipe mode to interactive REPL mode.
 * Previous implementation used `echo 'msg' | gemini` which caused the process to exit
 * immediately, making response polling impossible. Now launches `gemini` in interactive
 * mode within tmux (same approach as Claude/Codex).
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeminiTool = void 0;
const base_1 = require("./base");
const tmux_1 = require("../tmux/tmux");
const pasted_text_helper_1 = require("../pasted-text-helper");
const tmux_capture_cache_1 = require("../tmux/tmux-capture-cache");
const cli_patterns_1 = require("../detection/cli-patterns");
const logger_1 = require("../../lib/logger");
const logger = (0, logger_1.createLogger)('cli-tools/gemini');
/**
 * Extract error message from unknown error type (DRY)
 */
function getErrorMessage(error) {
    return error instanceof Error ? error.message : String(error);
}
/** Wait for Gemini CLI to initialize after launch (banner + auth + dialog) */
const GEMINI_INIT_WAIT_MS = 6000;
/** Interval for polling trust dialog / prompt detection */
const GEMINI_POLL_INTERVAL_MS = 1000;
/** Max attempts for initialization polling (30 * 1000ms = 30s total window) */
const GEMINI_INIT_MAX_ATTEMPTS = 30;
/** Timeout for waiting for prompt before sending a message */
const GEMINI_PROMPT_WAIT_TIMEOUT_MS = 15000;
/**
 * Gemini CLI tool implementation
 * Manages Gemini interactive sessions using tmux
 */
class GeminiTool extends base_1.BaseCLITool {
    id = 'gemini';
    name = 'Gemini CLI';
    command = 'gemini';
    /**
     * Check if Gemini session is running for a worktree
     *
     * @param worktreeId - Worktree ID
     * @returns True if session is running
     */
    async isRunning(worktreeId) {
        const sessionName = this.getSessionName(worktreeId);
        return await (0, tmux_1.hasSession)(sessionName);
    }
    /**
     * Start a new Gemini session for a worktree
     * Launches `gemini` in interactive REPL mode within tmux
     *
     * @param worktreeId - Worktree ID
     * @param worktreePath - Worktree path
     */
    async startSession(worktreeId, worktreePath) {
        // Check if Gemini is installed
        const geminiAvailable = await this.isInstalled();
        if (!geminiAvailable) {
            throw new Error('Gemini CLI is not installed or not in PATH');
        }
        const sessionName = this.getSessionName(worktreeId);
        // Check if session already exists
        const exists = await (0, tmux_1.hasSession)(sessionName);
        if (exists) {
            logger.info('gemini-session-sessionname');
            return;
        }
        try {
            // Create tmux session with large history buffer
            await (0, tmux_1.createSession)({
                sessionName,
                workingDirectory: worktreePath,
                historyLimit: 50000,
            });
            // Wait a moment for the session to be created
            await new Promise((resolve) => setTimeout(resolve, 100));
            // Start Gemini CLI in interactive mode (no flags = interactive REPL)
            await (0, tmux_1.sendKeys)(sessionName, 'gemini', true);
            // Wait for Gemini to initialize (minimum wait for banner/auth)
            await new Promise((resolve) => setTimeout(resolve, GEMINI_INIT_WAIT_MS));
            // Poll until Gemini interactive prompt is ready
            // Handles trust dialog automatically if encountered
            await this.waitForReady(sessionName);
            logger.info('started-gemini-session:sessionname');
        }
        catch (error) {
            const errorMessage = getErrorMessage(error);
            throw new Error(`Failed to start Gemini session: ${errorMessage}`);
        }
    }
    /**
     * Wait for Gemini CLI to become ready (prompt visible).
     * Handles trust dialog automatically if encountered.
     * Polls until GEMINI_PROMPT_PATTERN is detected or max attempts reached.
     */
    async waitForReady(sessionName) {
        let trustDialogHandled = false;
        for (let i = 0; i < GEMINI_INIT_MAX_ATTEMPTS; i++) {
            try {
                const rawOutput = await (0, tmux_1.capturePane)(sessionName, 50);
                // Strip ANSI escape codes before pattern matching
                // (Gemini TUI uses 24-bit color codes that break regex matching)
                const output = (0, cli_patterns_1.stripAnsi)(rawOutput);
                // Check if interactive prompt is ready
                if (cli_patterns_1.GEMINI_PROMPT_PATTERN.test(output)) {
                    logger.info('gemini-prompt-detected');
                    return;
                }
                // Handle trust dialog if not yet handled
                if (!trustDialogHandled && output.includes('Do you trust this folder?')) {
                    await (0, tmux_1.sendSpecialKey)(sessionName, 'Enter');
                    trustDialogHandled = true;
                    logger.info('auto-trusted-folder-for');
                    // Continue polling for prompt after trust dialog
                }
            }
            catch {
                // Capture may fail during initialization - continue polling
            }
            await new Promise((resolve) => setTimeout(resolve, GEMINI_POLL_INTERVAL_MS));
        }
        logger.info('gemini-prompt-detection');
    }
    /**
     * Wait for Gemini prompt before sending a message.
     * Used by sendMessage to ensure Gemini is ready to accept input.
     */
    async waitForPrompt(sessionName) {
        const startTime = Date.now();
        const pollInterval = 500;
        while (Date.now() - startTime < GEMINI_PROMPT_WAIT_TIMEOUT_MS) {
            try {
                const rawOutput = await (0, tmux_1.capturePane)(sessionName, 50);
                const output = (0, cli_patterns_1.stripAnsi)(rawOutput);
                if (cli_patterns_1.GEMINI_PROMPT_PATTERN.test(output)) {
                    return;
                }
            }
            catch {
                // Capture may fail - continue polling
            }
            await new Promise((resolve) => setTimeout(resolve, pollInterval));
        }
        logger.info('gemini-prompt-not');
    }
    /**
     * Send a message to Gemini interactive session
     *
     * @param worktreeId - Worktree ID
     * @param message - Message to send
     */
    async sendMessage(worktreeId, message) {
        const sessionName = this.getSessionName(worktreeId);
        // Check if session exists
        const exists = await (0, tmux_1.hasSession)(sessionName);
        if (!exists) {
            throw new Error(`Gemini session ${sessionName} does not exist. Start the session first.`);
        }
        try {
            // Verify Gemini is at prompt state before sending
            await this.waitForPrompt(sessionName);
            // Send message to Gemini (without Enter)
            await (0, tmux_1.sendKeys)(sessionName, message, false);
            // Wait a moment for the text to be typed
            await new Promise((resolve) => setTimeout(resolve, 100));
            // Send Enter key separately
            await (0, tmux_1.sendSpecialKey)(sessionName, 'C-m');
            // Wait a moment for the message to be processed
            await new Promise((resolve) => setTimeout(resolve, 200));
            // Detect [Pasted text] and resend Enter for multi-line messages
            if (message.includes('\n')) {
                await (0, pasted_text_helper_1.detectAndResendIfPastedText)(sessionName);
            }
            // Issue #405: Invalidate cache after sending message
            (0, tmux_capture_cache_1.invalidateCache)(sessionName);
            logger.info('sent-message-to-gemini-session:sessionna');
        }
        catch (error) {
            const errorMessage = getErrorMessage(error);
            throw new Error(`Failed to send message to Gemini: ${errorMessage}`);
        }
    }
    /**
     * Kill Gemini session
     *
     * @param worktreeId - Worktree ID
     */
    async killSession(worktreeId) {
        const sessionName = this.getSessionName(worktreeId);
        try {
            const exists = await (0, tmux_1.hasSession)(sessionName);
            if (exists) {
                // Send Ctrl+C to interrupt any running operation
                await (0, tmux_1.sendSpecialKey)(sessionName, 'C-c');
                await new Promise((resolve) => setTimeout(resolve, 300));
                // Send /quit to exit Gemini gracefully
                await (0, tmux_1.sendKeys)(sessionName, '/quit', true);
                await new Promise((resolve) => setTimeout(resolve, 500));
            }
            // Kill the tmux session
            const killed = await (0, tmux_1.killSession)(sessionName);
            if (killed) {
                logger.info('stopped-gemini-session:sessionname');
            }
        }
        catch (error) {
            logger.error('session:stop-failed', { error: getErrorMessage(error) });
            throw error;
        }
    }
}
exports.GeminiTool = GeminiTool;
