import * as fs from 'fs/promises';
import * as path from 'path';
const DEFAULT_STORAGE_PATH = path.join(process.env.HOME || '/tmp', '.bolloon', 'agents');
const DEFAULT_TASK_TIMEOUT_MS = 5 * 60 * 1000;
const DEFAULT_MAX_CONCURRENT_TASKS = 10;
export class SubAgentManager {
    agents = new Map();
    tasks = new Map();
    messages = new Map();
    config;
    initialized = false;
    messageListeners = new Map();
    taskListeners = new Map();
    cleanupInterval = null;
    constructor(config = {}) {
        this.config = {
            storagePath: config.storagePath || DEFAULT_STORAGE_PATH,
            autoCleanupIntervalMs: config.autoCleanupIntervalMs || 60000,
            taskTimeoutMs: config.taskTimeoutMs || DEFAULT_TASK_TIMEOUT_MS,
            maxConcurrentTasks: config.maxConcurrentTasks || DEFAULT_MAX_CONCURRENT_TASKS
        };
    }
    async initialize() {
        if (this.initialized)
            return;
        await fs.mkdir(this.config.storagePath, { recursive: true });
        await this.loadAgents();
        await this.loadTasks();
        this.cleanupInterval = setInterval(() => {
            this.cleanupStaleTasks();
        }, this.config.autoCleanupIntervalMs);
        this.initialized = true;
    }
    async loadAgents() {
        try {
            const agentsPath = path.join(this.config.storagePath, 'agents.json');
            const data = await fs.readFile(agentsPath, 'utf-8');
            const agentsArray = JSON.parse(data);
            this.agents.clear();
            for (const agent of agentsArray) {
                if (agent.status !== 'terminated') {
                    this.agents.set(agent.id, agent);
                }
            }
        }
        catch {
            this.agents.clear();
        }
    }
    async loadTasks() {
        try {
            const tasksPath = path.join(this.config.storagePath, 'tasks.json');
            const data = await fs.readFile(tasksPath, 'utf-8');
            const tasksArray = JSON.parse(data);
            this.tasks.clear();
            for (const task of tasksArray) {
                this.tasks.set(task.id, task);
            }
        }
        catch {
            this.tasks.clear();
        }
    }
    async saveAgents() {
        const agentsPath = path.join(this.config.storagePath, 'agents.json');
        const agentsArray = Array.from(this.agents.values());
        await fs.writeFile(agentsPath, JSON.stringify(agentsArray, null, 2));
    }
    async saveTasks() {
        const tasksPath = path.join(this.config.storagePath, 'tasks.json');
        const tasksArray = Array.from(this.tasks.values());
        await fs.writeFile(tasksPath, JSON.stringify(tasksArray, null, 2));
    }
    async registerAgent(agent) {
        await this.initialize();
        const newAgent = {
            ...agent,
            id: `agent_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`,
            status: 'creating',
            createdAt: new Date().toISOString(),
            lastActive: new Date().toISOString()
        };
        this.agents.set(newAgent.id, newAgent);
        await this.saveAgents();
        await this.updateAgentStatus(newAgent.id, 'active');
        return newAgent;
    }
    async unregisterAgent(agentId) {
        await this.initialize();
        await this.updateAgentStatus(agentId, 'terminated');
        this.agents.delete(agentId);
        await this.saveAgents();
    }
    async updateAgentStatus(agentId, status) {
        const agent = this.agents.get(agentId);
        if (agent) {
            agent.status = status;
            agent.lastActive = new Date().toISOString();
            await this.saveAgents();
        }
    }
    async updateAgent(agentId, updates) {
        const agent = this.agents.get(agentId);
        if (agent) {
            Object.assign(agent, updates, { lastActive: new Date().toISOString() });
            await this.saveAgents();
        }
    }
    async getAgent(agentId) {
        return this.agents.get(agentId);
    }
    async getAllAgents() {
        return Array.from(this.agents.values());
    }
    async getActiveAgents() {
        return Array.from(this.agents.values()).filter(a => a.status === 'active' || a.status === 'idle');
    }
    async getAgentsByCapability(capability) {
        return Array.from(this.agents.values()).filter(agent => agent.status === 'active' &&
            agent.capabilities.some(c => c.toLowerCase().includes(capability.toLowerCase())));
    }
    async getAgentByDid(did) {
        return Array.from(this.agents.values()).find(a => a.did === did);
    }
    async findBestAgentForTask(requiredCapabilities, excludeAgentId) {
        const availableAgents = Array.from(this.agents.values()).filter(a => a.status === 'active' && a.id !== excludeAgentId);
        if (availableAgents.length === 0)
            return undefined;
        const scored = availableAgents.map(agent => {
            const matchedCapabilities = agent.capabilities.filter(c => requiredCapabilities.some(req => c.toLowerCase().includes(req.toLowerCase())));
            const score = matchedCapabilities.length / requiredCapabilities.length;
            return { agent, score };
        });
        scored.sort((a, b) => b.score - a.score);
        return scored[0]?.agent;
    }
    async createTask(type, title, description, fromAgentId, toAgentId, priority = 'normal', input, metadata) {
        await this.initialize();
        const task = {
            id: `task_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`,
            type,
            title,
            description,
            priority,
            fromAgentId,
            toAgentId,
            assignedAgentId: toAgentId,
            status: toAgentId ? 'assigned' : 'pending',
            input,
            createdAt: new Date().toISOString(),
            metadata
        };
        if (toAgentId) {
            task.assignedAt = new Date().toISOString();
        }
        this.tasks.set(task.id, task);
        await this.saveTasks();
        return task;
    }
    async assignTask(taskId, toAgentId) {
        const task = this.tasks.get(taskId);
        if (task) {
            task.assignedAgentId = toAgentId;
            task.toAgentId = toAgentId;
            task.status = 'assigned';
            task.assignedAt = new Date().toISOString();
            await this.saveTasks();
            this.notifyTaskListeners(task);
        }
    }
    async updateTaskStatus(taskId, status, result, error) {
        const task = this.tasks.get(taskId);
        if (task) {
            task.status = status;
            if (result)
                task.result = result;
            if (error)
                task.error = error;
            if (status === 'completed' || status === 'failed') {
                task.completedAt = new Date().toISOString();
            }
            await this.saveTasks();
            this.notifyTaskListeners(task);
        }
    }
    async getTask(taskId) {
        return this.tasks.get(taskId);
    }
    async getTasksForAgent(agentId) {
        return Array.from(this.tasks.values()).filter(t => t.fromAgentId === agentId ||
            t.toAgentId === agentId ||
            t.assignedAgentId === agentId);
    }
    async getPendingTasks() {
        return Array.from(this.tasks.values()).filter(t => t.status === 'pending');
    }
    async getActiveTasks() {
        return Array.from(this.tasks.values()).filter(t => t.status === 'assigned' || t.status === 'in_progress');
    }
    async sendMessage(fromAgentId, toAgentId, content, type = 'notification', taskId, metadata) {
        await this.initialize();
        const message = {
            id: `msg_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`,
            type,
            fromAgentId,
            toAgentId,
            content,
            taskId,
            timestamp: new Date().toISOString(),
            metadata
        };
        if (!this.messages.has(toAgentId)) {
            this.messages.set(toAgentId, []);
        }
        this.messages.get(toAgentId).push(message);
        this.notifyMessageListeners(toAgentId, message);
        return message;
    }
    async getMessagesForAgent(agentId, since) {
        const messages = this.messages.get(agentId) || [];
        if (since) {
            const sinceTime = new Date(since).getTime();
            return messages.filter(m => new Date(m.timestamp).getTime() > sinceTime);
        }
        return [...messages];
    }
    async broadcastMessage(fromAgentId, content, type = 'notification') {
        const agents = await this.getActiveAgents();
        for (const agent of agents) {
            if (agent.id !== fromAgentId) {
                await this.sendMessage(fromAgentId, agent.id, content, type);
            }
        }
    }
    onMessage(agentId, callback) {
        if (!this.messageListeners.has(agentId)) {
            this.messageListeners.set(agentId, []);
        }
        this.messageListeners.get(agentId).push(callback);
        return () => {
            const listeners = this.messageListeners.get(agentId);
            if (listeners) {
                const index = listeners.indexOf(callback);
                if (index > -1)
                    listeners.splice(index, 1);
            }
        };
    }
    onTask(callback) {
        const wrapper = (task) => callback(task);
        const id = Math.random().toString(36);
        if (!this.taskListeners.has(id)) {
            this.taskListeners.set(id, []);
        }
        this.taskListeners.get(id).push(wrapper);
        return () => {
            const listeners = this.taskListeners.get(id);
            if (listeners) {
                const index = listeners.indexOf(wrapper);
                if (index > -1)
                    listeners.splice(index, 1);
            }
        };
    }
    notifyMessageListeners(agentId, message) {
        const listeners = this.messageListeners.get(agentId);
        if (listeners) {
            for (const listener of listeners) {
                try {
                    listener(message);
                }
                catch (e) {
                    console.error(`Message listener error for ${agentId}:`, e);
                }
            }
        }
    }
    notifyTaskListeners(task) {
        for (const listeners of this.taskListeners.values()) {
            for (const listener of listeners) {
                try {
                    listener(task);
                }
                catch (e) {
                    console.error('Task listener error:', e);
                }
            }
        }
    }
    cleanupStaleTasks() {
        const now = Date.now();
        const timeout = this.config.taskTimeoutMs;
        for (const task of this.tasks.values()) {
            if ((task.status === 'assigned' || task.status === 'in_progress') &&
                task.assignedAt &&
                now - new Date(task.assignedAt).getTime() > timeout) {
                task.status = 'failed';
                task.error = 'Task timeout';
                task.completedAt = new Date().toISOString();
            }
        }
    }
    async delegateTask(fromAgentId, taskDescription, requiredCapabilities, priority = 'normal', input) {
        const agent = await this.findBestAgentForTask(requiredCapabilities, fromAgentId);
        if (!agent) {
            const task = await this.createTask('delegate', taskDescription.substring(0, 50), taskDescription, fromAgentId, undefined, priority, input);
            return { task, agent: undefined };
        }
        const task = await this.createTask('delegate', taskDescription.substring(0, 50), taskDescription, fromAgentId, agent.id, priority, input);
        return { task, agent };
    }
    async consultAgent(fromAgentId, toAgentId, query, taskId) {
        return this.sendMessage(fromAgentId, toAgentId, query, 'query', taskId);
    }
    async getAgentStatistics() {
        const agents = Array.from(this.agents.values());
        const tasks = Array.from(this.tasks.values());
        return {
            total: agents.length,
            active: agents.filter(a => a.status === 'active').length,
            idle: agents.filter(a => a.status === 'idle').length,
            busy: agents.filter(a => a.status === 'busy').length,
            terminated: agents.filter(a => a.status === 'terminated').length,
            pendingTasks: tasks.filter(t => t.status === 'pending').length,
            activeTasks: tasks.filter(t => t.status === 'assigned' || t.status === 'in_progress').length
        };
    }
    async destroy() {
        if (this.cleanupInterval) {
            clearInterval(this.cleanupInterval);
            this.cleanupInterval = null;
        }
        this.agents.clear();
        this.tasks.clear();
        this.messages.clear();
        this.messageListeners.clear();
        this.taskListeners.clear();
        this.initialized = false;
    }
}
let subAgentManagerInstance = null;
export function getSubAgentManager() {
    if (!subAgentManagerInstance) {
        subAgentManagerInstance = new SubAgentManager();
    }
    return subAgentManagerInstance;
}
export async function createSubAgentManager(config) {
    if (subAgentManagerInstance) {
        return subAgentManagerInstance;
    }
    subAgentManagerInstance = new SubAgentManager(config);
    await subAgentManagerInstance.initialize();
    return subAgentManagerInstance;
}
export function resetSubAgentManager() {
    if (subAgentManagerInstance) {
        subAgentManagerInstance.destroy();
        subAgentManagerInstance = null;
    }
}
