"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.mcpWorkflowPlanner = mcpWorkflowPlanner;
const hotmesh_1 = require("@hotmeshio/hotmesh");
const orchestrator_1 = require("../../../services/orchestrator");
const activities = __importStar(require("./activities"));
const { generatePlan, persistPlan, persistBuiltWorkflow, updateSetStatus, deploySetNamespaces, } = hotmesh_1.Durable.workflow.proxyActivities({
    activities,
    retry: {
        maximumAttempts: 3,
        backoffCoefficient: 2,
        maximumInterval: '10 seconds',
    },
});
/**
 * Plan mode workflow — decomposes a specification into N workflows,
 * builds them leaf-first by delegating to mcpWorkflowBuilder,
 * and deploys the resulting set.
 */
async function mcpWorkflowPlanner(envelope) {
    const { specification, setId, existingPlan, existingSchemas, } = envelope.data;
    const isAddition = !!existingPlan?.length;
    // 1. Generate plan from specification
    // When adding to an existing set, tell the planner what already exists
    let planSpec = specification;
    if (isAddition) {
        const existingNames = existingPlan.map(p => `- ${p.name} [${p.namespace}]: ${p.description}`).join('\n');
        const namespaces = [...new Set(existingPlan.map(p => p.namespace))];
        planSpec = `${specification}\n\n## Existing workflows in this set (do NOT recreate these):\n${existingNames}\n\n## Namespace constraint\nNew workflows MUST use namespace "${namespaces[0]}". This set already uses this namespace and all tools must be co-located.`;
    }
    const planResult = await generatePlan(planSpec);
    // 2. Offset build_order for additions so new items sort after existing ones
    const buildOrderOffset = isAddition
        ? Math.max(...existingPlan.map((_, i) => i), 0) + 1
        : 0;
    if (isAddition) {
        for (const item of planResult.workflows) {
            item.build_order += buildOrderOffset;
        }
    }
    // 2b. Persist plan — append to existing plan for additions
    if (isAddition) {
        // Fetch current plan from the set and merge
        const { getWorkflowSet } = await Promise.resolve().then(() => __importStar(require('../../../services/workflow-sets')));
        const currentSet = await getWorkflowSet(setId);
        const mergedPlan = [...(currentSet?.plan || []), ...planResult.workflows];
        const mergedNamespaces = [...new Set([
                ...(currentSet?.namespaces || []),
                ...planResult.workflows.map(w => w.namespace),
            ])];
        await persistPlan(setId, mergedPlan, mergedNamespaces);
    }
    else {
        await persistPlan(setId, planResult.workflows);
    }
    // 3. Build each workflow in dependency order (leaf-first)
    await updateSetStatus(setId, 'building');
    // Seed sibling schemas with existing workflows when adding to a set
    const builtWorkflows = (existingSchemas || []).map(s => ({
        ...s,
        id: '',
    }));
    for (const planItem of planResult.workflows) {
        // Build context for the builder: sibling schemas for composition wiring
        const siblingSchemas = builtWorkflows.map(w => ({
            name: w.name,
            input_schema: w.input_schema,
            output_schema: w.output_schema,
            graph_topic: w.graph_topic,
        }));
        // Delegate to the existing builder workflow
        const builderResult = await (0, orchestrator_1.executeLT)({
            workflowName: 'mcpWorkflowBuilder',
            args: [{
                    data: {
                        prompt: buildPromptForPlanItem(planItem, siblingSchemas),
                        tags: [],
                        composition_context: {
                            sibling_schemas: siblingSchemas,
                            dependencies: planItem.dependencies,
                            namespace: planItem.namespace,
                            requires_await: planItem.dependencies.some(dep => planResult.workflows.find(w => w.name === dep)?.namespace === planItem.namespace),
                        },
                    },
                    metadata: envelope.metadata,
                    lt: envelope.lt,
                }],
            taskQueue: 'long-tail-system',
        });
        const builderData = builderResult.data;
        const builderPrompt = buildPromptForPlanItem(planItem, siblingSchemas);
        // Persist the built workflow with set membership
        const yamlId = await persistBuiltWorkflow(setId, planItem, {
            name: builderData.name || planItem.name,
            description: builderData.description || planItem.description,
            yaml_content: builderData.yaml,
            input_schema: builderData.input_schema || {},
            output_schema: planItem.io_contract.output_schema || {},
            activity_manifest: builderData.activity_manifest || [],
            tags: builderData.tags || [],
            graph_topic: builderData.name || planItem.name,
            original_prompt: builderPrompt,
        });
        builtWorkflows.push({
            name: planItem.name,
            id: yamlId,
            input_schema: builderData.input_schema || {},
            output_schema: planItem.io_contract.output_schema || {},
            graph_topic: builderData.name || planItem.name,
        });
    }
    // 4. Deploy all namespaces
    await updateSetStatus(setId, 'deploying');
    const namespaces = [...new Set(planResult.workflows.map(w => w.namespace))];
    await deploySetNamespaces(namespaces);
    // 5. Mark complete
    await updateSetStatus(setId, 'completed');
    return {
        type: 'return',
        data: {
            title: planResult.plan_name,
            summary: `Built ${builtWorkflows.length} workflows across ${namespaces.length} namespace(s)`,
            set_id: setId,
            workflows: builtWorkflows,
            namespaces,
        },
    };
}
/**
 * Build a detailed prompt for the builder from a plan item.
 * Includes the workflow description, I/O contract, and dependency context.
 */
function buildPromptForPlanItem(planItem, siblingSchemas) {
    const parts = [planItem.description];
    // Add I/O contract
    if (planItem.io_contract.input_schema?.properties) {
        parts.push(`\nInputs: ${JSON.stringify(planItem.io_contract.input_schema, null, 2)}`);
    }
    if (planItem.io_contract.output_schema?.properties) {
        parts.push(`\nOutputs: ${JSON.stringify(planItem.io_contract.output_schema, null, 2)}`);
    }
    // Add dependency context
    if (planItem.dependencies.length > 0) {
        const depSchemas = siblingSchemas.filter(s => planItem.dependencies.includes(s.name));
        if (depSchemas.length > 0) {
            parts.push('\nThis workflow depends on the following sibling workflows:');
            for (const dep of depSchemas) {
                parts.push(`- ${dep.name} (topic: ${dep.graph_topic})`);
                parts.push(`  Input: ${JSON.stringify(dep.input_schema)}`);
                parts.push(`  Output: ${JSON.stringify(dep.output_schema)}`);
            }
        }
    }
    return parts.join('\n');
}
