/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import * as fsPromises from 'node:fs/promises';
import React from 'react';
import { Text } from 'ink';
import { theme } from '../semantic-colors.js';
import { CommandKind } from './types.js';
import { decodeTagName } from '@google/gemini-cli-core';
import path from 'node:path';
import { MessageType } from '../types.js';
const getSavedChatTags = async (context, mtSortDesc) => {
    const cfg = context.services.config;
    const geminiDir = cfg?.storage?.getProjectTempDir();
    if (!geminiDir) {
        return [];
    }
    try {
        const file_head = 'checkpoint-';
        const file_tail = '.json';
        const files = await fsPromises.readdir(geminiDir);
        const chatDetails = [];
        for (const file of files) {
            if (file.startsWith(file_head) && file.endsWith(file_tail)) {
                const filePath = path.join(geminiDir, file);
                const stats = await fsPromises.stat(filePath);
                const tagName = file.slice(file_head.length, -file_tail.length);
                chatDetails.push({
                    name: decodeTagName(tagName),
                    mtime: stats.mtime.toISOString(),
                });
            }
        }
        chatDetails.sort((a, b) => mtSortDesc
            ? b.mtime.localeCompare(a.mtime)
            : a.mtime.localeCompare(b.mtime));
        return chatDetails;
    }
    catch (_err) {
        return [];
    }
};
const listCommand = {
    name: 'list',
    description: 'List saved conversation checkpoints',
    kind: CommandKind.BUILT_IN,
    action: async (context) => {
        const chatDetails = await getSavedChatTags(context, false);
        const item = {
            type: MessageType.CHAT_LIST,
            chats: chatDetails,
        };
        context.ui.addItem(item, Date.now());
    },
};
const saveCommand = {
    name: 'save',
    description: 'Save the current conversation as a checkpoint. Usage: /chat save <tag>',
    kind: CommandKind.BUILT_IN,
    action: async (context, args) => {
        const tag = args.trim();
        if (!tag) {
            return {
                type: 'message',
                messageType: 'error',
                content: 'Missing tag. Usage: /chat save <tag>',
            };
        }
        const { logger, config } = context.services;
        await logger.initialize();
        if (!context.overwriteConfirmed) {
            const exists = await logger.checkpointExists(tag);
            if (exists) {
                return {
                    type: 'confirm_action',
                    prompt: React.createElement(Text, null, 'A checkpoint with the tag ', React.createElement(Text, { color: theme.text.accent }, tag), ' already exists. Do you want to overwrite it?'),
                    originalInvocation: {
                        raw: context.invocation?.raw || `/chat save ${tag}`,
                    },
                };
            }
        }
        const chat = await config?.getGeminiClient()?.getChat();
        if (!chat) {
            return {
                type: 'message',
                messageType: 'error',
                content: 'No chat client available to save conversation.',
            };
        }
        const history = chat.getHistory();
        if (history.length > 2) {
            await logger.saveCheckpoint(history, tag);
            return {
                type: 'message',
                messageType: 'info',
                content: `Conversation checkpoint saved with tag: ${decodeTagName(tag)}.`,
            };
        }
        else {
            return {
                type: 'message',
                messageType: 'info',
                content: 'No conversation found to save.',
            };
        }
    },
};
const resumeCommand = {
    name: 'resume',
    altNames: ['load'],
    description: 'Resume a conversation from a checkpoint. Usage: /chat resume <tag>',
    kind: CommandKind.BUILT_IN,
    action: async (context, args) => {
        const tag = args.trim();
        if (!tag) {
            return {
                type: 'message',
                messageType: 'error',
                content: 'Missing tag. Usage: /chat resume <tag>',
            };
        }
        const { logger } = context.services;
        await logger.initialize();
        const conversation = await logger.loadCheckpoint(tag);
        if (conversation.length === 0) {
            return {
                type: 'message',
                messageType: 'info',
                content: `No saved checkpoint found with tag: ${decodeTagName(tag)}.`,
            };
        }
        const rolemap = {
            user: MessageType.USER,
            model: MessageType.GEMINI,
        };
        const uiHistory = [];
        let hasSystemPrompt = false;
        let i = 0;
        for (const item of conversation) {
            i += 1;
            const text = item.parts
                ?.filter((m) => !!m.text)
                .map((m) => m.text)
                .join('') || '';
            if (!text) {
                continue;
            }
            if (i === 1 && text.match(/context for our chat/)) {
                hasSystemPrompt = true;
            }
            if (i > 2 || !hasSystemPrompt) {
                uiHistory.push({
                    type: (item.role && rolemap[item.role]) || MessageType.GEMINI,
                    text,
                });
            }
        }
        return {
            type: 'load_history',
            history: uiHistory,
            clientHistory: conversation,
        };
    },
    completion: async (context, partialArg) => {
        const chatDetails = await getSavedChatTags(context, true);
        return chatDetails
            .map((chat) => chat.name)
            .filter((name) => name.startsWith(partialArg));
    },
};
const deleteCommand = {
    name: 'delete',
    description: 'Delete a conversation checkpoint. Usage: /chat delete <tag>',
    kind: CommandKind.BUILT_IN,
    action: async (context, args) => {
        const tag = args.trim();
        if (!tag) {
            return {
                type: 'message',
                messageType: 'error',
                content: 'Missing tag. Usage: /chat delete <tag>',
            };
        }
        const { logger } = context.services;
        await logger.initialize();
        const deleted = await logger.deleteCheckpoint(tag);
        if (deleted) {
            return {
                type: 'message',
                messageType: 'info',
                content: `Conversation checkpoint '${decodeTagName(tag)}' has been deleted.`,
            };
        }
        else {
            return {
                type: 'message',
                messageType: 'error',
                content: `Error: No checkpoint found with tag '${decodeTagName(tag)}'.`,
            };
        }
    },
    completion: async (context, partialArg) => {
        const chatDetails = await getSavedChatTags(context, true);
        return chatDetails
            .map((chat) => chat.name)
            .filter((name) => name.startsWith(partialArg));
    },
};
export function serializeHistoryToMarkdown(history) {
    return history
        .map((item) => {
        const text = item.parts
            ?.map((part) => {
            if (part.text) {
                return part.text;
            }
            if (part.functionCall) {
                return `**Tool Command**:\n\`\`\`json\n${JSON.stringify(part.functionCall, null, 2)}\n\`\`\``;
            }
            if (part.functionResponse) {
                return `**Tool Response**:\n\`\`\`json\n${JSON.stringify(part.functionResponse, null, 2)}\n\`\`\``;
            }
            return '';
        })
            .join('') || '';
        const roleIcon = item.role === 'user' ? '🧑‍💻' : '✨';
        return `${roleIcon} ## ${(item.role || 'model').toUpperCase()}\n\n${text}`;
    })
        .join('\n\n---\n\n');
}
const shareCommand = {
    name: 'share',
    description: 'Share the current conversation to a markdown or json file. Usage: /chat share <file>',
    kind: CommandKind.BUILT_IN,
    action: async (context, args) => {
        let filePathArg = args.trim();
        if (!filePathArg) {
            filePathArg = `gemini-conversation-${Date.now()}.json`;
        }
        const filePath = path.resolve(filePathArg);
        const extension = path.extname(filePath);
        if (extension !== '.md' && extension !== '.json') {
            return {
                type: 'message',
                messageType: 'error',
                content: 'Invalid file format. Only .md and .json are supported.',
            };
        }
        const chat = await context.services.config?.getGeminiClient()?.getChat();
        if (!chat) {
            return {
                type: 'message',
                messageType: 'error',
                content: 'No chat client available to share conversation.',
            };
        }
        const history = chat.getHistory();
        // An empty conversation has two hidden messages that setup the context for
        // the chat. Thus, to check whether a conversation has been started, we
        // can't check for length 0.
        if (history.length <= 2) {
            return {
                type: 'message',
                messageType: 'info',
                content: 'No conversation found to share.',
            };
        }
        let content = '';
        if (extension === '.json') {
            content = JSON.stringify(history, null, 2);
        }
        else {
            content = serializeHistoryToMarkdown(history);
        }
        try {
            await fsPromises.writeFile(filePath, content);
            return {
                type: 'message',
                messageType: 'info',
                content: `Conversation shared to ${filePath}`,
            };
        }
        catch (err) {
            const errorMessage = err instanceof Error ? err.message : String(err);
            return {
                type: 'message',
                messageType: 'error',
                content: `Error sharing conversation: ${errorMessage}`,
            };
        }
    },
};
export const chatCommand = {
    name: 'chat',
    description: 'Manage conversation history.',
    kind: CommandKind.BUILT_IN,
    subCommands: [
        listCommand,
        saveCommand,
        resumeCommand,
        deleteCommand,
        shareCommand,
    ],
};
//# sourceMappingURL=chatCommand.js.map