/**
 * Graph Maintenance Service
 *
 * Proactive graph health analysis: aggregates lint issues, orphan entities,
 * stale entities, and broken wikilinks into a unified health report.
 * Designed to run periodically or on-demand, with results surfaced
 * in the dashboard digest.
 */
import { lintWorkspace } from './lint-service.js';
import { findOrphans } from './orphan-service.js';
// ── Configuration ─────────────────────────────────────────────────────
const STALE_THRESHOLD_DAYS = 30;
const MAX_ISSUES_PER_CATEGORY = 20;
// ── Service ───────────────────────────────────────────────────────────
export async function analyzeGraphHealth(manager) {
    const missingFields = await lintWorkspace(manager);
    const orphans = await findOrphansWrapped(manager);
    const stale = findStaleEntities(manager);
    const brokenLinks = findBrokenWikilinks(manager);
    let totalEntities = 0;
    for (const repo of manager.getContentRepoConfigs()) {
        try {
            const graph = manager.getGraph(repo.name);
            const all = graph.search({});
            totalEntities += all.length;
        }
        catch { /* skip */ }
    }
    return {
        generatedAt: new Date().toISOString(),
        summary: {
            totalEntities,
            totalIssues: missingFields.length + orphans.length + stale.length + brokenLinks.length,
            missingFields: missingFields.length,
            orphanEntities: orphans.length,
            staleEntities: stale.length,
            brokenWikilinks: brokenLinks.length,
        },
        missingFields: missingFields.slice(0, MAX_ISSUES_PER_CATEGORY),
        orphans: orphans.slice(0, MAX_ISSUES_PER_CATEGORY),
        stale: stale.slice(0, MAX_ISSUES_PER_CATEGORY),
        brokenLinks: brokenLinks.slice(0, MAX_ISSUES_PER_CATEGORY),
    };
}
// ── Orphan detection wrapper ──────────────────────────────────────────
async function findOrphansWrapped(manager) {
    try {
        const result = await findOrphans(manager, {});
        return result.orphans ?? [];
    }
    catch {
        return [];
    }
}
// ── Stale entity detection ────────────────────────────────────────────
function findStaleEntities(manager) {
    const stale = [];
    const now = Date.now();
    const thresholdMs = STALE_THRESHOLD_DAYS * 24 * 60 * 60 * 1000;
    for (const repo of manager.getContentRepoConfigs()) {
        let graph;
        try {
            graph = manager.getGraph(repo.name);
        }
        catch {
            continue;
        }
        const entities = graph.search({});
        for (const entity of entities) {
            const data = entity.data;
            const updatedAt = data?.updated_at;
            if (!updatedAt)
                continue;
            const updatedTime = new Date(updatedAt).getTime();
            if (isNaN(updatedTime))
                continue;
            const daysSince = Math.floor((now - updatedTime) / (24 * 60 * 60 * 1000));
            if (daysSince >= STALE_THRESHOLD_DAYS) {
                stale.push({
                    repo: repo.name,
                    entityType: data.entity_type ?? entity.entityType,
                    entityId: entity.id,
                    name: data.name ?? entity.id,
                    lastUpdated: updatedAt,
                    daysSinceUpdate: daysSince,
                });
            }
        }
    }
    // Sort by staleness (most stale first)
    stale.sort((a, b) => b.daysSinceUpdate - a.daysSinceUpdate);
    return stale;
}
// ── Broken wikilink detection ─────────────────────────────────────────
function findBrokenWikilinks(manager) {
    const broken = [];
    // Build a set of all known entity IDs across the workspace
    const allEntityIds = new Set();
    for (const repo of manager.getContentRepoConfigs()) {
        try {
            const graph = manager.getGraph(repo.name);
            const entities = graph.search({});
            for (const e of entities) {
                allEntityIds.add(e.id);
            }
        }
        catch {
            continue;
        }
    }
    // Check wikilinks in each entity
    for (const repo of manager.getContentRepoConfigs()) {
        let graph;
        try {
            graph = manager.getGraph(repo.name);
        }
        catch {
            continue;
        }
        const entities = graph.search({});
        for (const entity of entities) {
            const wikilinks = entity.document?.wikilinks ?? [];
            const data = entity.data;
            for (const link of wikilinks) {
                // Normalize: strip [[ ]] if present
                const target = link.replace(/^\[\[|\]\]$/g, '').trim();
                if (!target)
                    continue;
                if (!allEntityIds.has(target)) {
                    broken.push({
                        repo: repo.name,
                        entityType: data?.entity_type ?? entity.entityType,
                        entityId: entity.id,
                        name: data?.name ?? entity.id,
                        brokenLink: target,
                    });
                }
            }
        }
    }
    return broken;
}
//# sourceMappingURL=graph-maintenance.js.map