/**
 * Orchestrator API Implementation
 *
 * This module provides the OrchestratorAPI class that extends QuarryAPI
 * with orchestration-specific functionality for managing AI agents, their
 * sessions, and task assignments.
 *
 * The OrchestratorAPI wraps an existing QuarryAPI instance and adds:
 * - Agent registration and management
 * - Session tracking
 * - Orchestrator-specific task metadata
 */
import { ElementType, EntityTypeValue, createEntity, createDirectChannel, createTimestamp, } from '@stoneforge/core';
import { QuarryAPIImpl, } from '@stoneforge/quarry';
import { validateAgentMetadata, getOrchestratorTaskMeta, setOrchestratorTaskMeta, updateOrchestratorTaskMeta, generateBranchName, generateWorktreePath, createSlugFromTitle, } from '../types/index.js';
// ============================================================================
// Constants
// ============================================================================
/**
 * Key used to store agent metadata in Entity.metadata
 */
const AGENT_META_KEY = 'agent';
/**
 * Generates a unique channel name for an agent based on agent name
 * Channel names follow the pattern: agent-{agentName}
 */
function generateAgentChannelName(agentName) {
    return `agent-${agentName}`;
}
/**
 * Type guard to check if an entity is an agent entity
 */
export function isAgentEntity(entity) {
    if (!entity.metadata || typeof entity.metadata !== 'object') {
        return false;
    }
    const agentMeta = entity.metadata[AGENT_META_KEY];
    return agentMeta !== undefined && validateAgentMetadata(agentMeta);
}
/**
 * Gets the agent metadata from an entity
 */
export function getAgentMetadata(entity) {
    if (!entity.metadata || typeof entity.metadata !== 'object') {
        return undefined;
    }
    const agentMeta = entity.metadata[AGENT_META_KEY];
    if (validateAgentMetadata(agentMeta)) {
        return agentMeta;
    }
    return undefined;
}
// ============================================================================
// OrchestratorAPI Implementation
// ============================================================================
/**
 * OrchestratorAPI implementation that extends QuarryAPIImpl
 */
