/**
 * Resource inventory — the single chokepoint for "what does this agent@version
 * have" (RUSH-2238, parent RUSH-2236). Contract: cli/docs/specifications.md.
 *
 * One API answers four orthogonal questions per (agent, version, kind):
 *
 *   capable   — the agent+version feature flags allow the kind at all
 *   declared  — resources the DotAgents layers (project/user/system/extra)
 *               declare for sync
 *   onDisk    — resources actually installed in the version home filesystem
 *               (the installed-resource truth — NOT agents.yaml tracking data)
 *   wired     — resources the harness native config references so they fire
 *   unmanaged — onDisk − declared (orphans no layer declares anymore)
 *
 * Callers (inspect, doctor, view, sync) MUST come here rather than
 * re-implementing path joins or config parsing per command. Wiring parsing
 * currently covers the settings.json family (claude, droid,
 * muse), Grok hooks.json, and Kimi config.toml via checkVersionHookWiring.
 */
import { AGENTS } from './agents.js';
import { supports } from './capabilities.js';
import { listResources } from './resources.js';
import { checkVersionHookWiring, listHooksInVersionHome, listInstalledHooksWithScope, } from './hooks/install.js';
const IMPLEMENTED_KINDS = ['hooks'];
/**
 * The single inventory API. Harness-scoped (claude/codex/grok/kimi/droid) —
 * never a model or profile. Throws for kinds without an implementation rather
 * than returning a half-empty report that looks like truth.
 */
export function getResourceInventory(agent, version, kind, opts = {}) {
    if (!IMPLEMENTED_KINDS.includes(kind)) {
        // Fail loud at the boundary: the kind is part of the API shape but its
        // inventory lands with a RUSH-2236 follow-up — a silent empty report would
        // read as "nothing installed".
        throw new Error(`getResourceInventory: kind '${kind}' is not implemented yet (implemented: ${IMPLEMENTED_KINDS.join(', ')}; tracked under RUSH-2236)`);
    }
    return hooksInventory(agent, version, opts.cwd);
}
/**
 * On-disk hook listing for an explicit home (version home or effective home)
 * plus the project overlay — the one listing `getAgentResources` routes
 * through, so inspect/doctor/view share a single hooks source. Paths resolve
 * through the absolute-hooksDir-safe join (RUSH-2237), so grok/kimi version
 * homes list their real hooks.
 */
export function listOnDiskHooks(agent, opts = {}) {
    return listInstalledHooksWithScope(agent, opts.cwd ?? process.cwd(), { home: opts.home }).map((h) => ({
        name: h.name,
        path: h.path,
        source: h.scope,
        detail: h.dataFile,
    }));
}
function hooksInventory(agent, version, cwd) {
    const capable = supports(agent, 'hooks', version).ok;
    // Declared: the layered DotAgents repos are the declaration surface. Hook
    // script names carry their extension here ('git-guard.sh'); normalize to the
    // extension-less install name so declared/onDisk sets compare.
    const declared = listResources('hooks', cwd).map((r) => ({
        name: r.name.replace(/\.[^./]+$/, ''),
        path: r.path,
        source: r.source,
    }));
    // onDisk / wired need the version home; both are empty for an agent whose
    // hooks capability is gated off (unknown agents included — AGENTS lookup
    // would otherwise throw).
    let onDisk = [];
    let wired = [];
    let wiringSupported = false;
    let wiring;
    if (capable && AGENTS[agent]?.supportsHooks) {
        onDisk = listHooksInVersionHome(agent, version).map((e) => ({
            name: e.name,
            path: e.scriptPath,
            source: 'version-home',
            detail: e.dataFile,
        }));
        const report = checkVersionHookWiring(agent, version);
        wiring = report;
        // supported=true means the harness format is known; settingsUnparseable means
        // we could not trust the file. Only report wired counts when the parse succeeded —
        // otherwise inspect/doctor would render "wired 0" for corrupt configs (PR #2140 review).
        wiringSupported = report.supported && !report.settingsUnparseable;
        if (wiringSupported) {
            // One ref per hook, events folded into detail — a hook wired under
            // PreToolUse AND Stop is one inventory entry, not two.
            const eventsByName = new Map();
            const commandByName = new Map();
            for (const issue of report.wired) {
                let events = eventsByName.get(issue.name);
                if (!events) {
                    events = new Set();
                    eventsByName.set(issue.name, events);
                    commandByName.set(issue.name, issue.command);
                }
                events.add(issue.event);
            }
            wired = [...eventsByName.entries()]
                .sort(([a], [b]) => a.localeCompare(b))
                .map(([name, events]) => ({
                name,
                path: commandByName.get(name),
                source: 'native-config',
                detail: [...events].sort().join('/'),
            }));
        }
    }
    const declaredNames = new Set(declared.map((r) => r.name));
    const unmanaged = onDisk.filter((r) => !declaredNames.has(r.name));
    return {
        agent,
        version,
        kind: 'hooks',
        capable,
        declared,
        onDisk,
        wired,
        unmanaged,
        wiringSupported,
        wiring,
    };
}
