/**
 * Pi Ant Colony Integration for Bolloon
 *
 * Multi-agent collaboration system with white-box signal protocol.
 * Based on oh-pi's ant colony system: COLONY_SIGNAL protocol for agent coordination.
 *
 * Signal Protocol:
 * COLONY_SIGNAL:LAUNCHED → SCOUTING → WORKING → REVIEWING → COMPLETE
 *
 * Key features:
 * - Fully visible signal protocol (no black boxes)
 * - Per-ant turn limits, cost tracking, concurrent scheduling
 * - White-box design: no "what is it doing in the background" anxiety
 */
import { EventEmitter } from 'events';
import * as fs from 'fs/promises';
import * as path from 'path';
// Colony state
const ants = new Map();
const tasks = new Map();
const signalHistory = [];
const STATE_DIR = path.join(process.env.HOME || '/tmp', '.bolloon', 'colony');
// Event emitter
class ColonyEventEmitter extends EventEmitter {
}
const colonyEvents = new ColonyEventEmitter();
/**
 * Generate a unique ant ID
 */
function generateAntId() {
    return `ant-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
}
/**
 * Register a new ant in the colony
 */
export function registerAnt(name, role, maxTurns = 10, tokenBudget = 50000) {
    const id = generateAntId();
    const ant = {
        id,
        name,
        role,
        signal: 'LAUNCHED',
        turnCount: 0,
        maxTurns,
        tokenBudget,
        tokensUsed: 0,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
    };
    ants.set(id, ant);
    emitSignal(id, 'LAUNCHED');
    console.log(`[Colony] Registered ant: ${name} (${role})`);
    return ant;
}
/**
 * Emit a signal transition
 */
function emitSignal(antId, newSignal, details) {
    const ant = ants.get(antId);
    if (!ant)
        return;
    const event = {
        antId,
        antName: ant.name,
        fromSignal: ant.signal,
        toSignal: newSignal,
        timestamp: new Date().toISOString(),
        details,
    };
    signalHistory.push(event);
    if (signalHistory.length > 100) {
        signalHistory.shift();
    }
    ant.signal = newSignal;
    ant.updatedAt = new Date().toISOString();
    ants.set(antId, ant);
    colonyEvents.emit('signal', event);
    console.log(`[Colony] ${ant.name}: ${event.fromSignal} → ${event.toSignal}${details ? ` (${details})` : ''}`);
}
/**
 * Transition ant to SCOUTING
 */
export function antScouting(antId, task) {
    const ant = ants.get(antId);
    if (!ant)
        return;
    ant.task = task;
    ants.set(antId, ant);
    emitSignal(antId, 'SCOUTING', `Task: ${task.substring(0, 50)}...`);
}
/**
 * Transition ant to WORKING
 */
export function antWorking(antId) {
    const ant = ants.get(antId);
    if (!ant)
        return;
    emitSignal(antId, 'WORKING');
}
/**
 * Transition ant to REVIEWING
 */
export function antReviewing(antId) {
    emitSignal(antId, 'REVIEWING');
}
/**
 * Complete ant work
 */
export function antComplete(antId, result) {
    const ant = ants.get(antId);
    if (!ant)
        return;
    ant.result = result;
    ants.set(antId, ant);
    emitSignal(antId, 'COMPLETE', `Result: ${result.substring(0, 50)}...`);
}
/**
 * Fail ant work
 */
export function antFail(antId, error) {
    const ant = ants.get(antId);
    if (!ant)
        return;
    ant.result = error;
    ants.set(antId, ant);
    emitSignal(antId, 'FAILED', error);
}
/**
 * Abort ant work
 */
export function antAbort(antId, reason) {
    emitSignal(antId, 'ABORTED', reason);
}
/**
 * Increment turn count
 */
export function antTick(antId, tokensUsed = 0) {
    const ant = ants.get(antId);
    if (!ant)
        return;
    ant.turnCount++;
    ant.tokensUsed += tokensUsed;
    ant.updatedAt = new Date().toISOString();
    ants.set(antId, ant);
    // Check if exceeded limits
    if (ant.turnCount >= ant.maxTurns) {
        antAbort(antId, 'Max turns exceeded');
    }
    else if (ant.tokensUsed >= ant.tokenBudget) {
        antAbort(antId, 'Token budget exceeded');
    }
}
/**
 * Create a colony task
 */
export function createTask(description) {
    const id = `task-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
    const task = {
        id,
        description,
        status: 'pending',
        assignedAnts: [],
        results: new Map(),
        createdAt: new Date().toISOString(),
    };
    tasks.set(id, task);
    console.log(`[Colony] Created task: ${id} - ${description.substring(0, 50)}...`);
    return task;
}
/**
 * Dispatch task to ants
 */
export function dispatchTask(taskId, antIds) {
    const task = tasks.get(taskId);
    if (!task)
        return;
    task.status = 'dispatched';
    task.assignedAnts = antIds;
    tasks.set(taskId, task);
    for (const antId of antIds) {
        const ant = ants.get(antId);
        if (ant) {
            antScouting(antId, task.description);
        }
    }
    console.log(`[Colony] Dispatched task ${taskId} to ${antIds.length} ants`);
}
/**
 * Record ant result for a task
 */
