import { createMcpServer, mcpTool } from '@inharness/agent-adapters';
import { z } from 'zod';
import { DomainError } from '../services/tags.js';
const RELATION_ENUM = z.enum([
    'depends_on',
    'implements',
    'extends',
    'example_of',
    'contradicts',
    'see_also',
]);
export function createDependencyToolsServer(deps) {
    const ok = (payload) => ({
        content: [{ type: 'text', text: JSON.stringify(payload) }],
    });
    const fail = (err) => {
        const code = err instanceof DomainError ? err.code : 'INTERNAL';
        const message = err instanceof Error ? err.message : String(err);
        return {
            content: [{ type: 'text', text: JSON.stringify({ error: message, code }) }],
            isError: true,
        };
    };
    const resolveAnchor = (args) => {
        if (typeof args.anchor === 'string' && args.anchor)
            return args.anchor;
        if (typeof args.pagePath === 'string' && args.pagePath) {
            const list = deps.sectionsService.listByPage(args.pagePath);
            if (typeof args.heading === 'string' && args.heading) {
                const needle = args.heading.trim().toLowerCase();
                const match = list.find((s) => s.headingText.trim().toLowerCase() === needle ||
                    s.headingPath.toLowerCase().endsWith(needle));
                return match?.anchor ?? null;
            }
            if (list.length === 1)
                return list[0].anchor;
        }
        return null;
    };
    const impactAnalysis = mcpTool('impact_analysis', 'Analyze which sections depend on a target section. Use before editing a section to see what will need review.', {
        anchor: z.string().optional().describe('Target section anchor ID'),
        pagePath: z.string().optional(),
        heading: z.string().optional(),
    }, async (args) => {
        try {
            const anchor = resolveAnchor(args);
            if (!anchor)
                return fail(new DomainError('NOT_FOUND', 'could not resolve target anchor'));
            const result = deps.dependenciesService.impactAnalysis(anchor);
            if (!result)
                return fail(new DomainError('NOT_FOUND', `section '${anchor}' not found`));
            return ok(result);
        }
        catch (err) {
            return fail(err);
        }
    });
    const propagateChange = mcpTool('propagate_change', 'List all dependencies whose target is the given anchor and mark them as stale — useful after rewriting a section.', { anchor: z.string() }, async (args) => {
        try {
            const result = deps.dependenciesService.impactAnalysis(String(args.anchor));
            return ok(result ?? { targetAnchor: args.anchor, dependents: [] });
        }
        catch (err) {
            return fail(err);
        }
    });
    const listStaleDependencies = mcpTool('list_stale_dependencies', 'List dependencies with status stale or broken — those requiring review.', { pagePath: z.string().optional() }, async (args) => {
        try {
            const stale = deps.dependenciesService.list({
                status: 'stale',
                pagePath: args.pagePath,
            });
            const broken = deps.dependenciesService.list({
                status: 'broken',
                pagePath: args.pagePath,
            });
            return ok({ stale, broken, counts: { stale: stale.length, broken: broken.length } });
        }
        catch (err) {
            return fail(err);
        }
    });
    const dependencyGraph = mcpTool('dependency_graph', 'Return full dependency graph: nodes (sections, each flagged isolated when it has no edges) + edges (dependencies) + detected cycles.', { scope: z.string().optional().describe('Optional pagePath prefix to scope the graph') }, async (args) => {
        try {
            const graph = deps.dependenciesService.graph({
                pagePathPrefix: typeof args.scope === 'string' ? args.scope : undefined,
                includeIsolated: true,
            });
            return ok(graph);
        }
        catch (err) {
            return fail(err);
        }
    });
    const sectionIndex = mcpTool('section_index', 'Read-only index of sections (anchors + headings). Useful to pick targets for dependencies.', { pagePath: z.string().optional() }, async (args) => {
        try {
            const list = typeof args.pagePath === 'string' && args.pagePath
                ? deps.sectionsService.listByPage(args.pagePath)
                : deps.sectionsService.list();
            return ok({
                sections: list.map((s) => ({
                    anchor: s.anchor,
                    pagePath: s.pagePath,
                    headingText: s.headingText,
                    headingPath: s.headingPath,
                    headingLevel: s.headingLevel,
                })),
                total: list.length,
            });
        }
        catch (err) {
            return fail(err);
        }
    });
    const createDependency = mcpTool('create_dependency', 'Create a typed dependency (section → section). Idempotent on (source, target, relation). Also inserts a <section_ref/> XML tag in the source section.', {
        sourceAnchor: z.string(),
        targetAnchor: z.string(),
        relation: RELATION_ENUM,
        description: z.string().optional(),
    }, async (args) => {
        try {
            const dep = deps.dependenciesService.create({
                sourceAnchor: String(args.sourceAnchor),
                targetAnchor: String(args.targetAnchor),
                relation: args.relation,
                description: args.description,
            });
            await deps.referencesService.insertSectionRef(dep.sourceAnchor, dep.targetAnchor, dep.relation);
            return ok({ created: true, id: dep.id, dependency: dep });
        }
        catch (err) {
            return fail(err);
        }
    });
    const removeDependency = mcpTool('remove_dependency', 'Remove a dependency edge and its <section_ref/> tag from the source markdown.', {
        sourceAnchor: z.string(),
        targetAnchor: z.string(),
        relation: RELATION_ENUM,
    }, async (args) => {
        try {
            const all = deps.dependenciesService.list();
            const match = all.find((d) => d.sourceAnchor === args.sourceAnchor &&
                d.targetAnchor === args.targetAnchor &&
                d.relation === args.relation);
            if (!match)
                return fail(new DomainError('NOT_FOUND', 'dependency not found'));
            deps.dependenciesService.remove(match.id);
            await deps.referencesService.removeSectionRef(String(args.sourceAnchor), String(args.targetAnchor), args.relation);
            return ok({ removed: true });
        }
        catch (err) {
            return fail(err);
        }
    });
    const verifyDependency = mcpTool('verify_dependency', 'Mark a dependency as active (verified) — aligns last_verified_hash with current target content_hash.', {
        sourceAnchor: z.string(),
        targetAnchor: z.string(),
        relation: RELATION_ENUM,
    }, async (args) => {
        try {
            const all = deps.dependenciesService.list();
            const match = all.find((d) => d.sourceAnchor === args.sourceAnchor &&
                d.targetAnchor === args.targetAnchor &&
                d.relation === args.relation);
            if (!match)
                return fail(new DomainError('NOT_FOUND', 'dependency not found'));
            const verified = deps.dependenciesService.verify(match.id);
            return ok({ verified: true, dependency: verified });
        }
        catch (err) {
            return fail(err);
        }
    });
    const suggestDependencies = mcpTool('suggest_dependencies', 'Heuristic suggestions for which sections could be linked as dependencies (shared entities, keyword overlap).', {
        anchor: z.string().optional(),
        pagePath: z.string().optional(),
        heading: z.string().optional(),
    }, async (args) => {
        try {
            const anchor = resolveAnchor(args);
            if (!anchor)
                return fail(new DomainError('NOT_FOUND', 'could not resolve anchor'));
            const suggestions = deps.dependenciesService.suggest(anchor);
            return ok({ anchor, suggestions });
        }
        catch (err) {
            return fail(err);
        }
    });
    return createMcpServer({
        name: 'dependency-tools',
        tools: [
            impactAnalysis,
            propagateChange,
            listStaleDependencies,
            dependencyGraph,
            sectionIndex,
            createDependency,
            removeDependency,
            verifyDependency,
            suggestDependencies,
        ],
    });
}
//# sourceMappingURL=dependency-tools.js.map