/** Side-effect-free deterministic Health v1 collection. */
/* eslint-disable jsdoc/require-param-description, jsdoc/require-returns -- typed probe assembly helpers are self-describing */
import { lstat, realpath } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { runConfigSync } from "../sync/config-sync.js";
import { summarizeHealthFindings, validateHealthResult, } from "./contract.js";
import { HealthDeadline } from "./deadline.js";
import { declaredChecksDriftFinding } from "./declared-checks-inspection.js";
import { loadConfigState, safeSyncReads, shareRulesetReader, } from "./deterministic-inputs.js";
import { deterministicFinding } from "./finding-utils.js";
import { hookFinding, managedTemplateFinding, packageFinding, pluginFinding, workflowFinding, } from "./governance-probes.js";
import { readCoreHooksPath } from "./hook-inspection.js";
import { readInstalledClaudePlugins, } from "./plugin-inspection.js";
import { configFindings, instructionFinding, projectStateFinding, starterFinding, wikiFinding, } from "./project-probes.js";
import { readGithubRulesets, rulesetFinding, } from "./ruleset-inspection.js";
import { detectHealthProjectShape, } from "./template-inspection.js";
const DEFAULT_DEADLINE_MS = 110_000;
const CHECKS = [
    { check: "project.state", unavailable: "fail" },
    { check: "project.wiki", unavailable: "fail" },
    { check: "starters.remote", unavailable: "warn" },
    { check: "config.required", unavailable: "fail" },
    { check: "config.sync", unavailable: "fail" },
    { check: "templates.managed", unavailable: "fail" },
    { check: "package.conformance", unavailable: "fail" },
    { check: "instructions.canonical", unavailable: "fail" },
    { check: "hooks.managed", unavailable: "fail" },
    { check: "plugins.current", unavailable: "warn" },
    { check: "ci.workflows", unavailable: "fail" },
    { check: "github.rulesets", unavailable: "warn" },
    { check: "github.declared-checks", unavailable: "warn" },
];
/**
 * Stable fallback when a check cannot complete inside the shared deadline.
 * @param check
 * @param status
 */
function unavailableFinding(check, status) {
    return deterministicFinding(check, status, `${check} could not be safely observed within the deterministic deadline.`);
}
/**
 * Build a validated immutable HealthResult from ordered findings.
 * @param started
 * @param completed
 * @param findings
 */
function result(started, completed, findings) {
    const safeCompleted = completed < started ? started : completed;
    return validateHealthResult({
        schemaVersion: 1,
        runId: `health-${crypto.randomUUID()}`,
        mode: "deterministic",
        startedAt: started.toISOString(),
        completedAt: safeCompleted.toISOString(),
        findings,
        summary: summarizeHealthFindings(findings),
    });
}
/**
 * Resolve canonical project and Lisa roots inside the shared deadline.
 * @param projectPath
 * @param lisaPath
 */
async function resolveRoots(projectPath, lisaPath) {
    const [projectRoot, lisaRoot] = await Promise.all([
        realpath(path.resolve(projectPath)),
        realpath(lisaPath),
    ]);
    if (!(await lstat(projectRoot)).isDirectory()) {
        throw new Error("Health project root is not a directory");
    }
    return { projectRoot, lisaRoot };
}
/**
 * Create the fixed-order probe registry from safely captured setup state.
 * @param roots
 * @param roots.projectRoot
 * @param roots.lisaRoot
 * @param configState
 * @param shape
 * @param deadline
 * @param options
 */
// eslint-disable-next-line max-lines-per-function -- fixed-order registry keeps the complete contract auditable
function probes(roots, configState, shape, deadline, options) {
    const safeShape = shape ?? {
        types: [],
        packageJson: undefined,
    };
    const sync = runConfigSync(roots.projectRoot, {
        dryRun: true,
        reads: safeSyncReads(roots.projectRoot),
    });
    const rulesets = shareRulesetReader(options.readRulesets ?? readGithubRulesets);
    return [
        {
            ...CHECKS[0],
            run: () => projectStateFinding(roots.projectRoot, configState, shape),
        },
        { ...CHECKS[1], run: () => wikiFinding(roots.projectRoot) },
        { ...CHECKS[2], run: async () => starterFinding() },
        { ...CHECKS[3], run: async () => (await configFindings(await sync))[0] },
        { ...CHECKS[4], run: async () => (await configFindings(await sync))[1] },
        {
            ...CHECKS[5],
            run: () => managedTemplateFinding(roots.lisaRoot, roots.projectRoot, safeShape, configState.config),
        },
        {
            ...CHECKS[6],
            run: () => packageFinding(roots.lisaRoot, roots.projectRoot, safeShape),
        },
        { ...CHECKS[7], run: () => instructionFinding(roots.projectRoot) },
        {
            ...CHECKS[8],
            run: () => hookFinding(roots.lisaRoot, roots.projectRoot, safeShape.types, configState.config, options.readHooksPath ?? readCoreHooksPath, deadline.remainingMs(), deadline.signal),
        },
        {
            ...CHECKS[9],
            run: () => pluginFinding(roots.projectRoot, configState.config, safeShape.types, options.readInstalledPlugins ?? readInstalledClaudePlugins, deadline.remainingMs(), deadline.signal),
        },
        {
            ...CHECKS[10],
            run: () => workflowFinding(roots.lisaRoot, roots.projectRoot, safeShape.types, configState.config),
        },
        {
            ...CHECKS[11],
            run: () => rulesetFinding(roots.lisaRoot, roots.projectRoot, safeShape.types, configState.config, rulesets, deadline.remainingMs(), deadline.signal),
        },
        {
            ...CHECKS[12],
            run: () => declaredChecksDriftFinding(roots.projectRoot, configState.config, rulesets, deadline.remainingMs(), deadline.signal),
        },
    ];
}
/**
 * Collect deterministic Health v1 without persistence, repair, installation,
 * update checks, process-exit mutation, or model calls.
 * @param projectPath - Host project root
 * @param options - Injectable roots, deadline, clock, and bounded readers
 * @returns Validated immutable deterministic HealthResult
 */
export async function runDeterministicHealth(projectPath, options = {}) {
    const now = options.now ?? (() => new Date());
    const started = now();
    const deadline = new HealthDeadline(options.deadlineMs ?? DEFAULT_DEADLINE_MS);
    try {
        const lisaPath = options.lisaRoot ??
            path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
        const roots = await deadline.run(() => resolveRoots(projectPath, lisaPath), undefined);
        if (roots === undefined) {
            return result(started, now(), CHECKS.map(spec => unavailableFinding(spec.check, spec.unavailable)));
        }
        const [configState, shape] = await Promise.all([
            deadline.run(() => loadConfigState(roots.projectRoot), {
                config: {},
                present: false,
                readable: false,
            }),
            deadline.run(() => detectHealthProjectShape(roots.projectRoot), undefined),
        ]);
        const findings = await Promise.all(probes(roots, configState, shape, deadline, options).map(probe => deadline.run(probe.run, unavailableFinding(probe.check, probe.unavailable))));
        return result(started, now(), findings);
    }
    finally {
        deadline.close();
    }
}
export { readGithubRulesets };
/* eslint-enable jsdoc/require-param-description, jsdoc/require-returns -- restore repository documentation defaults */
//# sourceMappingURL=deterministic.js.map