import { describe, it, expect, vi } from 'vitest';
import { formatPrompt, parseCommand } from './minimal.js';
import type { ModelConfig } from '../../config/schema.js';
import type { ManagedSession } from '../../session/index.js';
import type { ModelClient } from '../../models/types.js';
import type { ModelRouter } from '../../models/router.js';
import type { NativeAgent } from '../../backends/native/agent.js';
import { MinimalTui } from './minimal.js';

type TuiRouterStub = Pick<ModelRouter, 'getTier' | 'getAvailableTiers' | 'setTier' | 'getLabel'> &
  Partial<ModelRouter> &
  Partial<ModelClient> & {
    getLocalProviderName: () => string | undefined;
    setLocalClient: ReturnType<typeof vi.fn>;
  };

function asSession(value: unknown): ManagedSession {
  return value as ManagedSession;
}

function asRouter(value: unknown): ModelClient & ModelRouter {
  return value as ModelClient & ModelRouter;
}

function asModelClient(value: unknown): ModelClient {
  return value as ModelClient;
}

function asAgent(value: unknown): NativeAgent {
  return value as NativeAgent;
}

function minimalTuiPrivates(value: MinimalTui): {
  handleBackendCommand: (provider: string) => Promise<void>;
  handleModelCommand: (tier: string, providerModel?: string) => void;
  handleContextCommand: () => void;
  handleVerboseCommand: () => void;
  handleToolEvent: (event: unknown) => void;
  handleCommand: (command: unknown) => Promise<void>;
  handleMessage: (content: string) => Promise<void>;
  handleEscapeAction: () => boolean;
  handleCtrlCPress: (nowMs?: number) => boolean;
  clearSubmittedPromptLine: () => boolean;
  prompt: (text: string) => Promise<string>;
  rl: {
    once: (event: string, cb: () => void) => void;
    removeListener: (event: string, cb: () => void) => void;
    question: (text: string, cb: (answer: string) => void) => void;
    write: (data: string | null, key?: { ctrl?: boolean; name?: string }) => void;
    prompt: () => void;
  };
  activePromptCancel: (() => void) | null;
  activeOperationCancel: (() => void) | null;
  commandInFlight: boolean;
  running: boolean;
} {
  return value as unknown as {
    handleBackendCommand: (provider: string) => Promise<void>;
    handleModelCommand: (tier: string, providerModel?: string) => void;
    handleContextCommand: () => void;
    handleVerboseCommand: () => void;
    handleToolEvent: (event: unknown) => void;
    handleCommand: (command: unknown) => Promise<void>;
    handleMessage: (content: string) => Promise<void>;
    handleEscapeAction: () => boolean;
    handleCtrlCPress: (nowMs?: number) => boolean;
    clearSubmittedPromptLine: () => boolean;
    prompt: (text: string) => Promise<string>;
    rl: {
      once: (event: string, cb: () => void) => void;
      removeListener: (event: string, cb: () => void) => void;
      question: (text: string, cb: (answer: string) => void) => void;
      write: (data: string | null, key?: { ctrl?: boolean; name?: string }) => void;
      prompt: () => void;
    };
    activePromptCancel: (() => void) | null;
    activeOperationCancel: (() => void) | null;
    commandInFlight: boolean;
    running: boolean;
  };
}

describe('formatPrompt', () => {
  it('formats default prompt', () => {
    const prompt = formatPrompt('default');
    expect(prompt).toContain('flynn>');
  });

  it('formats thinking prompt', () => {
    const prompt = formatPrompt('thinking');
    expect(prompt).toContain('flynn...');
  });
});

describe('parseCommand (re-exported)', () => {
  it('parses /quit command', () => {
    const result = parseCommand('/quit');
    expect(result).toEqual({ type: 'quit' });
  });

  it('parses /model command', () => {
    const result = parseCommand('/model local');
    expect(result).toEqual({ type: 'model', name: 'local' });
  });

  it('parses regular message', () => {
    const result = parseCommand('Hello, Flynn!');
    expect(result).toEqual({ type: 'message', content: 'Hello, Flynn!' });
  });

  it('returns null for empty input', () => {
    const result = parseCommand('');
    expect(result).toBeNull();
  });
});

