/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { act } from 'react';
import { renderHook } from '../../test-utils/render.js';
import { useInputHistory } from './useInputHistory.js';
describe('useInputHistory', () => {
    const mockOnSubmit = vi.fn();
    const mockOnChange = vi.fn();
    beforeEach(() => {
        vi.clearAllMocks();
    });
    const userMessages = ['message 1', 'message 2', 'message 3'];
    it('should initialize with historyIndex -1 and empty originalQueryBeforeNav', () => {
        const { result } = renderHook(() => useInputHistory({
            userMessages: [],
            onSubmit: mockOnSubmit,
            isActive: true,
            currentQuery: '',
            currentCursorOffset: 0,
            onChange: mockOnChange,
        }));
        // Internal state is not directly testable, but we can infer from behavior.
        // Attempting to navigate down should do nothing if historyIndex is -1.
        act(() => {
            result.current.navigateDown();
        });
        expect(mockOnChange).not.toHaveBeenCalled();
    });
    describe('handleSubmit', () => {
        it('should call onSubmit with trimmed value and reset history', () => {
            const { result } = renderHook(() => useInputHistory({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery: '  test query  ',
                currentCursorOffset: 0,
                onChange: mockOnChange,
            }));
            act(() => {
                result.current.handleSubmit('  submit value  ');
            });
            expect(mockOnSubmit).toHaveBeenCalledWith('submit value');
            // Check if history is reset (e.g., by trying to navigate down)
            act(() => {
                result.current.navigateDown();
            });
            expect(mockOnChange).not.toHaveBeenCalled();
        });
        it('should not call onSubmit if value is empty after trimming', () => {
            const { result } = renderHook(() => useInputHistory({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery: '',
                currentCursorOffset: 0,
                onChange: mockOnChange,
            }));
            act(() => {
                result.current.handleSubmit('   ');
            });
            expect(mockOnSubmit).not.toHaveBeenCalled();
        });
    });
    describe('navigateUp', () => {
        it('should not navigate if isActive is false', () => {
            const { result } = renderHook(() => useInputHistory({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: false,
                currentQuery: 'current',
                currentCursorOffset: 0,
                onChange: mockOnChange,
            }));
            act(() => {
                const navigated = result.current.navigateUp();
                expect(navigated).toBe(false);
            });
            expect(mockOnChange).not.toHaveBeenCalled();
        });
        it('should not navigate if userMessages is empty', () => {
            const { result } = renderHook(() => useInputHistory({
                userMessages: [],
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery: 'current',
                currentCursorOffset: 0,
                onChange: mockOnChange,
            }));
            act(() => {
                const navigated = result.current.navigateUp();
                expect(navigated).toBe(false);
            });
            expect(mockOnChange).not.toHaveBeenCalled();
        });
        it('should call onChange with the last message when navigating up from initial state', () => {
            const currentQuery = 'current query';
            const { result } = renderHook(() => useInputHistory({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery,
                currentCursorOffset: 0,
                onChange: mockOnChange,
            }));
            act(() => {
                result.current.navigateUp();
            });
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[2], 'start'); // Last message
        });
        it('should store currentQuery and currentCursorOffset as original state on first navigateUp', () => {
            const currentQuery = 'original user input';
            const currentCursorOffset = 5;
            const { result } = renderHook(() => useInputHistory({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery,
                currentCursorOffset,
                onChange: mockOnChange,
            }));
            act(() => {
                result.current.navigateUp(); // historyIndex becomes 0
            });
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[2], 'start');
            // Navigate down to restore original query and cursor position
            act(() => {
                result.current.navigateDown(); // historyIndex becomes -1
            });
            expect(mockOnChange).toHaveBeenCalledWith(currentQuery, currentCursorOffset);
        });
        it('should navigate through history messages on subsequent navigateUp calls', () => {
            const { result } = renderHook(() => useInputHistory({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery: '',
                currentCursorOffset: 0,
                onChange: mockOnChange,
            }));
            act(() => {
                result.current.navigateUp(); // Navigates to 'message 3'
            });
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[2], 'start');
            act(() => {
                result.current.navigateUp(); // Navigates to 'message 2'
            });
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[1], 'start');
            act(() => {
                result.current.navigateUp(); // Navigates to 'message 1'
            });
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[0], 'start');
        });
    });
    describe('navigateDown', () => {
        it('should not navigate if isActive is false', () => {
            const initialProps = {
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true, // Start active to allow setup navigation
                currentQuery: 'current',
                currentCursorOffset: 0,
                onChange: mockOnChange,
            };
            const { result, rerender } = renderHook((props) => useInputHistory(props), {
                initialProps,
            });
            // First navigate up to have something in history
            act(() => {
                result.current.navigateUp();
            });
            mockOnChange.mockClear(); // Clear calls from setup
            // Set isActive to false for the actual test
            rerender({ ...initialProps, isActive: false });
            act(() => {
                const navigated = result.current.navigateDown();
                expect(navigated).toBe(false);
            });
            expect(mockOnChange).not.toHaveBeenCalled();
        });
        it('should not navigate if historyIndex is -1 (not in history navigation)', () => {
            const { result } = renderHook(() => useInputHistory({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery: 'current',
                currentCursorOffset: 0,
                onChange: mockOnChange,
            }));
            act(() => {
                const navigated = result.current.navigateDown();
                expect(navigated).toBe(false);
            });
            expect(mockOnChange).not.toHaveBeenCalled();
        });
        it('should restore cursor offset only when in middle of compose prompt', () => {
            const originalQuery = 'my original input';
            const originalCursorOffset = 5; // Middle
            const { result } = renderHook(() => useInputHistory({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery: originalQuery,
                currentCursorOffset: originalCursorOffset,
                onChange: mockOnChange,
            }));
            act(() => {
                result.current.navigateUp();
            });
            mockOnChange.mockClear();
            act(() => {
                result.current.navigateDown();
            });
            // Should restore middle offset
            expect(mockOnChange).toHaveBeenCalledWith(originalQuery, originalCursorOffset);
        });
        it('should NOT restore cursor offset if it was at start or end of compose prompt', () => {
            const originalQuery = 'my original input';
            const { result, rerender } = renderHook((props) => useInputHistory(props), {
                initialProps: {
                    userMessages,
                    onSubmit: mockOnSubmit,
                    isActive: true,
                    currentQuery: originalQuery,
                    currentCursorOffset: 0, // Start
                    onChange: mockOnChange,
                },
            });
            // Case 1: Start
            act(() => {
                result.current.navigateUp();
            });
            mockOnChange.mockClear();
            act(() => {
                result.current.navigateDown();
            });
            // Should use 'end' default instead of 0
            expect(mockOnChange).toHaveBeenCalledWith(originalQuery, 'end');
            // Case 2: End
            rerender({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery: originalQuery,
                currentCursorOffset: originalQuery.length, // End
                onChange: mockOnChange,
            });
            act(() => {
                result.current.navigateUp();
            });
            mockOnChange.mockClear();
            act(() => {
                result.current.navigateDown();
            });
            // Should use 'end' default
            expect(mockOnChange).toHaveBeenCalledWith(originalQuery, 'end');
        });
        it('should remember text edits but use default cursor when navigating between history items', () => {
            const originalQuery = 'my original input';
            const originalCursorOffset = 5;
            const { result, rerender } = renderHook((props) => useInputHistory(props), {
                initialProps: {
                    userMessages,
                    onSubmit: mockOnSubmit,
                    isActive: true,
                    currentQuery: originalQuery,
                    currentCursorOffset: originalCursorOffset,
                    onChange: mockOnChange,
                },
            });
            // 1. Navigate UP from compose prompt (-1 -> 0)
            act(() => {
                result.current.navigateUp();
            });
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[2], 'start');
            mockOnChange.mockClear();
            // Simulate being at History[0] ('message 3') and editing it
            const editedHistoryText = 'message 3 edited';
            const editedHistoryOffset = 5;
            rerender({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery: editedHistoryText,
                currentCursorOffset: editedHistoryOffset,
                onChange: mockOnChange,
            });
            // 2. Navigate UP to next history item (0 -> 1)
            act(() => {
                result.current.navigateUp();
            });
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[1], 'start');
            mockOnChange.mockClear();
            // 3. Navigate DOWN back to History[0] (1 -> 0)
            act(() => {
                result.current.navigateDown();
            });
            // Should restore edited text AND the offset because we just came from History[0]
            expect(mockOnChange).toHaveBeenCalledWith(editedHistoryText, editedHistoryOffset);
            mockOnChange.mockClear();
            // Simulate being at History[0] (restored) and navigating DOWN to compose prompt (0 -> -1)
            rerender({
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery: editedHistoryText,
                currentCursorOffset: editedHistoryOffset,
                onChange: mockOnChange,
            });
            // 4. Navigate DOWN to compose prompt
            act(() => {
                result.current.navigateDown();
            });
            // Level -1 should ALWAYS restore its offset if it was in the middle
            expect(mockOnChange).toHaveBeenCalledWith(originalQuery, originalCursorOffset);
        });
        it('should restore offset for history items ONLY if returning from them immediately', () => {
            const originalQuery = 'my original input';
            const initialProps = {
                userMessages,
                onSubmit: mockOnSubmit,
                isActive: true,
                currentQuery: originalQuery,
                currentCursorOffset: 5,
                onChange: mockOnChange,
            };
            const { result, rerender } = renderHook((props) => useInputHistory(props), {
                initialProps,
            });
            // -1 -> 0 ('message 3')
            act(() => {
                result.current.navigateUp();
            });
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[2], 'start');
            const historyOffset = 4;
            // Manually update props to reflect current level
            rerender({
                ...initialProps,
                currentQuery: userMessages[2],
                currentCursorOffset: historyOffset,
            });
            // 0 -> 1 ('message 2')
            act(() => {
                result.current.navigateUp();
            });
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[1], 'start');
            rerender({
                ...initialProps,
                currentQuery: userMessages[1],
                currentCursorOffset: 0,
            });
            // 1 -> 2 ('message 1')
            act(() => {
                result.current.navigateUp();
            });
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[0], 'start');
            rerender({
                ...initialProps,
                currentQuery: userMessages[0],
                currentCursorOffset: 0,
            });
            mockOnChange.mockClear();
            // 2 -> 1 ('message 2')
            act(() => {
                result.current.navigateDown();
            });
            // 2 -> 1 is immediate back-and-forth.
            // But Level 1 offset was 0 (not in middle), so use 'end' default.
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[1], 'end');
            mockOnChange.mockClear();
            // Rerender to reflect Level 1 state
            rerender({
                ...initialProps,
                currentQuery: userMessages[1],
                currentCursorOffset: userMessages[1].length,
            });
            // 1 -> 0 ('message 3')
            act(() => {
                result.current.navigateDown();
            });
            // 1 -> 0 is NOT immediate (Level 2 was the last jump point).
            // So Level 0 SHOULD use default 'end' even though it has a middle offset saved.
            expect(mockOnChange).toHaveBeenCalledWith(userMessages[2], 'end');
        });
    });
});
//# sourceMappingURL=useInputHistory.test.js.map