/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { ChatRecordingService } from '@google/gemini-cli-core';
import { listSessions, deleteSession } from './sessions.js';
import { SessionSelector } from './sessionUtils.js';
// Mock the SessionSelector and ChatRecordingService
vi.mock('./sessionUtils.js', () => ({
    SessionSelector: vi.fn(),
    formatRelativeTime: vi.fn(() => 'some time ago'),
}));
vi.mock('@google/gemini-cli-core', async () => {
    const actual = await vi.importActual('@google/gemini-cli-core');
    return {
        ...actual,
        ChatRecordingService: vi.fn(),
    };
});
describe('listSessions', () => {
    let mockConfig;
    let mockListSessions;
    let consoleLogSpy;
    beforeEach(() => {
        // Create mock config
        mockConfig = {
            storage: {
                getProjectTempDir: vi.fn().mockReturnValue('/tmp/test-project'),
            },
            getSessionId: vi.fn().mockReturnValue('current-session-id'),
        };
        // Create mock listSessions method
        mockListSessions = vi.fn();
        // Mock SessionSelector constructor to return object with listSessions method
        vi.mocked(SessionSelector).mockImplementation(() => ({
            listSessions: mockListSessions,
        }));
        // Spy on console.log
        consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
    });
    afterEach(() => {
        vi.clearAllMocks();
        consoleLogSpy.mockRestore();
    });
    it('should display message when no previous sessions were found', async () => {
        // Arrange: Return empty array from listSessions
        mockListSessions.mockResolvedValue([]);
        // Act
        await listSessions(mockConfig);
        // Assert
        expect(mockListSessions).toHaveBeenCalledOnce();
        expect(consoleLogSpy).toHaveBeenCalledWith('No previous sessions found for this project.');
    });
    it('should list sessions when sessions are found', async () => {
        // Arrange: Create test sessions
        const now = new Date('2025-01-20T12:00:00.000Z');
        const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
        const twoDaysAgo = new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000);
        const mockSessions = [
            {
                id: 'session-1',
                file: 'session-2025-01-18T12-00-00-session-1',
                fileName: 'session-2025-01-18T12-00-00-session-1.json',
                startTime: twoDaysAgo.toISOString(),
                lastUpdated: twoDaysAgo.toISOString(),
                messageCount: 5,
                displayName: 'First user message',
                firstUserMessage: 'First user message',
                isCurrentSession: false,
                index: 1,
            },
            {
                id: 'session-2',
                file: 'session-2025-01-20T11-00-00-session-2',
                fileName: 'session-2025-01-20T11-00-00-session-2.json',
                startTime: oneHourAgo.toISOString(),
                lastUpdated: oneHourAgo.toISOString(),
                messageCount: 10,
                displayName: 'Second user message',
                firstUserMessage: 'Second user message',
                isCurrentSession: false,
                index: 2,
            },
            {
                id: 'current-session-id',
                file: 'session-2025-01-20T12-00-00-current-s',
                fileName: 'session-2025-01-20T12-00-00-current-s.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 3,
                displayName: 'Current session',
                firstUserMessage: 'Current session',
                isCurrentSession: true,
                index: 3,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        // Act
        await listSessions(mockConfig);
        // Assert
        expect(mockListSessions).toHaveBeenCalledOnce();
        // Check that the header was displayed
        expect(consoleLogSpy).toHaveBeenCalledWith('\nAvailable sessions for this project (3):\n');
        // Check that each session was logged
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('1. First user message'));
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('[session-1]'));
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('2. Second user message'));
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('[session-2]'));
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('3. Current session'));
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining(', current)'));
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('[current-session-id]'));
    });
    it('should sort sessions by start time (oldest first)', async () => {
        // Arrange: Create sessions in non-chronological order
        const session1Time = new Date('2025-01-18T12:00:00.000Z');
        const session2Time = new Date('2025-01-19T12:00:00.000Z');
        const session3Time = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'session-2',
                file: 'session-2',
                fileName: 'session-2.json',
                startTime: session2Time.toISOString(), // Middle
                lastUpdated: session2Time.toISOString(),
                messageCount: 5,
                displayName: 'Middle session',
                firstUserMessage: 'Middle session',
                isCurrentSession: false,
                index: 2,
            },
            {
                id: 'session-1',
                file: 'session-1',
                fileName: 'session-1.json',
                startTime: session1Time.toISOString(), // Oldest
                lastUpdated: session1Time.toISOString(),
                messageCount: 5,
                displayName: 'Oldest session',
                firstUserMessage: 'Oldest session',
                isCurrentSession: false,
                index: 1,
            },
            {
                id: 'session-3',
                file: 'session-3',
                fileName: 'session-3.json',
                startTime: session3Time.toISOString(), // Newest
                lastUpdated: session3Time.toISOString(),
                messageCount: 5,
                displayName: 'Newest session',
                firstUserMessage: 'Newest session',
                isCurrentSession: false,
                index: 3,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        // Act
        await listSessions(mockConfig);
        // Assert
        // Get all the session log calls (skip the header)
        const sessionCalls = consoleLogSpy.mock.calls.filter((call) => typeof call[0] === 'string' &&
            call[0].includes('[session-') &&
            !call[0].includes('Available sessions'));
        // Verify they are sorted by start time (oldest first)
        expect(sessionCalls[0][0]).toContain('1. Oldest session');
        expect(sessionCalls[1][0]).toContain('2. Middle session');
        expect(sessionCalls[2][0]).toContain('3. Newest session');
    });
    it('should format session output with relative time and session ID', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'abc123def456',
                file: 'session-file',
                fileName: 'session-file.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 5,
                displayName: 'Test message',
                firstUserMessage: 'Test message',
                isCurrentSession: false,
                index: 1,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        // Act
        await listSessions(mockConfig);
        // Assert
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('1. Test message'));
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('some time ago'));
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('[abc123def456]'));
    });
    it('should handle single session', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'single-session',
                file: 'session-file',
                fileName: 'session-file.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 5,
                displayName: 'Only session',
                firstUserMessage: 'Only session',
                isCurrentSession: true,
                index: 1,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        // Act
        await listSessions(mockConfig);
        // Assert
        expect(consoleLogSpy).toHaveBeenCalledWith('\nAvailable sessions for this project (1):\n');
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('1. Only session'));
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining(', current)'));
    });
    it('should display summary as title when available instead of first user message', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'session-with-summary',
                file: 'session-file',
                fileName: 'session-file.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 10,
                displayName: 'Add dark mode to the app', // Summary
                firstUserMessage: 'How do I add dark mode to my React application with CSS variables?',
                isCurrentSession: false,
                index: 1,
                summary: 'Add dark mode to the app',
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        // Act
        await listSessions(mockConfig);
        // Assert: Should show the summary (displayName), not the first user message
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('1. Add dark mode to the app'));
        expect(consoleLogSpy).not.toHaveBeenCalledWith(expect.stringContaining('How do I add dark mode to my React application'));
    });
});
describe('deleteSession', () => {
    let mockConfig;
    let mockListSessions;
    let mockDeleteSession;
    let consoleLogSpy;
    let consoleErrorSpy;
    beforeEach(() => {
        // Create mock config
        mockConfig = {
            storage: {
                getProjectTempDir: vi.fn().mockReturnValue('/tmp/test-project'),
            },
            getSessionId: vi.fn().mockReturnValue('current-session-id'),
        };
        // Create mock methods
        mockListSessions = vi.fn();
        mockDeleteSession = vi.fn();
        // Mock SessionSelector constructor
        vi.mocked(SessionSelector).mockImplementation(() => ({
            listSessions: mockListSessions,
        }));
        // Mock ChatRecordingService
        vi.mocked(ChatRecordingService).mockImplementation(() => ({
            deleteSession: mockDeleteSession,
        }));
        // Spy on console methods
        consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
        consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
    });
    afterEach(() => {
        vi.clearAllMocks();
        consoleLogSpy.mockRestore();
        consoleErrorSpy.mockRestore();
    });
    it('should display error when no sessions are found', async () => {
        // Arrange
        mockListSessions.mockResolvedValue([]);
        // Act
        await deleteSession(mockConfig, '1');
        // Assert
        expect(mockListSessions).toHaveBeenCalledOnce();
        expect(consoleErrorSpy).toHaveBeenCalledWith('No sessions found for this project.');
        expect(mockDeleteSession).not.toHaveBeenCalled();
    });
    it('should delete session by UUID', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'session-uuid-123',
                file: 'session-file-123',
                fileName: 'session-file-123.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 5,
                displayName: 'Test session',
                firstUserMessage: 'Test session',
                isCurrentSession: false,
                index: 1,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        mockDeleteSession.mockImplementation(() => { });
        // Act
        await deleteSession(mockConfig, 'session-uuid-123');
        // Assert
        expect(mockListSessions).toHaveBeenCalledOnce();
        expect(mockDeleteSession).toHaveBeenCalledWith('session-file-123');
        expect(consoleLogSpy).toHaveBeenCalledWith('Deleted session 1: Test session (some time ago)');
        expect(consoleErrorSpy).not.toHaveBeenCalled();
    });
    it('should delete session by index', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
        const mockSessions = [
            {
                id: 'session-1',
                file: 'session-file-1',
                fileName: 'session-file-1.json',
                startTime: oneHourAgo.toISOString(),
                lastUpdated: oneHourAgo.toISOString(),
                messageCount: 5,
                displayName: 'First session',
                firstUserMessage: 'First session',
                isCurrentSession: false,
                index: 1,
            },
            {
                id: 'session-2',
                file: 'session-file-2',
                fileName: 'session-file-2.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 10,
                displayName: 'Second session',
                firstUserMessage: 'Second session',
                isCurrentSession: false,
                index: 2,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        mockDeleteSession.mockImplementation(() => { });
        // Act
        await deleteSession(mockConfig, '2');
        // Assert
        expect(mockListSessions).toHaveBeenCalledOnce();
        expect(mockDeleteSession).toHaveBeenCalledWith('session-file-2');
        expect(consoleLogSpy).toHaveBeenCalledWith('Deleted session 2: Second session (some time ago)');
    });
    it('should display error for invalid session identifier (non-numeric)', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'session-1',
                file: 'session-file-1',
                fileName: 'session-file-1.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 5,
                displayName: 'Test session',
                firstUserMessage: 'Test session',
                isCurrentSession: false,
                index: 1,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        // Act
        await deleteSession(mockConfig, 'invalid-id');
        // Assert
        expect(consoleErrorSpy).toHaveBeenCalledWith('Invalid session identifier "invalid-id". Use --list-sessions to see available sessions.');
        expect(mockDeleteSession).not.toHaveBeenCalled();
    });
    it('should display error for invalid session identifier (out of range)', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'session-1',
                file: 'session-file-1',
                fileName: 'session-file-1.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 5,
                displayName: 'Test session',
                firstUserMessage: 'Test session',
                isCurrentSession: false,
                index: 1,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        // Act
        await deleteSession(mockConfig, '999');
        // Assert
        expect(consoleErrorSpy).toHaveBeenCalledWith('Invalid session identifier "999". Use --list-sessions to see available sessions.');
        expect(mockDeleteSession).not.toHaveBeenCalled();
    });
    it('should display error for invalid session identifier (zero)', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'session-1',
                file: 'session-file-1',
                fileName: 'session-file-1.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 5,
                displayName: 'Test session',
                firstUserMessage: 'Test session',
                isCurrentSession: false,
                index: 1,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        // Act
        await deleteSession(mockConfig, '0');
        // Assert
        expect(consoleErrorSpy).toHaveBeenCalledWith('Invalid session identifier "0". Use --list-sessions to see available sessions.');
        expect(mockDeleteSession).not.toHaveBeenCalled();
    });
    it('should prevent deletion of current session', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'current-session-id',
                file: 'current-session-file',
                fileName: 'current-session-file.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 5,
                displayName: 'Current session',
                firstUserMessage: 'Current session',
                isCurrentSession: true,
                index: 1,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        // Act - try to delete by index
        await deleteSession(mockConfig, '1');
        // Assert
        expect(consoleErrorSpy).toHaveBeenCalledWith('Cannot delete the current active session.');
        expect(mockDeleteSession).not.toHaveBeenCalled();
    });
    it('should prevent deletion of current session by UUID', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'current-session-id',
                file: 'current-session-file',
                fileName: 'current-session-file.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 5,
                displayName: 'Current session',
                firstUserMessage: 'Current session',
                isCurrentSession: true,
                index: 1,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        // Act - try to delete by UUID
        await deleteSession(mockConfig, 'current-session-id');
        // Assert
        expect(consoleErrorSpy).toHaveBeenCalledWith('Cannot delete the current active session.');
        expect(mockDeleteSession).not.toHaveBeenCalled();
    });
    it('should handle deletion errors gracefully', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'session-1',
                file: 'session-file-1',
                fileName: 'session-file-1.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 5,
                displayName: 'Test session',
                firstUserMessage: 'Test session',
                isCurrentSession: false,
                index: 1,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        mockDeleteSession.mockImplementation(() => {
            throw new Error('File deletion failed');
        });
        // Act
        await deleteSession(mockConfig, '1');
        // Assert
        expect(mockDeleteSession).toHaveBeenCalledWith('session-file-1');
        expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to delete session: File deletion failed');
    });
    it('should handle non-Error deletion failures', async () => {
        // Arrange
        const now = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'session-1',
                file: 'session-file-1',
                fileName: 'session-file-1.json',
                startTime: now.toISOString(),
                lastUpdated: now.toISOString(),
                messageCount: 5,
                displayName: 'Test session',
                firstUserMessage: 'Test session',
                isCurrentSession: false,
                index: 1,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        mockDeleteSession.mockImplementation(() => {
            // eslint-disable-next-line no-restricted-syntax
            throw 'Unknown error type';
        });
        // Act
        await deleteSession(mockConfig, '1');
        // Assert
        expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to delete session: Unknown error');
    });
    it('should sort sessions before finding by index', async () => {
        // Arrange: Create sessions in non-chronological order
        const session1Time = new Date('2025-01-18T12:00:00.000Z');
        const session2Time = new Date('2025-01-19T12:00:00.000Z');
        const session3Time = new Date('2025-01-20T12:00:00.000Z');
        const mockSessions = [
            {
                id: 'session-3',
                file: 'session-file-3',
                fileName: 'session-file-3.json',
                startTime: session3Time.toISOString(), // Newest
                lastUpdated: session3Time.toISOString(),
                messageCount: 5,
                displayName: 'Newest session',
                firstUserMessage: 'Newest session',
                isCurrentSession: false,
                index: 3,
            },
            {
                id: 'session-1',
                file: 'session-file-1',
                fileName: 'session-file-1.json',
                startTime: session1Time.toISOString(), // Oldest
                lastUpdated: session1Time.toISOString(),
                messageCount: 5,
                displayName: 'Oldest session',
                firstUserMessage: 'Oldest session',
                isCurrentSession: false,
                index: 1,
            },
            {
                id: 'session-2',
                file: 'session-file-2',
                fileName: 'session-file-2.json',
                startTime: session2Time.toISOString(), // Middle
                lastUpdated: session2Time.toISOString(),
                messageCount: 5,
                displayName: 'Middle session',
                firstUserMessage: 'Middle session',
                isCurrentSession: false,
                index: 2,
            },
        ];
        mockListSessions.mockResolvedValue(mockSessions);
        mockDeleteSession.mockImplementation(() => { });
        // Act - delete index 1 (should be oldest session after sorting)
        await deleteSession(mockConfig, '1');
        // Assert
        expect(mockDeleteSession).toHaveBeenCalledWith('session-file-1');
        expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Oldest session'));
    });
});
//# sourceMappingURL=sessions.test.js.map