describe('MinimalTui backend command', () => {
  it('switches local backend when provider is configured', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };

    const mockRouter: TuiRouterStub = {
      getTier: () => 'default' as const,
      getAvailableTiers: () => ['default', 'local'],
      setTier: vi.fn(() => true),
      getLabel: (tier: string) => tier,
      getLocalProviderName: () => 'ollama',
      setLocalClient: vi.fn(),
      chat: vi.fn(),
      getClient: vi.fn(),
    };

    const localProviders: Record<string, ModelConfig> = {
      llamacpp: {
        provider: 'llamacpp',
        model: '',
        endpoint: 'http://localhost:8080',
      },
    };

    const tui = new MinimalTui({
      session: asSession(mockSession),
      modelClient: asRouter(mockRouter),
      modelRouter: asRouter(mockRouter),
      systemPrompt: 'test',
      localProviders,
    });

    // Access private method for testing
    await minimalTuiPrivates(tui).handleBackendCommand('llamacpp');

    expect(mockRouter.setLocalClient).toHaveBeenCalled();
  });

  it('syncs agent tier when /model command switches tier', () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };

    const mockRouter: TuiRouterStub = {
      getTier: () => 'default' as const,
      getAvailableTiers: () => ['default', 'local'],
      setTier: vi.fn(() => true),
      getLabel: (tier: string) => tier,
      getLocalProviderName: () => 'ollama',
      setLocalClient: vi.fn(),
      chat: vi.fn(),
      getClient: vi.fn(),
    };

    const mockAgent = {
      setModelTier: vi.fn(),
      getModelTier: vi.fn(() => 'default'),
      process: vi.fn(),
    };

    const tui = new MinimalTui({
      session: asSession(mockSession),
      modelClient: asRouter(mockRouter),
      modelRouter: asRouter(mockRouter),
      agent: asAgent(mockAgent),
      systemPrompt: 'test',
    });

    // Call private handleModelCommand to switch to local
    minimalTuiPrivates(tui).handleModelCommand('local');

    expect(mockRouter.setTier).toHaveBeenCalledWith('local');
    expect(mockAgent.setModelTier).toHaveBeenCalledWith('local');
  });

  it('uses configured compaction threshold in /context output', () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [{ role: 'user', content: 'x'.repeat(400) }],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };

    const mockRouter: TuiRouterStub = {
      getTier: () => 'default' as const,
      getAvailableTiers: () => ['default'],
      setTier: vi.fn(() => true),
      getLabel: () => 'gpt-4o',
      getLocalProviderName: () => 'ollama',
      setLocalClient: vi.fn(),
      chat: vi.fn(),
      getClient: vi.fn(),
    };

    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    try {
      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asRouter(mockRouter),
        modelRouter: asRouter(mockRouter),
        systemPrompt: 'test',
        contextThresholdPct: 67,
      });

      minimalTuiPrivates(tui).handleContextCommand();
      expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('compaction threshold: 67%'));
    } finally {
      logSpy.mockRestore();
    }
  });

  it('reuses configured provider credentials for /model <tier> <provider/model>', () => {
    const prevOpenRouterKey = process.env.OPENROUTER_API_KEY;
    delete process.env.OPENROUTER_API_KEY;

    try {
      const mockSession = {
        id: 'test',
        getHistory: () => [],
        addMessage: vi.fn(),
        clear: vi.fn(),
        replaceHistory: vi.fn(),
      };

      const mockRouter: TuiRouterStub = {
        getTier: () => 'default' as const,
        getAvailableTiers: () => ['default', 'local'],
        setTier: vi.fn(() => true),
        getLabel: (tier: string) => tier,
        getLocalProviderName: () => 'ollama',
        setLocalClient: vi.fn(),
        setClient: vi.fn(),
        setTierStrict: vi.fn(),
        chat: vi.fn(),
        getClient: vi.fn(),
      };

      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asRouter(mockRouter),
        modelRouter: asRouter(mockRouter),
        systemPrompt: 'test',
        modelProviderConfigs: {
          openrouter: {
            provider: 'openrouter',
            model: 'seed-model',
            api_key: 'test-key',
            endpoint: 'https://openrouter.ai/api/v1',
          },
        },
      });

      minimalTuiPrivates(tui).handleModelCommand('default', 'openrouter/deepseek/deepseek-chat');

      expect(mockRouter.setClient).toHaveBeenCalledOnce();
      expect(mockRouter.setTierStrict).toHaveBeenCalledWith('default', true);
      expect(mockRouter.setTier).toHaveBeenCalledWith('default');
    } finally {
      if (prevOpenRouterKey) {
        process.env.OPENROUTER_API_KEY = prevOpenRouterKey;
      } else {
        delete process.env.OPENROUTER_API_KEY;
      }
    }
  });

  it('switches active tier and syncs agent for /model <tier> <provider/model>', () => {
    const prevOpenRouterKey = process.env.OPENROUTER_API_KEY;
    delete process.env.OPENROUTER_API_KEY;

    try {
      const mockSession = {
        id: 'test',
        getHistory: () => [],
        addMessage: vi.fn(),
        clear: vi.fn(),
        replaceHistory: vi.fn(),
      };

      const mockRouter: TuiRouterStub = {
        getTier: () => 'fast' as const,
        getAvailableTiers: () => ['default', 'fast', 'local'],
        setTier: vi.fn(() => true),
        getLabel: (tier: string) => tier,
        getLocalProviderName: () => 'ollama',
        setLocalClient: vi.fn(),
        setClient: vi.fn(),
        setTierStrict: vi.fn(),
        chat: vi.fn(),
        getClient: vi.fn(),
      };

      const mockAgent = {
        setModelTier: vi.fn(),
        getModelTier: vi.fn(() => 'fast'),
        process: vi.fn(),
      };

      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asRouter(mockRouter),
        modelRouter: asRouter(mockRouter),
        agent: asAgent(mockAgent),
        systemPrompt: 'test',
        modelProviderConfigs: {
          openrouter: {
            provider: 'openrouter',
            model: 'seed-model',
            api_key: 'test-key',
            endpoint: 'https://openrouter.ai/api/v1',
          },
        },
      });

      minimalTuiPrivates(tui).handleModelCommand('default', 'openrouter/deepseek/deepseek-chat');

      expect(mockRouter.setTier).toHaveBeenCalledWith('default');
      expect(mockAgent.setModelTier).toHaveBeenCalledWith('default');
    } finally {
      if (prevOpenRouterKey) {
        process.env.OPENROUTER_API_KEY = prevOpenRouterKey;
      } else {
        delete process.env.OPENROUTER_API_KEY;
      }
    }
  });

  it('prints transfer result text when /transfer is invoked', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };
    const onTransfer = vi.fn(() => 'Session transferred to Telegram (12345)');
    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    try {
      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asModelClient({}),
        systemPrompt: 'test',
        onTransfer,
      });

      await minimalTuiPrivates(tui).handleCommand({ type: 'transfer', target: 'telegram' });
      expect(onTransfer).toHaveBeenCalledWith('telegram');
      expect(logSpy).toHaveBeenCalledWith('Session transferred to Telegram (12345)\n');
    } finally {
      logSpy.mockRestore();
    }
  });

  it('clears local session and resets agent for /reset when an agent is configured', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };
    const mockAgent = {
      reset: vi.fn(),
      setModelTier: vi.fn(),
      getModelTier: vi.fn(() => 'default'),
      process: vi.fn(),
    };

    const tui = new MinimalTui({
      session: asSession(mockSession),
      modelClient: asModelClient({}),
      systemPrompt: 'test',
      agent: asAgent(mockAgent),
    });

    await minimalTuiPrivates(tui).handleCommand({ type: 'reset' });

    expect(mockSession.clear).toHaveBeenCalledOnce();
    expect(mockAgent.reset).toHaveBeenCalledOnce();
  });

  it('prints tools output when /tools is invoked', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };
    const onTools = vi.fn(() => 'Available tools (2):\n- file.read\n- council.run');
    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    try {
      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asModelClient({}),
        systemPrompt: 'test',
        onTools,
      });

      await minimalTuiPrivates(tui).handleCommand({ type: 'tools' });
      expect(onTools).toHaveBeenCalledOnce();
      expect(logSpy).toHaveBeenCalledWith('Available tools (2):\n- file.read\n- council.run\n');
    } finally {
      logSpy.mockRestore();
    }
  });

  it('forwards /runtime command through runtime command callback', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };
    const onRuntimeCommand = vi.fn(async () => 'Backend mode: config_default');
    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    try {
      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asModelClient({}),
        systemPrompt: 'test',
        onRuntimeCommand,
      });

      await minimalTuiPrivates(tui).handleCommand({ type: 'runtime', input: 'status' });
      expect(onRuntimeCommand).toHaveBeenCalledWith('status');
      expect(logSpy).toHaveBeenCalledWith('Backend mode: config_default\n');
    } finally {
      logSpy.mockRestore();
    }
  });

  it('prints guidance when runtime command service is unavailable', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };
    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    try {
      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asModelClient({}),
        systemPrompt: 'test',
      });

      await minimalTuiPrivates(tui).handleCommand({ type: 'runtime', input: 'status' });
      expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Runtime backend mode command service is unavailable in this TUI session.'));
    } finally {
      logSpy.mockRestore();
    }
  });

  it('keeps /backend status local-only and does not invoke runtime command callback', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };
    const onRuntimeCommand = vi.fn(async () => 'should not be called');
    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    try {
      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asModelClient({}),
        systemPrompt: 'test',
        onRuntimeCommand,
      });

      await minimalTuiPrivates(tui).handleCommand({ type: 'backend', provider: 'status' });
      expect(onRuntimeCommand).not.toHaveBeenCalled();
      expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Backend switching not available.'));
    } finally {
      logSpy.mockRestore();
    }
  });

  it('collects multiline input from /paste and sends as one message', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };
    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    try {
      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asModelClient({}),
        systemPrompt: 'test',
      });

      const promptSpy = vi.fn()
        .mockResolvedValueOnce('first line')
        .mockResolvedValueOnce('second line')
        .mockResolvedValueOnce('.');
      minimalTuiPrivates(tui).prompt = promptSpy;

      const handleMessageSpy = vi.fn(async () => {});
      minimalTuiPrivates(tui).handleMessage = handleMessageSpy;
      minimalTuiPrivates(tui).running = true;
      minimalTuiPrivates(tui).rl = {
        once: vi.fn(),
        removeListener: vi.fn(),
        question: vi.fn(),
        write: vi.fn(),
        prompt: vi.fn(),
      };

      await minimalTuiPrivates(tui).handleCommand({ type: 'multiline' });

      expect(handleMessageSpy).toHaveBeenCalledWith('first line\nsecond line');
      expect(promptSpy).toHaveBeenCalledTimes(3);
    } finally {
      logSpy.mockRestore();
    }
  });

  it('only renders tool activity when verbose mode is enabled', () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };
    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    try {
      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asModelClient({}),
        systemPrompt: 'test',
      });

      minimalTuiPrivates(tui).handleToolEvent({ type: 'start', tool: 'shell.exec', args: { command: 'ls' } });
      expect(logSpy).not.toHaveBeenCalled();

      minimalTuiPrivates(tui).handleVerboseCommand();
      minimalTuiPrivates(tui).handleToolEvent({ type: 'start', tool: 'shell.exec', args: { command: 'ls' } });
      expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Shell: Exec'));
    } finally {
      logSpy.mockRestore();
    }
  });
});

