"use strict";
/**
 * docuflow doctor
 *
 * Diagnostic command that reports installed DocuFlow packages, MCP server
 * registrations, detected workflow, and actionable recommendations.
 *
 * Usage:
 *   docuflow doctor            # human-readable report
 *   docuflow doctor --json     # machine-readable JSON
 *   docuflow doctor --quiet    # recommendations only
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = run;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const node_os_1 = __importDefault(require("node:os"));
// ── Package version resolution ────────────────────────────────────────────────
function resolvePackageVersion(pkgName) {
    const candidates = [
        () => require.resolve(`${pkgName}/package.json`),
        () => node_path_1.default.resolve(__dirname, "../../../", pkgName.replace("@doquflow/", ""), "package.json"),
        () => node_path_1.default.resolve(__dirname, "../../", pkgName.replace("@doquflow/", ""), "package.json"),
    ];
    for (const c of candidates) {
        try {
            const pkgPath = c();
            const raw = node_fs_1.default.readFileSync(pkgPath, "utf8");
            return JSON.parse(raw).version;
        }
        catch { /* try next */ }
    }
    return null;
}
function getInstalledPackages() {
    const packages = ["@doquflow/cli", "@doquflow/core", "@doquflow/studio", "@doquflow/server"];
    return packages.map(name => ({ name, version: resolvePackageVersion(name) }));
}
function getClaudeDesktopConfigPath() {
    const p = process.platform;
    if (p === "darwin")
        return node_path_1.default.join(node_os_1.default.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
    if (p === "win32")
        return node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Roaming", "Claude", "claude_desktop_config.json");
    return node_path_1.default.join(node_os_1.default.homedir(), ".config", "Claude", "claude_desktop_config.json");
}
function getMcpRegistrations(projectPath) {
    const results = [];
    // Project-level .mcp.json
    const projectMcp = node_path_1.default.join(projectPath, ".mcp.json");
    try {
        const cfg = JSON.parse(node_fs_1.default.readFileSync(projectMcp, "utf8"));
        const entry = cfg.mcpServers?.docuflow;
        results.push({ source: ".mcp.json", registered: !!entry, command: entry?.command, args: entry?.args });
    }
    catch {
        results.push({ source: ".mcp.json", registered: false });
    }
    // Claude Desktop
    try {
        const cfg = JSON.parse(node_fs_1.default.readFileSync(getClaudeDesktopConfigPath(), "utf8"));
        const entry = cfg.mcpServers?.docuflow;
        results.push({ source: "Claude Desktop", registered: !!entry, command: entry?.command, args: entry?.args });
    }
    catch {
        results.push({ source: "Claude Desktop", registered: false });
    }
    // VS Code user MCP
    const vscodePath = process.platform === "darwin"
        ? node_path_1.default.join(node_os_1.default.homedir(), "Library", "Application Support", "Code", "User", "mcp.json")
        : process.platform === "win32"
            ? node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Roaming", "Code", "User", "mcp.json")
            : node_path_1.default.join(node_os_1.default.homedir(), ".config", "Code", "User", "mcp.json");
    try {
        const cfg = JSON.parse(node_fs_1.default.readFileSync(vscodePath, "utf8"));
        const entry = cfg.servers?.docuflow;
        results.push({ source: "VS Code (user)", registered: !!entry, command: entry?.command, args: entry?.args });
    }
    catch {
        results.push({ source: "VS Code (user)", registered: false });
    }
    // Copilot CLI
    const copilotPath = node_path_1.default.join(node_os_1.default.homedir(), ".copilot", "mcp-config.json");
    try {
        const cfg = JSON.parse(node_fs_1.default.readFileSync(copilotPath, "utf8"));
        const entry = cfg.mcpServers?.docuflow;
        results.push({ source: "Copilot CLI", registered: !!entry, command: entry?.command, args: entry?.args });
    }
    catch {
        results.push({ source: "Copilot CLI", registered: false });
    }
    return results;
}
function detectWorkflow(projectPath) {
    const docuDir = node_path_1.default.join(projectPath, ".docuflow");
    const hasDocuflow = node_fs_1.default.existsSync(docuDir);
    let hasSources = false;
    let hasSpecs = false;
    let hasQueryUsage = false;
    let watchRunning = false;
    if (hasDocuflow) {
        try {
            hasSources = node_fs_1.default.readdirSync(node_path_1.default.join(docuDir, "sources")).some(f => f.endsWith(".md"));
        }
        catch { /* */ }
        try {
            hasSpecs = node_fs_1.default.readdirSync(node_path_1.default.join(docuDir, "specs")).some(f => f.endsWith(".md"));
        }
        catch { /* */ }
        try {
            const log = node_fs_1.default.readFileSync(node_path_1.default.join(docuDir, "log.md"), "utf8");
            hasQueryUsage = /query-wiki|queryWiki/i.test(log);
        }
        catch { /* */ }
        // Check watch daemon PID file
        const pidFile = node_path_1.default.join(docuDir, "watch.pid.json");
        try {
            const data = JSON.parse(node_fs_1.default.readFileSync(pidFile, "utf8"));
            try {
                process.kill(data.pid, 0);
                watchRunning = true;
            }
            catch { /* not running */ }
        }
        catch { /* no pid file */ }
    }
    return { hasDocuflow, hasSources, hasSpecs, hasQueryUsage, watchRunning };
}
function getHealthSummary(projectPath) {
    const docuDir = node_path_1.default.join(projectPath, ".docuflow");
    const byCategory = { entities: 0, concepts: 0, timelines: 0, syntheses: 0 };
    for (const cat of Object.keys(byCategory)) {
        try {
            const files = node_fs_1.default.readdirSync(node_path_1.default.join(docuDir, "wiki", cat));
            byCategory[cat] = files.filter(f => f.endsWith(".md")).length;
        }
        catch { /* dir may not exist */ }
    }
    const totalPages = Object.values(byCategory).reduce((a, b) => a + b, 0);
    let lastUpdate = "never";
    try {
        const log = node_fs_1.default.readFileSync(node_path_1.default.join(docuDir, "log.md"), "utf8");
        const match = log.match(/\[(\d{4}-\d{2}-\d{2}[^\]]*)\]/);
        if (match) {
            const d = new Date(match[1]);
            if (!isNaN(d.getTime())) {
                const diffH = Math.floor((Date.now() - d.getTime()) / 3_600_000);
                lastUpdate = diffH < 1 ? "just now" : diffH < 24 ? `${diffH}h ago` : `${Math.floor(diffH / 24)}d ago`;
            }
        }
    }
    catch { /* */ }
    return { totalPages, byCategory, lastUpdate, healthScore: null };
}
function buildRecommendations(packages, registrations, workflow, health) {
    const recs = [];
    const pkgMap = Object.fromEntries(packages.map(p => [p.name, p.version]));
    // Core/Studio installed?
    if (!pkgMap["@doquflow/core"] && !pkgMap["@doquflow/studio"]) {
        if (pkgMap["@doquflow/cli"]) {
            // CLI installs core+studio transitively — this just means they weren't directly required
        }
        else {
            recs.push({ severity: "error", message: "No DocuFlow packages detected", action: 'Run: npm i -g @doquflow/cli' });
        }
    }
    // Suggest minimal install if only using query/ingest
    const hasCli = !!pkgMap["@doquflow/cli"];
    const hasCore = !!pkgMap["@doquflow/core"];
    const hasStudio = !!pkgMap["@doquflow/studio"];
    if (hasCli && !workflow.watchRunning && !workflow.hasQueryUsage && hasCore && !hasStudio) {
        recs.push({ severity: "info", message: "You only use core tools (query/ingest)", action: "Consider: npm i -g @doquflow/core for a smaller install" });
    }
    // Not initialised
    if (!workflow.hasDocuflow) {
        recs.push({ severity: "error", message: ".docuflow/ not found in this directory", action: "Run: docuflow init" });
        return recs; // no point checking further
    }
    // No MCP registration anywhere
    const anyRegistered = registrations.some(r => r.registered);
    if (!anyRegistered) {
        recs.push({ severity: "error", message: "DocuFlow is not registered with any MCP host", action: "Run: docuflow init (re-run is safe)" });
    }
    // No sources
    if (!workflow.hasSources) {
        recs.push({ severity: "warn", message: "No source documents in .docuflow/sources/", action: "Drop a markdown file there, then run: docuflow ingest" });
    }
    // Empty wiki
    if (health.totalPages === 0 && workflow.hasSources) {
        recs.push({ severity: "warn", message: "Wiki is empty but sources exist", action: "Run: docuflow rewiki" });
    }
    // Old @doquflow/server pointing at dist/index.js instead of dist/mcp/index.js
    for (const reg of registrations.filter(r => r.registered)) {
        const argStr = reg.args?.join(" ") ?? "";
        if (argStr.includes("@doquflow/server") && argStr.includes("dist/index.js")) {
            recs.push({ severity: "info", message: `${reg.source}: MCP registration uses old server path`, action: "Run: docuflow init to refresh to @doquflow/studio/dist/mcp/index.js" });
        }
    }
    if (recs.length === 0) {
        recs.push({ severity: "info", message: "Everything looks good", action: "No action needed" });
    }
    return recs;
}
// ── Output formatters ─────────────────────────────────────────────────────────
function icon(severity) {
    return severity === "error" ? "🔴" : severity === "warn" ? "🟡" : "🟢";
}
function printHuman(packages, registrations, workflow, health, recs, quiet) {
    if (!quiet) {
        // 1. Installed packages
        console.log("\n── 1. Installed packages ────────────────────────────────");
        for (const pkg of packages) {
            const mark = pkg.version ? "✓" : "✗";
            const ver = pkg.version ?? "not found";
            console.log(`  ${mark}  ${pkg.name.padEnd(22)} ${ver}`);
        }
        // 2. MCP registrations
        console.log("\n── 2. MCP server registrations ──────────────────────────");
        for (const reg of registrations) {
            const mark = reg.registered ? "✓" : "·";
            const detail = reg.registered && reg.command
                ? `  ${reg.command} ${(reg.args ?? []).slice(-1)[0] ?? ""}`
                : "";
            console.log(`  ${mark}  ${reg.source.padEnd(18)}${reg.registered ? "registered" : "not registered"}${detail}`);
        }
        // 3. Workflow detection
        console.log("\n── 3. Workflow detection ─────────────────────────────────");
        console.log(`  ${workflow.hasDocuflow ? "✓" : "✗"}  .docuflow/ initialised`);
        console.log(`  ${workflow.hasSources ? "✓" : "·"}  source documents present`);
        console.log(`  ${workflow.hasSpecs ? "✓" : "·"}  specs written`);
        console.log(`  ${workflow.hasQueryUsage ? "✓" : "·"}  query_wiki usage in logs`);
        console.log(`  ${workflow.watchRunning ? "✓" : "·"}  watch daemon running`);
        // 4. Health summary
        console.log("\n── 5. Wiki health summary ───────────────────────────────");
        console.log(`  Pages total:    ${health.totalPages}`);
        if (health.totalPages > 0) {
            for (const [cat, n] of Object.entries(health.byCategory)) {
                if (n > 0)
                    console.log(`    ${cat.padEnd(14)} ${n}`);
            }
        }
        console.log(`  Last updated:   ${health.lastUpdate}`);
        console.log("");
    }
    // 4. Recommendations (always shown)
    if (!quiet)
        console.log("── 4. Recommendations ───────────────────────────────────");
    for (const rec of recs) {
        console.log(`  ${icon(rec.severity)}  ${rec.message}`);
        console.log(`     → ${rec.action}`);
    }
    console.log("");
}
// ── Entry point ───────────────────────────────────────────────────────────────
async function run(opts = {}) {
    const projectPath = process.cwd();
    const packages = getInstalledPackages();
    const registrations = getMcpRegistrations(projectPath);
    const workflow = detectWorkflow(projectPath);
    const health = getHealthSummary(projectPath);
    const recs = buildRecommendations(packages, registrations, workflow, health);
    if (opts.json) {
        console.log(JSON.stringify({ packages, registrations, workflow, health, recommendations: recs }, null, 2));
        return;
    }
    printHuman(packages, registrations, workflow, health, recs, opts.quiet ?? false);
}
