/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import express from 'express';
import { DefaultRequestHandler, InMemoryTaskStore, DefaultExecutionEventBus, } from '@a2a-js/sdk/server';
import { A2AExpressApp } from '@a2a-js/sdk/server/express'; // Import server components
import { v4 as uuidv4 } from 'uuid';
import { logger } from '../utils/logger.js';
import { GCSTaskStore, NoOpTaskStore } from '../persistence/gcs.js';
import { CoderAgentExecutor } from '../agent/executor.js';
import { requestStorage } from './requestStorage.js';
import { loadConfig, loadEnvironment, setTargetDir } from '../config/config.js';
import { loadSettings } from '../config/settings.js';
import { loadExtensions } from '../config/extension.js';
import { commandRegistry } from '../commands/command-registry.js';
import { debugLogger, SimpleExtensionLoader } from '@google/gemini-cli-core';
import { GitService } from '@google/gemini-cli-core';
const coderAgentCard = {
    name: 'Gemini SDLC Agent',
    description: 'An agent that generates code based on natural language instructions and streams file outputs.',
    url: 'http://localhost:41242/',
    provider: {
        organization: 'Google',
        url: 'https://google.com',
    },
    protocolVersion: '0.3.0',
    version: '0.0.2', // Incremented version
    capabilities: {
        streaming: true,
        pushNotifications: false,
        stateTransitionHistory: true,
    },
    securitySchemes: undefined,
    security: undefined,
    defaultInputModes: ['text'],
    defaultOutputModes: ['text'],
    skills: [
        {
            id: 'code_generation',
            name: 'Code Generation',
            description: 'Generates code snippets or complete files based on user requests, streaming the results.',
            tags: ['code', 'development', 'programming'],
            examples: [
                'Write a python function to calculate fibonacci numbers.',
                'Create an HTML file with a basic button that alerts "Hello!" when clicked.',
            ],
            inputModes: ['text'],
            outputModes: ['text'],
        },
    ],
    supportsAuthenticatedExtendedCard: false,
};
export function updateCoderAgentCardUrl(port) {
    coderAgentCard.url = `http://localhost:${port}/`;
}
async function handleExecuteCommand(req, res, context) {
    logger.info('[CoreAgent] Received /executeCommand request: ', req.body);
    const { command, args } = req.body;
    try {
        if (typeof command !== 'string') {
            return res.status(400).json({ error: 'Invalid "command" field.' });
        }
        if (args && !Array.isArray(args)) {
            return res.status(400).json({ error: '"args" field must be an array.' });
        }
        const commandToExecute = commandRegistry.get(command);
        if (commandToExecute?.requiresWorkspace) {
            if (!process.env['CODER_AGENT_WORKSPACE_PATH']) {
                return res.status(400).json({
                    error: `Command "${command}" requires a workspace, but CODER_AGENT_WORKSPACE_PATH is not set.`,
                });
            }
        }
        if (!commandToExecute) {
            return res.status(404).json({ error: `Command not found: ${command}` });
        }
        if (commandToExecute.streaming) {
            const eventBus = new DefaultExecutionEventBus();
            res.setHeader('Content-Type', 'text/event-stream');
            const eventHandler = (event) => {
                const jsonRpcResponse = {
                    jsonrpc: '2.0',
                    // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
                    id: 'taskId' in event ? event.taskId : event.messageId,
                    result: event,
                };
                res.write(`data: ${JSON.stringify(jsonRpcResponse)}\n`);
            };
            eventBus.on('event', eventHandler);
            await commandToExecute.execute({ ...context, eventBus }, args ?? []);
            eventBus.off('event', eventHandler);
            eventBus.finished();
            return res.end(); // Explicit return for streaming path
        }
        else {
            const result = await commandToExecute.execute(context, args ?? []);
            logger.info('[CoreAgent] Sending /executeCommand response: ', result);
            return res.status(200).json(result);
        }
    }
    catch (e) {
        logger.error(`Error executing /executeCommand: ${command} with args: ${JSON.stringify(args)}`, e);
        const errorMessage = e instanceof Error ? e.message : 'Unknown error executing command';
        return res.status(500).json({ error: errorMessage });
    }
}
export async function createApp() {
    try {
        // Load the server configuration once on startup.
        const workspaceRoot = setTargetDir(undefined);
        loadEnvironment();
        const settings = loadSettings(workspaceRoot);
        const extensions = loadExtensions(workspaceRoot);
        const config = await loadConfig(settings, new SimpleExtensionLoader(extensions), 'a2a-server');
        let git;
        if (config.getCheckpointingEnabled()) {
            git = new GitService(config.getTargetDir(), config.storage);
            await git.initialize();
        }
        // loadEnvironment() is called within getConfig now
        const bucketName = process.env['GCS_BUCKET_NAME'];
        let taskStoreForExecutor;
        let taskStoreForHandler;
        if (bucketName) {
            logger.info(`Using GCSTaskStore with bucket: ${bucketName}`);
            const gcsTaskStore = new GCSTaskStore(bucketName);
            taskStoreForExecutor = gcsTaskStore;
            taskStoreForHandler = new NoOpTaskStore(gcsTaskStore);
        }
        else {
            logger.info('Using InMemoryTaskStore');
            const inMemoryTaskStore = new InMemoryTaskStore();
            taskStoreForExecutor = inMemoryTaskStore;
            taskStoreForHandler = inMemoryTaskStore;
        }
        const agentExecutor = new CoderAgentExecutor(taskStoreForExecutor);
        const context = { config, git, agentExecutor };
        const requestHandler = new DefaultRequestHandler(coderAgentCard, taskStoreForHandler, agentExecutor);
        let expressApp = express();
        expressApp.use((req, res, next) => {
            requestStorage.run({ req }, next);
        });
        const appBuilder = new A2AExpressApp(requestHandler);
        expressApp = appBuilder.setupRoutes(expressApp, '');
        expressApp.use(express.json());
        expressApp.post('/tasks', async (req, res) => {
            try {
                const taskId = uuidv4();
                // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
                const agentSettings = req.body.agentSettings;
                const contextId = req.body.contextId || uuidv4();
                const wrapper = await agentExecutor.createTask(taskId, contextId, agentSettings);
                await taskStoreForExecutor.save(wrapper.toSDKTask());
                res.status(201).json(wrapper.id);
            }
            catch (error) {
                logger.error('[CoreAgent] Error creating task:', error);
                const errorMessage = error instanceof Error
                    ? error.message
                    : 'Unknown error creating task';
                res.status(500).send({ error: errorMessage });
            }
        });
        expressApp.post('/executeCommand', (req, res) => {
            void handleExecuteCommand(req, res, context);
        });
        expressApp.get('/listCommands', (req, res) => {
            try {
                const transformCommand = (command, visited) => {
                    const commandName = command.name;
                    if (visited.includes(commandName)) {
                        debugLogger.warn(`Command ${commandName} already inserted in the response, skipping`);
                        return undefined;
                    }
                    return {
                        name: command.name,
                        description: command.description,
                        arguments: command.arguments ?? [],
                        subCommands: (command.subCommands ?? [])
                            .map((subCommand) => transformCommand(subCommand, visited.concat(commandName)))
                            .filter((subCommand) => !!subCommand),
                    };
                };
                const commands = commandRegistry
                    .getAllCommands()
                    .filter((command) => command.topLevel)
                    .map((command) => transformCommand(command, []));
                return res.status(200).json({ commands });
            }
            catch (e) {
                logger.error('Error executing /listCommands:', e);
                const errorMessage = e instanceof Error ? e.message : 'Unknown error listing commands';
                return res.status(500).json({ error: errorMessage });
            }
        });
        expressApp.get('/tasks/metadata', async (req, res) => {
            // This endpoint is only meaningful if the task store is in-memory.
            if (!(taskStoreForExecutor instanceof InMemoryTaskStore)) {
                res.status(501).send({
                    error: 'Listing all task metadata is only supported when using InMemoryTaskStore.',
                });
            }
            try {
                const wrappers = agentExecutor.getAllTasks();
                if (wrappers && wrappers.length > 0) {
                    const tasksMetadata = await Promise.all(wrappers.map((wrapper) => wrapper.task.getMetadata()));
                    res.status(200).json(tasksMetadata);
                }
                else {
                    res.status(204).send();
                }
            }
            catch (error) {
                logger.error('[CoreAgent] Error getting all task metadata:', error);
                const errorMessage = error instanceof Error
                    ? error.message
                    : 'Unknown error getting task metadata';
                res.status(500).send({ error: errorMessage });
            }
        });
        expressApp.get('/tasks/:taskId/metadata', async (req, res) => {
            const taskId = req.params.taskId;
            let wrapper = agentExecutor.getTask(taskId);
            if (!wrapper) {
                const sdkTask = await taskStoreForExecutor.load(taskId);
                if (sdkTask) {
                    wrapper = await agentExecutor.reconstruct(sdkTask);
                }
            }
            if (!wrapper) {
                res.status(404).send({ error: 'Task not found' });
                return;
            }
            res.json({ metadata: await wrapper.task.getMetadata() });
        });
        return expressApp;
    }
    catch (error) {
        logger.error('[CoreAgent] Error during startup:', error);
        process.exit(1);
    }
}
export async function main() {
    try {
        const expressApp = await createApp();
        const port = Number(process.env['CODER_AGENT_PORT'] || 0);
        const server = expressApp.listen(port, 'localhost', () => {
            const address = server.address();
            let actualPort;
            if (process.env['CODER_AGENT_PORT']) {
                actualPort = process.env['CODER_AGENT_PORT'];
            }
            else if (address && typeof address !== 'string') {
                actualPort = address.port;
            }
            else {
                throw new Error('[Core Agent] Could not find port number.');
            }
            updateCoderAgentCardUrl(Number(actualPort));
            logger.info(`[CoreAgent] Agent Server started on http://localhost:${actualPort}`);
            logger.info(`[CoreAgent] Agent Card: http://localhost:${actualPort}/.well-known/agent-card.json`);
            logger.info('[CoreAgent] Press Ctrl+C to stop the server');
        });
    }
    catch (error) {
        logger.error('[CoreAgent] Error during startup:', error);
        process.exit(1);
    }
}
//# sourceMappingURL=app.js.map