describe('MinimalTui prompt cancellation', () => {
  it('omits leading newline when submitted prompt line was cleared', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };

    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
    try {
      const mockAgent = {
        process: vi.fn(async () => 'ok'),
      };

      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asRouter({}),
        agent: asAgent(mockAgent),
        systemPrompt: 'test',
      });

      const clearSpy = vi.fn(() => true);
      minimalTuiPrivates(tui).clearSubmittedPromptLine = clearSpy;

      await minimalTuiPrivates(tui).handleCommand({ type: 'message', content: 'hello' });

      expect(clearSpy).toHaveBeenCalledOnce();
      const userHeader = writeSpy.mock.calls
        .map(([chunk]) => String(chunk))
        .find((chunk) => chunk.includes('You'));
      expect(userHeader).toBeDefined();
      expect(userHeader?.startsWith('\n')).toBe(false);
    } finally {
      writeSpy.mockRestore();
      logSpy.mockRestore();
    }
  });

  it('cancels an active prompt without closing the TUI', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };

    const tui = new MinimalTui({
      session: asSession(mockSession),
      modelClient: asRouter({}),
      systemPrompt: 'test',
    });

    let onAnswer: ((answer: string) => void) | undefined;
    const write = vi.fn((_: string | null, key?: { ctrl?: boolean; name?: string }) => {
      if (key?.name === 'return') {
        onAnswer?.('');
      }
    });

    minimalTuiPrivates(tui).rl = {
      once: vi.fn(),
      removeListener: vi.fn(),
      question: vi.fn((_text: string, cb: (answer: string) => void) => {
        onAnswer = cb;
      }),
      write,
      prompt: vi.fn(),
    };

    const promptPromise = minimalTuiPrivates(tui).prompt('Confirm? ');
    expect(minimalTuiPrivates(tui).activePromptCancel).toBeTypeOf('function');

    minimalTuiPrivates(tui).activePromptCancel?.();

    await expect(promptPromise).resolves.toBe('');
    expect(write).toHaveBeenCalledWith(null, { ctrl: true, name: 'u' });
    expect(write).toHaveBeenCalledWith(null, { name: 'return' });
    expect(minimalTuiPrivates(tui).activePromptCancel).toBeNull();
  });

  it('returns empty string when readline is already closed during question', async () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };

    const tui = new MinimalTui({
      session: asSession(mockSession),
      modelClient: asRouter({}),
      systemPrompt: 'test',
    });

    const questionError = new Error('readline was closed');
    (questionError as Error & { code?: string }).code = 'ERR_USE_AFTER_CLOSE';

    minimalTuiPrivates(tui).rl = {
      once: vi.fn(),
      removeListener: vi.fn(),
      question: vi.fn(() => {
        throw questionError;
      }),
      write: vi.fn(),
      prompt: vi.fn(),
    };

    await expect(minimalTuiPrivates(tui).prompt('Confirm? ')).resolves.toBe('');
    expect(minimalTuiPrivates(tui).activePromptCancel).toBeNull();
  });

  it('uses Esc to cancel active running operation', () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };

    const tui = new MinimalTui({
      session: asSession(mockSession),
      modelClient: asRouter({}),
      systemPrompt: 'test',
    });

    const cancelRunningOperation = vi.fn();
    minimalTuiPrivates(tui).activeOperationCancel = cancelRunningOperation;

    expect(minimalTuiPrivates(tui).handleEscapeAction()).toBe(true);
    expect(cancelRunningOperation).toHaveBeenCalledOnce();
  });

  it('treats first Ctrl+C as clear-input and second as exit intent', () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };

    const write = vi.fn();
    const prompt = vi.fn();
    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    try {
      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asRouter({}),
        systemPrompt: 'test',
      });
      minimalTuiPrivates(tui).rl = {
        once: vi.fn(),
        removeListener: vi.fn(),
        question: vi.fn(),
        write,
        prompt,
      };
      minimalTuiPrivates(tui).running = true;

      const first = minimalTuiPrivates(tui).handleCtrlCPress(1000);
      const second = minimalTuiPrivates(tui).handleCtrlCPress(2000);

      expect(first).toBe(false);
      expect(second).toBe(true);
      expect(write).toHaveBeenCalledWith(null, { ctrl: true, name: 'u' });
      expect(prompt).toHaveBeenCalled();
      expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Press Ctrl+C again to quit'));
    } finally {
      logSpy.mockRestore();
    }
  });

  it('exits immediately on Ctrl+C when a command is in flight', () => {
    const mockSession = {
      id: 'test',
      getHistory: () => [],
      addMessage: vi.fn(),
      clear: vi.fn(),
      replaceHistory: vi.fn(),
    };

    const write = vi.fn();
    const prompt = vi.fn();
    const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
    try {
      const tui = new MinimalTui({
        session: asSession(mockSession),
        modelClient: asRouter({}),
        systemPrompt: 'test',
      });
      minimalTuiPrivates(tui).rl = {
        once: vi.fn(),
        removeListener: vi.fn(),
        question: vi.fn(),
        write,
        prompt,
      };
      minimalTuiPrivates(tui).running = true;
      minimalTuiPrivates(tui).commandInFlight = true;
      const cancel = vi.fn();
      minimalTuiPrivates(tui).activeOperationCancel = cancel;

      const shouldExit = minimalTuiPrivates(tui).handleCtrlCPress(1000);

      expect(shouldExit).toBe(true);
      expect(cancel).toHaveBeenCalledOnce();
      expect(write).not.toHaveBeenCalled();
      expect(prompt).not.toHaveBeenCalled();
      expect(logSpy).not.toHaveBeenCalledWith(expect.stringContaining('Press Ctrl+C again to quit'));
    } finally {
      logSpy.mockRestore();
    }
  });
});
