import fs from 'node:fs';
import path from 'node:path';
import { parse as parseYaml } from 'yaml';
function scanMarkdownLinks(filePath) {
    try {
        const content = fs.readFileSync(filePath, 'utf-8');
        const links = [];
        // Match markdown links [text](path) and wikilinks [[path]]
        const mdLinkRegex = /\[([^\]]*)\]\(([^)]+\.md)\)/g;
        const wikiLinkRegex = /\[\[([^\]]+\.md)\]\]/g;
        let match;
        while ((match = mdLinkRegex.exec(content)) !== null) {
            links.push(match[2]);
        }
        while ((match = wikiLinkRegex.exec(content)) !== null) {
            links.push(match[1]);
        }
        return links;
    }
    catch {
        return [];
    }
}
function buildGraph(squadsDir) {
    const nodes = [];
    const edges = [];
    const nodeIds = new Set();
    const skillUsers = new Map(); // skill -> node ids that use it
    const memoryRefs = new Map(); // memory path -> node ids that ref it
    if (!fs.existsSync(squadsDir))
        return { nodes, edges };
    const entries = fs.readdirSync(squadsDir, { withFileTypes: true });
    for (const entry of entries) {
        if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name.startsWith('_'))
            continue;
        const squadDir = path.join(squadsDir, entry.name);
        const yamlPath = path.join(squadDir, 'squad.yaml');
        if (!fs.existsSync(yamlPath))
            continue;
        let parsed;
        try {
            parsed = parseYaml(fs.readFileSync(yamlPath, 'utf-8'));
        }
        catch {
            continue;
        }
        const s = parsed?.squad;
        if (!s)
            continue;
        const squadCode = s.code || entry.name;
        const squadId = `squad:${squadCode}`;
        // Squad node
        nodes.push({ id: squadId, label: s.name || squadCode, type: 'squad' });
        nodeIds.add(squadId);
        // Skills
        const skills = Array.isArray(s.skills) ? s.skills : [];
        for (const skill of skills) {
            const skillId = `skill:${skill}`;
            if (!nodeIds.has(skillId)) {
                nodes.push({ id: skillId, label: skill, type: 'skill' });
                nodeIds.add(skillId);
            }
            edges.push({ source: squadId, target: skillId, type: 'uses_skill' });
            if (!skillUsers.has(skill))
                skillUsers.set(skill, []);
            skillUsers.get(skill).push(squadId);
        }
        // Memory files
        const memoryDir = path.join(squadDir, '_memory');
        if (fs.existsSync(memoryDir)) {
            const memFiles = fs.readdirSync(memoryDir).filter((f) => f.endsWith('.md'));
            for (const mf of memFiles) {
                const memId = `memory:${squadCode}/${mf}`;
                nodes.push({ id: memId, label: mf.replace('.md', ''), type: 'memory', squad: squadCode });
                nodeIds.add(memId);
                edges.push({ source: squadId, target: memId, type: 'has_memory' });
                const memPath = `_memory/${mf}`;
                if (!memoryRefs.has(memPath))
                    memoryRefs.set(memPath, []);
                memoryRefs.get(memPath).push(squadId);
            }
        }
        // Shared memory refs (company, preferences)
        for (const ref of ['company', 'preferences', 'memory']) {
            const refPath = s[ref];
            if (typeof refPath === 'string' && refPath.includes('_expxagents/')) {
                const sharedMemId = `memory:shared:${path.basename(refPath, '.md')}`;
                if (!nodeIds.has(sharedMemId)) {
                    nodes.push({ id: sharedMemId, label: path.basename(refPath, '.md'), type: 'memory' });
                    nodeIds.add(sharedMemId);
                }
                edges.push({ source: squadId, target: sharedMemId, type: 'shared_memory' });
            }
        }
        // Agents
        const agents = Array.isArray(s.agents) ? s.agents : [];
        const agentMap = new Map();
        for (const agent of agents) {
            const agentId = `agent:${squadCode}/${agent.id}`;
            nodes.push({ id: agentId, label: agent.name || agent.id, type: 'agent', squad: squadCode });
            nodeIds.add(agentId);
            agentMap.set(agent.id, agentId);
            edges.push({ source: squadId, target: agentId, type: 'contains' });
            // Agent-level skills from frontmatter
            if (agent.prompt) {
                const promptPath = path.join(squadDir, agent.prompt);
                if (fs.existsSync(promptPath)) {
                    try {
                        const content = fs.readFileSync(promptPath, 'utf-8');
                        const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
                        if (fmMatch) {
                            const fm = parseYaml(fmMatch[1]);
                            if (Array.isArray(fm?.skills)) {
                                for (const sk of fm.skills) {
                                    const skId = `skill:${sk}`;
                                    if (!nodeIds.has(skId)) {
                                        nodes.push({ id: skId, label: sk, type: 'skill' });
                                        nodeIds.add(skId);
                                    }
                                    edges.push({ source: agentId, target: skId, type: 'uses_skill' });
                                    if (!skillUsers.has(sk))
                                        skillUsers.set(sk, []);
                                    skillUsers.get(sk).push(agentId);
                                }
                            }
                            // base_agent reference
                            if (fm?.base_agent) {
                                const baseId = `skill:base:${fm.base_agent}`;
                                if (!nodeIds.has(baseId)) {
                                    nodes.push({ id: baseId, label: fm.base_agent, type: 'skill' });
                                    nodeIds.add(baseId);
                                }
                                edges.push({ source: agentId, target: baseId, type: 'uses_skill', label: 'base_agent' });
                            }
                        }
                    }
                    catch {
                        // skip
                    }
                }
            }
        }
        // Pipeline connections
        const steps = s.pipeline?.steps;
        if (Array.isArray(steps)) {
            let prevAgentNodeId = null;
            for (const step of steps) {
                if (!step.agent)
                    continue;
                const agentNodeId = agentMap.get(step.agent);
                if (!agentNodeId)
                    continue;
                if (step.deliverFrom) {
                    const fromNodeId = agentMap.get(step.deliverFrom);
                    if (fromNodeId) {
                        edges.push({ source: fromNodeId, target: agentNodeId, type: 'delivers', label: step.label });
                    }
                }
                else if (prevAgentNodeId && prevAgentNodeId !== agentNodeId) {
                    edges.push({ source: prevAgentNodeId, target: agentNodeId, type: 'pipeline' });
                }
                prevAgentNodeId = agentNodeId;
            }
        }
    }
    // Add shared_skill edges between squads that use the same skills
    for (const [, users] of skillUsers) {
        if (users.length <= 1)
            continue;
        const squadUsers = users.filter((u) => u.startsWith('squad:'));
        for (let i = 0; i < squadUsers.length; i++) {
            for (let j = i + 1; j < squadUsers.length; j++) {
                const exists = edges.some((e) => e.type === 'shared_skill' && ((e.source === squadUsers[i] && e.target === squadUsers[j]) || (e.source === squadUsers[j] && e.target === squadUsers[i])));
                if (!exists) {
                    edges.push({ source: squadUsers[i], target: squadUsers[j], type: 'shared_skill' });
                }
            }
        }
    }
    return { nodes, edges };
}
export async function graphRoutes(app, opts) {
    const { squadsDir } = opts;
    app.get('/api/graph', { preHandler: [app.requireAuth] }, async () => {
        return buildGraph(squadsDir);
    });
}
export { buildGraph };
//# sourceMappingURL=graph-routes.js.map