export class OrchestratorAPIImpl extends QuarryAPIImpl {
    constructor(backend) {
        super(backend);
    }
    // ----------------------------------------
    // Agent Registration
    // ----------------------------------------
    async registerDirector(input) {
        const agentMetadata = {
            agentRole: 'director',
            sessionStatus: 'idle',
            maxConcurrentTasks: input.maxConcurrentTasks,
            roleDefinitionRef: input.roleDefinitionRef,
            provider: input.provider,
            model: input.model,
            executablePath: input.executablePath,
        };
        // Create the entity with agent metadata nested under the agent key
        const entity = await createEntity({
            name: input.name,
            entityType: EntityTypeValue.AGENT,
            createdBy: input.createdBy,
            tags: input.tags,
            metadata: { [AGENT_META_KEY]: agentMetadata },
        });
        // Save the entity using the generic create method
        // Cast the entity to satisfy the create method's signature
        const saved = await this.create(entity);
        const agentEntity = saved;
        const agentEntityId = agentEntity.id;
        // Create dedicated channel for the agent
        const channel = await this.createAgentChannel(input.name, agentEntityId, input.createdBy);
        // Update agent metadata with channel ID
        const updatedAgent = await this.updateAgentMetadata(agentEntityId, {
            channelId: channel.id,
        });
        return updatedAgent;
    }
    async registerWorker(input) {
        const agentMetadata = {
            agentRole: 'worker',
            workerMode: input.workerMode,
            sessionStatus: 'idle',
            maxConcurrentTasks: input.maxConcurrentTasks,
            roleDefinitionRef: input.roleDefinitionRef,
            provider: input.provider,
            model: input.model,
            executablePath: input.executablePath,
        };
        // Create the entity with agent metadata nested under the agent key
        const entity = await createEntity({
            name: input.name,
            entityType: EntityTypeValue.AGENT,
            createdBy: input.createdBy,
            tags: input.tags,
            metadata: { [AGENT_META_KEY]: agentMetadata },
            reportsTo: input.reportsTo,
        });
        // Save the entity using the generic create method
        const saved = await this.create(entity);
        const agentEntity = saved;
        const agentEntityId = agentEntity.id;
        // Create dedicated channel for the agent
        const channel = await this.createAgentChannel(input.name, agentEntityId, input.createdBy);
        // Update agent metadata with channel ID
        const updatedAgent = await this.updateAgentMetadata(agentEntityId, {
            channelId: channel.id,
        });
        return updatedAgent;
    }
    async registerSteward(input) {
        const agentMetadata = {
            agentRole: 'steward',
            stewardFocus: input.stewardFocus,
            triggers: input.triggers,
            playbook: input.playbook,
            playbookId: input.playbookId,
            sessionStatus: 'idle',
            maxConcurrentTasks: input.maxConcurrentTasks,
            roleDefinitionRef: input.roleDefinitionRef,
            provider: input.provider,
            model: input.model,
            executablePath: input.executablePath,
        };
        // Create the entity with agent metadata nested under the agent key
        const entity = await createEntity({
            name: input.name,
            entityType: EntityTypeValue.AGENT,
            createdBy: input.createdBy,
            tags: input.tags,
            metadata: { [AGENT_META_KEY]: agentMetadata },
            reportsTo: input.reportsTo,
        });
        // Save the entity using the generic create method
        const saved = await this.create(entity);
        const agentEntity = saved;
        const agentEntityId = agentEntity.id;
        // Create dedicated channel for the agent
        const channel = await this.createAgentChannel(input.name, agentEntityId, input.createdBy);
        // Update agent metadata with channel ID
        const updatedAgent = await this.updateAgentMetadata(agentEntityId, {
            channelId: channel.id,
        });
        return updatedAgent;
    }
    // ----------------------------------------
    // Agent Queries
    // ----------------------------------------
    async getAgent(entityId) {
        // Cast EntityId to ElementId - they are both branded string types
        const entity = await this.get(entityId);
        if (!entity || !isAgentEntity(entity)) {
            return undefined;
        }
        return entity;
    }
    async getAgentByName(name) {
        // Use lookupEntityByName from the parent class
        const entity = await this.lookupEntityByName(name);
        if (!entity || !isAgentEntity(entity)) {
            return undefined;
        }
        return entity;
    }
    async listAgents(filter) {
        // Get all agent-type entities
        const entities = await this.list({ type: ElementType.ENTITY });
        // Filter to only those with valid agent metadata and entityType 'agent'
        let agents = entities.filter((e) => e.entityType === EntityTypeValue.AGENT && isAgentEntity(e));
        // Apply additional filters
        if (filter) {
            if (filter.role !== undefined) {
                agents = agents.filter((a) => {
                    const meta = getAgentMetadata(a);
                    return meta?.agentRole === filter.role;
                });
            }
            if (filter.workerMode !== undefined) {
                agents = agents.filter((a) => {
                    const meta = getAgentMetadata(a);
                    return meta?.agentRole === 'worker' && meta.workerMode === filter.workerMode;
                });
            }
            if (filter.stewardFocus !== undefined) {
                agents = agents.filter((a) => {
                    const meta = getAgentMetadata(a);
                    return meta?.agentRole === 'steward' && meta.stewardFocus === filter.stewardFocus;
                });
            }
            if (filter.sessionStatus !== undefined) {
                agents = agents.filter((a) => {
                    const meta = getAgentMetadata(a);
                    return meta?.sessionStatus === filter.sessionStatus;
                });
            }
            if (filter.reportsTo !== undefined) {
                agents = agents.filter((a) => a.reportsTo === filter.reportsTo);
            }
            if (filter.hasSession !== undefined) {
                agents = agents.filter((a) => {
                    const meta = getAgentMetadata(a);
                    return (meta?.sessionId !== undefined) === filter.hasSession;
                });
            }
        }
        return agents;
    }
    async getAgentsByRole(role) {
        return this.listAgents({ role });
    }
    async getAvailableWorkers() {
        const workers = await this.listAgents({ role: 'worker' });
        return workers.filter((w) => {
            const meta = getAgentMetadata(w);
            const status = meta?.sessionStatus;
            return status === 'idle' || status === undefined;
        });
    }
    async getStewards() {
        return this.listAgents({ role: 'steward' });
    }
    async getDirector() {
        const directors = await this.listAgents({ role: 'director' });
        return directors[0];
    }
    // ----------------------------------------
    // Agent Session Management
    // ----------------------------------------
    async updateAgentSession(entityId, sessionId, status) {
        const agent = await this.getAgent(entityId);
        if (!agent) {
            throw new Error(`Agent not found: ${entityId}`);
        }
        const currentMeta = getAgentMetadata(agent);
        if (!currentMeta) {
            throw new Error(`Entity is not an agent: ${entityId}`);
        }
        const updatedAgentMeta = {
            ...currentMeta,
            sessionId,
            sessionStatus: status,
            lastActivityAt: createTimestamp(),
        };
        const updated = await this.update(entityId, {
            metadata: { ...agent.metadata, [AGENT_META_KEY]: updatedAgentMeta },
        });
        return updated;
    }
    async getAgentChannel(entityId) {
        const agent = await this.getAgent(entityId);
        if (!agent) {
            return undefined;
        }
        const meta = getAgentMetadata(agent);
        return meta?.channelId;
    }
    // ----------------------------------------
    // Orchestrator Task Metadata
    // ----------------------------------------
    async setTaskOrchestratorMeta(taskId, meta) {
        const task = await this.get(taskId);
        if (!task) {
            throw new Error(`Task not found: ${taskId}`);
        }
        const newMetadata = setOrchestratorTaskMeta(task.metadata, meta);
        return this.update(taskId, { metadata: newMetadata });
    }
    async updateTaskOrchestratorMeta(taskId, updates) {
        const task = await this.get(taskId);
        if (!task) {
            throw new Error(`Task not found: ${taskId}`);
        }
        const newMetadata = updateOrchestratorTaskMeta(task.metadata, updates);
        return this.update(taskId, { metadata: newMetadata });
    }
    async getTaskOrchestratorMeta(taskId) {
        const task = await this.get(taskId);
        if (!task) {
            return undefined;
        }
        return getOrchestratorTaskMeta(task.metadata);
    }
    async assignTaskToAgent(taskId, agentId, options) {
        const task = await this.get(taskId);
        if (!task) {
            throw new Error(`Task not found: ${taskId}`);
        }
        const agent = await this.getAgent(agentId);
        if (!agent) {
            throw new Error(`Agent not found: ${agentId}`);
        }
        // Generate branch and worktree names if not provided
        const slug = createSlugFromTitle(task.title);
        const branch = options?.branch ?? generateBranchName(agent.name, taskId, slug);
        const worktree = options?.worktree ?? generateWorktreePath(agent.name, slug);
        // Update task with assignee (and status if markAsStarted)
        const updates = { assignee: agentId };
        if (options?.markAsStarted) {
            updates.status = 'in_progress';
        }
        await this.update(taskId, updates);
        // Set orchestrator metadata
        return this.setTaskOrchestratorMeta(taskId, {
            assignedAgent: agentId,
            branch,
            worktree,
            sessionId: options?.sessionId,
            startedAt: createTimestamp(),
        });
    }
    // ----------------------------------------
    // Private Helpers
    // ----------------------------------------
    /**
     * Creates a dedicated channel for an agent
     *
     * @param agentName - The name of the agent (used for channel naming)
     * @param agentId - The ID of the agent entity (used for membership)
     * @param createdBy - The entity that created the agent (will be a channel member)
     * @returns The created channel
     */
    async createAgentChannel(agentName, agentId, createdBy) {
        // Look up the creator entity name for the channel display name
        const creatorEntity = await this.get(createdBy);
        const creatorName = creatorEntity?.name ?? 'operator';
        // Create a direct channel between the agent and the operator
        const channel = await createDirectChannel({
            entityA: createdBy, // Operator (el-0000)
            entityB: agentId, // The new agent
            entityAName: creatorName,
            entityBName: agentName,
            createdBy: createdBy,
            tags: ['agent-channel'],
            metadata: {
                agentId,
                agentName,
                purpose: 'Agent direct messaging channel',
            },
        });
        // Save the channel
        const savedChannel = await this.create(channel);
        return savedChannel;
    }
    /**
     * Updates an agent's metadata
     */
    async updateAgentMetadata(agentId, updates) {
        const agent = await this.getAgent(agentId);
        if (!agent) {
            throw new Error(`Agent not found: ${agentId}`);
        }
        const currentMeta = getAgentMetadata(agent) ?? {};
        const updatedMeta = { ...currentMeta, ...updates };
        const updated = await this.update(agentId, { metadata: { ...agent.metadata, [AGENT_META_KEY]: updatedMeta } });
        return updated;
    }
}
// ============================================================================
// Factory Function
// ============================================================================
/**
 * Creates an OrchestratorAPI instance
 */
export function createOrchestratorAPI(backend) {
    return new OrchestratorAPIImpl(backend);
}
//# sourceMappingURL=orchestrator-api.js.map