export function recordResult(taskId, antId, result) {
    const task = tasks.get(taskId);
    if (!task)
        return;
    task.results.set(antId, result);
    // Check if all ants completed
    if (task.results.size === task.assignedAnts.length) {
        task.status = 'completed';
        task.completedAt = new Date().toISOString();
        for (const id of task.assignedAnts) {
            antReviewing(id);
        }
    }
    tasks.set(taskId, task);
}
/**
 * Get ant by ID
 */
export function getAnt(antId) {
    return ants.get(antId);
}
/**
 * List all ants
 */
export function listAnts() {
    return Array.from(ants.values());
}
/**
 * List ants by role
 */
export function listAntsByRole(role) {
    return Array.from(ants.values()).filter((a) => a.role === role);
}
/**
 * Get ants by signal state
 */
export function listAntsBySignal(signal) {
    return Array.from(ants.values()).filter((a) => a.signal === signal);
}
/**
 * Get active ants (not complete/failed/aborted)
 */
export function getActiveAnts() {
    return Array.from(ants.values()).filter((a) => !['COMPLETE', 'FAILED', 'ABORTED'].includes(a.signal));
}
/**
 * Get task by ID
 */
export function getTask(taskId) {
    return tasks.get(taskId);
}
/**
 * List all tasks
 */
export function listTasks() {
    return Array.from(tasks.values());
}
/**
 * Get signal history
 */
export function getSignalHistory() {
    return [...signalHistory];
}
/**
 * Get colony status
 */
export function getColonyStatus() {
    const antList = Array.from(ants.values());
    const taskList = Array.from(tasks.values());
    const signalDistribution = {
        LAUNCHED: 0,
        SCOUTING: 0,
        WORKING: 0,
        REVIEWING: 0,
        COMPLETE: 0,
        FAILED: 0,
        ABORTED: 0,
    };
    for (const ant of antList) {
        signalDistribution[ant.signal]++;
    }
    return {
        antCount: antList.length,
        activeAnts: antList.filter((a) => !['COMPLETE', 'FAILED', 'ABORTED'].includes(a.signal)).length,
        taskCount: taskList.length,
        activeTasks: taskList.filter((t) => t.status === 'in_progress' || t.status === 'dispatched').length,
        signalDistribution,
    };
}
/**
 * Subscribe to colony events
 */
export function onColonyEvent(event, callback) {
    colonyEvents.on(event, callback);
}
/**
 * Unsubscribe from colony events
 */
export function offColonyEvent(event, callback) {
    colonyEvents.off(event, callback);
}
/**
 * Get formatted colony dump for debugging
 */
export function getColonyDump() {
    const status = getColonyStatus();
    const active = getActiveAnts();
    let dump = '# Colony Status\n\n';
    dump += `Ants: ${status.antCount} total, ${status.activeAnts} active\n`;
    dump += `Tasks: ${status.taskCount} total, ${status.activeTasks} active\n\n`;
    dump += '## Signal Distribution\n';
    for (const [signal, count] of Object.entries(status.signalDistribution)) {
        if (count > 0) {
            dump += `- ${signal}: ${count}\n`;
        }
    }
    dump += '\n## Active Ants\n';
    for (const ant of active) {
        dump += `- ${ant.name} (${ant.role}): ${ant.signal} [${ant.turnCount}/${ant.maxTurns} turns, ${ant.tokensUsed}/${ant.tokenBudget} tokens]\n`;
        if (ant.task) {
            dump += `  Task: ${ant.task.substring(0, 60)}...\n`;
        }
    }
    dump += '\n## Recent Signals\n';
    const recent = signalHistory.slice(-10);
    for (const event of recent.reverse()) {
        dump += `- ${event.timestamp}: ${event.antName}: ${event.fromSignal} → ${event.toSignal}\n`;
    }
    return dump;
}
/**
 * Persist colony state
 */
export async function persistColony() {
    try {
        await fs.mkdir(STATE_DIR, { recursive: true });
        const state = {
            ants: Array.from(ants.values()),
            tasks: Array.from(tasks.entries()).map(([id, t]) => ({
                ...t,
                results: Array.from(t.results.entries()),
            })),
            signalHistory: signalHistory.slice(-50),
            persistedAt: new Date().toISOString(),
        };
        await fs.writeFile(path.join(STATE_DIR, 'colony.json'), JSON.stringify(state, null, 2));
        console.log('[Colony] Persisted state');
    }
    catch (e) {
        console.warn('[Colony] Failed to persist:', e);
    }
}
/**
 * Load colony state
 */
export async function loadColony() {
    try {
        const data = await fs.readFile(path.join(STATE_DIR, 'colony.json'), 'utf-8');
        const state = JSON.parse(data);
        ants.clear();
        for (const a of state.ants) {
            ants.set(a.id, a);
        }
        tasks.clear();
        for (const t of state.tasks) {
            tasks.set(t.id, { ...t, results: new Map(t.results) });
        }
        signalHistory.length = 0;
        signalHistory.push(...state.signalHistory);
        console.log(`[Colony] Loaded ${ants.size} ants, ${tasks.size} tasks`);
    }
    catch {
        // No persisted state
    }
}
