import { existsSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { ModuleRegistry, ModuleRegistryError } from "./module-registry.js";
import { resolveWorkspaceConfigWithLayers } from "./workspace-kit-config.js";
function moduleContractPackageRoot() {
    return resolve(dirname(fileURLToPath(import.meta.url)), "../..");
}
function hasModuleContractMarker(candidateRoot) {
    return existsSync(resolve(candidateRoot, "src/modules/task-engine/config.md"));
}
/**
 * Instruction paths in module registration are package-relative. Use `workspacePath` when it
 * contains the kit sources; otherwise fall back to the installed package root.
 */
export function pickModuleContractWorkspacePath(workspacePath) {
    if (hasModuleContractMarker(workspacePath)) {
        return workspacePath;
    }
    const packageRoot = moduleContractPackageRoot();
    if (hasModuleContractMarker(packageRoot)) {
        return packageRoot;
    }
    return process.cwd();
}
function readNonEmptyStringArray(value) {
    if (!Array.isArray(value)) {
        return undefined;
    }
    const out = value.filter((x) => typeof x === "string" && x.trim().length > 0);
    return out.length > 0 ? out : undefined;
}
/**
 * Reads `modules.enabled` / `modules.disabled` from effective config and maps them
 * to ModuleRegistryOptions. Unknown module ids throw — fail fast on typos.
 *
 * Semantics (matches resolveEnabledModuleIds):
 * - If `modules.enabled` is non-empty: only those ids are candidates, then `modules.disabled` subtracts.
 * - If `modules.enabled` is empty/absent: start from each module's enabledByDefault, then subtract disabled.
 */
export function moduleRegistryOptionsFromEffectiveConfig(effective, knownModuleIds) {
    const root = effective.modules;
    if (root === undefined || root === null) {
        return {};
    }
    if (typeof root !== "object" || Array.isArray(root)) {
        throw new ModuleRegistryError("invalid-modules-config", "effectiveConfig.modules must be an object when present");
    }
    const mod = root;
    const enabled = readNonEmptyStringArray(mod.enabled);
    const disabled = readNonEmptyStringArray(mod.disabled);
    for (const id of [...(enabled ?? []), ...(disabled ?? [])]) {
        if (!knownModuleIds.has(id)) {
            throw new ModuleRegistryError("unknown-module-in-config", `Unknown module id in modules.enabled / modules.disabled: '${id}'`);
        }
    }
    return {
        enabledModules: enabled,
        disabledModules: disabled
    };
}
function enabledSignature(registry) {
    return registry
        .getEnabledModules()
        .map((m) => m.registration.id)
        .sort()
        .join(",");
}
/**
 * Resolves layered config together with module enablement toggles from that config.
 * Iterates until the enabled module set stabilizes (module config contributions can
 * change when modules drop out of startup order).
 */
export async function resolveRegistryAndConfig(workspacePath, allModules, invocationConfig) {
    const knownIds = new Set(allModules.map((m) => m.registration.id));
    const moduleContractPath = pickModuleContractWorkspacePath(workspacePath);
    let registry = new ModuleRegistry(allModules, { workspacePath: moduleContractPath });
    for (let i = 0; i < 8; i++) {
        const { effective, layers } = await resolveWorkspaceConfigWithLayers({
            workspacePath,
            registry,
            invocationConfig
        });
        const extra = moduleRegistryOptionsFromEffectiveConfig(effective, knownIds);
        const candidate = new ModuleRegistry(allModules, { workspacePath: moduleContractPath, ...extra });
        if (enabledSignature(registry) === enabledSignature(candidate)) {
            const fin = await resolveWorkspaceConfigWithLayers({
                workspacePath,
                registry: candidate,
                invocationConfig
            });
            return {
                registry: candidate,
                effective: fin.effective,
                layers: fin.layers
            };
        }
        registry = candidate;
    }
    throw new ModuleRegistryError("module-enablement-unstable", "modules.enabled / modules.disabled did not stabilize after repeated config resolution; check config layers");
}
