/**
 * Product-shaped Guidance Rules catalog rows (activation + artifact closure → UI DTO).
 * Pure transform — callers must not mutate registry storage here.
 */
function asRecord(value) {
    return value && typeof value === "object" && !Array.isArray(value)
        ? value
        : null;
}
function describeScopeCondition(cond) {
    const kind = cond.kind;
    if (kind === "always")
        return "always applies";
    if (kind === "phaseKey" && typeof cond.value === "string")
        return `phase is ${cond.value}`;
    if (kind === "commandName") {
        const match = cond.match === "prefix" ? "starts with" : "is exactly";
        const v = typeof cond.value === "string" ? cond.value : "";
        return `command name ${match} ${JSON.stringify(v)}`;
    }
    if (kind === "commandArgEquals" && typeof cond.path === "string") {
        return `argument ${cond.path} equals ${JSON.stringify(cond.value ?? null)}`;
    }
    if (kind === "taskTag") {
        const vals = Array.isArray(cond.values)
            ? cond.values.filter((x) => typeof x === "string")
            : [];
        const mode = cond.match === "all" ? "all" : "any";
        return vals.length === 0 ? null : `task tags (${mode}) include ${vals.join(", ")}`;
    }
    if (kind === "taskIdPattern" && typeof cond.pattern === "string") {
        return `task id matches /${cond.pattern}/`;
    }
    return null;
}
export function describeActivationScope(scope) {
    const s = asRecord(scope);
    const rawConds = s?.conditions;
    const conds = Array.isArray(rawConds)
        ? rawConds.filter((c) => !!c && typeof c === "object" && !Array.isArray(c))
        : [];
    if (conds.length === 0)
        return "Triggers when Guidance evaluates — scope predicates were empty or unrecognized.";
    const parts = conds.map((c) => describeScopeCondition(c)).filter((p) => !!p?.length);
    return parts.length
        ? `Matches when ALL of these are true: ${parts.join("; ")}.`
        : "Triggers when Guidance evaluates.";
}
function asFamily(kind) {
    if (kind === "policy" || kind === "think" || kind === "do" || kind === "review")
        return kind;
    return null;
}
function attentionForFamily(family) {
    if (family === "policy")
        return "required";
    if (family === "review")
        return "check";
    return "advisory";
}
function lifecycleLabel(ls) {
    switch (ls) {
        case "draft":
            return "Draft";
        case "active":
            return "Active";
        case "retired":
            return "Retired";
        case "disabled":
            return "Disabled";
        default:
            return ls;
    }
}
/** Workspace-level gates for versioned registry mutations (Dashboard / extension). */
export function deriveGuidanceRegistryMutationCapability(input) {
    if (input.registryStore !== "sqlite") {
        return {
            canMutate: false,
            denialReason: "Guidance registry mutations require SQLite registry store (`kit.cae.registryStore`)."
        };
    }
    if (!input.adminMutations) {
        return {
            canMutate: false,
            denialReason: "Guidance admin mutations are disabled (`kit.cae.adminMutations`)."
        };
    }
    return { canMutate: true, denialReason: null };
}
function deriveMutationHints(input) {
    const coarse = deriveGuidanceRegistryMutationCapability({
        registryStore: input.registryStore,
        adminMutations: input.adminMutations
    });
    if (!coarse.canMutate) {
        return {
            canClone: false,
            canActivate: false,
            canEditDraft: false,
            canRetire: false,
            denialReason: coarse.denialReason
        };
    }
    const st = input.lifecycleState;
    return {
        canClone: true,
        canActivate: st === "draft",
        canEditDraft: st === "draft",
        canRetire: st === "active",
        denialReason: null
    };
}
/** Build deterministic catalog rows for every activation row in the loaded snapshot. Exported for golden tests. */
export function buildRulesCatalogFromRegistry(input) {
    const acts = [...input.loaded.activations].sort((a, b) => String(a.activationId ?? "").localeCompare(String(b.activationId ?? "")));
    const items = [];
    let ordinal = 0;
    for (const raw of acts) {
        ordinal += 1;
        const row = raw;
        const family = asFamily(row.family);
        if (!family)
            continue;
        const refs = Array.isArray(row.artifactRefs) ? row.artifactRefs : [];
        const artifactIds = refs
            .map((ref) => ref && typeof ref === "object" && typeof ref.artifactId === "string"
            ? String(ref.artifactId)
            : "")
            .filter((id) => id.length > 0);
        const sourceTitles = artifactIds
            .map((id) => {
            const art = input.loaded.artifactById.get(id);
            const title = art && typeof art.title === "string"
                ? String(art.title).trim()
                : "";
            return title.length > 0 ? title : id;
        })
            .slice(0, 8);
        const lsRaw = typeof row.lifecycleState === "string" ? row.lifecycleState : "draft";
        const lifecycleState = lsRaw === "draft" || lsRaw === "active" || lsRaw === "retired" || lsRaw === "disabled" ? lsRaw : "draft";
        const ack = row.acknowledgement;
        const acknowledgementStrength = typeof ack?.strength === "string" && ack.strength.length > 0 ? ack.strength : null;
        const acknowledgementTokenPresent = typeof ack?.token === "string" && String(ack.token).trim().length > 0;
        items.push({
            schemaVersion: 1,
            ordinal,
            family,
            familyLabel: input.familyLabels[family],
            displayTitle: sourceTitles[0] ?? String(row.activationId ?? "Guidance rule"),
            appliesWhen: describeActivationScope(row.scope),
            lifecycleState,
            lifecycleLabel: lifecycleLabel(lifecycleState),
            priority: Number(row.priority ?? 0),
            attention: attentionForFamily(family),
            acknowledgementStrength,
            acknowledgementTokenPresent,
            sources: sourceTitles.map((title) => ({ title })),
            mutation: deriveMutationHints({
                adminMutations: input.adminMutations,
                registryStore: input.registryStore,
                lifecycleState
            }),
            debug: {
                activationId: String(row.activationId ?? ""),
                artifactIds
            }
        });
    }
    return {
        schemaVersion: 1,
        registrySnapshotTag: input.snapshotTag,
        itemCount: items.length,
        items
    };
}
/** Top-level aggregator used by `cae-dashboard-summary` product model. */
export function buildGuidanceRulesCatalogEnvelope(input) {
    const store = typeof input.registryStoreRaw === "string" ? input.registryStoreRaw : "sqlite";
    const versionId = typeof input.health.activeRegistryVersionId === "string" ? input.health.activeRegistryVersionId : null;
    if (!input.loadedOk || !input.loaded) {
        return {
            schemaVersion: 1,
            registrySnapshotTag: versionId,
            itemCount: 0,
            items: [],
            degraded: true,
            degradedCode: input.loadFail?.code ?? "cae-registry-unavailable",
            degradedMessage: input.loadFail?.message ?? "Registry unavailable for Guidance rules catalog."
        };
    }
    const digestSnippet = `${store}:${input.loaded.registryDigest.slice(0, 12)}`;
    const snapshotTag = store === "json" ? digestSnippet : versionId ?? digestSnippet;
    return buildRulesCatalogFromRegistry({
        loaded: input.loaded,
        registryStore: store,
        adminMutations: input.adminMutations,
        snapshotTag,
        familyLabels: input.familyLabels
    });
}
