"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateFlow = exports.flowsEndpoint = void 0;
const crypto_1 = require("crypto");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const zod_1 = require("zod");
const get_step_language_1 = require("./get-step-language");
const guards_1 = require("./guards");
const flows_stream_1 = require("./streams/flows-stream");
const flowsEndpoint = (lockedData, app) => {
    const flowsStream = lockedData.createStream({
        filePath: '__motia.flows',
        hidden: true,
        config: {
            name: '__motia.flows',
            schema: zod_1.z.object({ id: zod_1.z.string(), name: zod_1.z.string() }),
            baseConfig: { storageType: 'custom', factory: () => new flows_stream_1.FlowsStream(lockedData) },
        },
    }, { disableTypeCreation: true })();
    lockedData.on('flow-created', (flow) => flowsStream.create(flow, { id: flow, name: flow }));
    lockedData.on('flow-updated', (flow) => flowsStream.update(flow, { id: flow, name: flow }));
    lockedData.on('flow-removed', (flow) => flowsStream.delete(flow));
    app.get('/flows/:id', async (req, res) => {
        const flowId = req.params.id;
        const flow = lockedData.flows[flowId];
        if (!flow) {
            res.status(404).send({ error: 'Flow not found' });
        }
        else {
            res.status(200).send((0, exports.generateFlow)(flowId, flow.steps));
        }
    });
};
exports.flowsEndpoint = flowsEndpoint;
// Helper functions
const getNodeComponentPath = (filePath) => {
    const filePathWithoutExtension = filePath.replace(/\.[^/.]+$/, '');
    const tsxPath = filePathWithoutExtension + '.tsx';
    return fs_1.default.existsSync(tsxPath) ? tsxPath : undefined;
};
const getRelativePath = (filePath) => {
    const baseDir = process.cwd();
    return path_1.default.relative(baseDir, filePath);
};
const createEdge = (sourceId, targetId, topic, label, variant, conditional) => ({
    id: `${sourceId}-${targetId}`,
    source: sourceId,
    target: targetId,
    data: {
        variant,
        label,
        topic,
        labelVariant: conditional ? 'conditional' : 'default',
    },
});
const processEmit = (emit) => {
    const isString = typeof emit === 'string';
    return {
        topic: isString ? emit : emit.topic,
        label: isString ? undefined : emit.label,
        conditional: isString ? undefined : emit.conditional,
    };
};
const createEdgesForEmits = (sourceStep, targetSteps, emits, variant) => {
    const edges = [];
    emits.forEach((emit) => {
        const { topic, label, conditional } = processEmit(emit);
        targetSteps.forEach((targetStep) => {
            if (targetStep.subscribes?.includes(topic)) {
                edges.push(createEdge(sourceStep.id, targetStep.id, topic, label, variant, conditional));
            }
        });
    });
    return edges;
};
const createBaseStepResponse = (step, id) => ({
    id,
    name: step.config.name,
    description: step.config.description,
    nodeComponentPath: getNodeComponentPath(step.filePath),
    filePath: getRelativePath(step.filePath),
    language: (0, get_step_language_1.getStepLanguage)(step.filePath),
});
const createApiStepResponse = (step, id) => {
    if (!(0, guards_1.isApiStep)(step)) {
        throw new Error('Attempted to create API step response with non-API step');
    }
    return {
        ...createBaseStepResponse(step, id),
        type: 'api',
        emits: step.config.emits,
        virtualEmits: step.config.virtualEmits ?? [],
        subscribes: step.config.virtualSubscribes ?? undefined,
        action: 'webhook',
        webhookUrl: `${step.config.method} ${step.config.path}`,
        bodySchema: step.config.bodySchema ?? undefined,
    };
};
const createEventStepResponse = (step, id) => {
    if (!(0, guards_1.isEventStep)(step)) {
        throw new Error('Attempted to create Event step response with non-Event step');
    }
    return {
        ...createBaseStepResponse(step, id),
        type: 'event',
        emits: step.config.emits,
        virtualEmits: step.config.virtualEmits ?? [],
        subscribes: step.config.subscribes,
    };
};
const createNoopStepResponse = (step, id) => {
    if (!(0, guards_1.isNoopStep)(step)) {
        throw new Error('Attempted to create Noop step response with non-Noop step');
    }
    return {
        ...createBaseStepResponse(step, id),
        type: 'noop',
        emits: [],
        virtualEmits: step.config.virtualEmits,
        subscribes: step.config.virtualSubscribes,
    };
};
const createCronStepResponse = (step, id) => {
    if (!(0, guards_1.isCronStep)(step)) {
        throw new Error('Attempted to create Cron step response with non-Cron step');
    }
    return {
        ...createBaseStepResponse(step, id),
        type: 'cron',
        emits: step.config.emits,
        cronExpression: step.config.cron,
    };
};
const createStepResponse = (step) => {
    const id = (0, crypto_1.randomUUID)();
    if ((0, guards_1.isApiStep)(step))
        return createApiStepResponse(step, id);
    if ((0, guards_1.isEventStep)(step))
        return createEventStepResponse(step, id);
    if ((0, guards_1.isNoopStep)(step))
        return createNoopStepResponse(step, id);
    if ((0, guards_1.isCronStep)(step))
        return createCronStepResponse(step, id);
    throw new Error(`Unknown step type for step: ${step.config.name}`);
};
const createEdgesForStep = (sourceStep, allSteps) => {
    const regularEdges = createEdgesForEmits(sourceStep, allSteps, sourceStep.emits, 'event');
    const virtualEdges = sourceStep.virtualEmits
        ? createEdgesForEmits(sourceStep, allSteps, sourceStep.virtualEmits, 'virtual')
        : [];
    return [...regularEdges, ...virtualEdges];
};
const generateFlow = (flowId, flowSteps) => {
    const steps = flowSteps.map(createStepResponse);
    const edges = steps.flatMap((step) => createEdgesForStep(step, steps));
    return { id: flowId, name: flowId, steps, edges };
};
exports.generateFlow = generateFlow;
