/**
 * @fileoverview Pure helpers for the Entity Admin dashboard's AI-agent integration.
 *
 * These functions are intentionally free of Angular / component dependencies so they
 * can be unit-tested in isolation. The component ({@link EntityAdminDashboardComponent})
 * supplies a plain snapshot of its current ERD-browser state and these helpers shape it
 * into the key-value `AgentContext` object that flows to the async chat agent and the
 * realtime co-agent via `NavigationService.SetAgentContext`.
 *
 * The deep ("hardcore", Data-Explorer depth) enrichment here mirrors the Data Explorer
 * pattern: bounded name lists (cap {@link ENTITY_ADMIN_NAME_LIST_CAP} with a companion
 * `*Count` flag when truncated), a relationship summary for the selected entity, the
 * schema-grouping landscape, the active filter state, and a tolerant id→display-name
 * resolver so the agent can refer to entities the way the user sees them (the on-screen
 * DISPLAY name — e.g. "AI Models", not the registered "MJ: AI Models").
 *
 * 🔒 SAFETY BOUNDARY: The Entity Admin surface is a metadata BROWSER (the ERD entity
 * explorer). Both the context shaped here and the tools wired in the component are
 * strictly read-only / navigational. No helper in this file describes — and no tool may
 * perform — entity-metadata create/edit/delete, schema changes, or any other mutation.
 */
/**
 * The "MJ: " schema prefix carried by newer core entities (e.g. "MJ: AI Models",
 * "MJ: ML Models"). The ERD strips it when an entity has no explicit DisplayName, and
 * users almost always say the stripped form ("AI Models") rather than the full
 * registered Name. See {@link entityDisplayName} / {@link stripMJPrefix}.
 */
export const MJ_ENTITY_NAME_PREFIX = 'MJ: ';
/**
 * Upper bound on how many names we publish in a name-list context field
 * (AvailableEntities, RelatedEntities, SchemaGroups). Keeping the streamed note
 * bounded avoids flooding the co-agent with hundreds of names; when the underlying
 * list is larger we surface a companion total-count field instead.
 */
export const ENTITY_ADMIN_NAME_LIST_CAP = 25;
/**
 * Strip the leading "MJ: " schema prefix from an entity Name, if present.
 * Pure + deterministic. Returns the input unchanged when the prefix is absent.
 */
export function stripMJPrefix(name) {
    return name.startsWith(MJ_ENTITY_NAME_PREFIX) ? name.slice(MJ_ENTITY_NAME_PREFIX.length) : name;
}
/**
 * The display label the ERD shows for an entity — the entity's DisplayName when set,
 * else the registered Name with the "MJ: " schema prefix stripped. This is what the
 * user reads on screen and therefore what they say ("AI Models", not "MJ: AI Models").
 */
export function entityDisplayName(name, displayName) {
    if (displayName) {
        return displayName;
    }
    return stripMJPrefix(name);
}
/**
 * Resolve an agent-supplied entity reference to one of the available entities. The
 * agent may pass either an entity ID (GUID) or a name the way the user sees it. We try,
 * in order:
 *   1. exact ID (case-insensitive — UUIDs may differ in case across SQL Server / PG)
 *   2. exact registered Name (case-insensitive)
 *   3. display name (case-insensitive) — DisplayName, else the prefix-stripped Name
 *   4. display name ignoring the "MJ: " prefix (covers an input carrying the prefix)
 *   5. partial (contains) match on the display name
 *
 * Pure + deterministic over the supplied candidate list, so it's unit-testable in
 * isolation; the component passes its `EntityInfo[]` (structurally `EntityNameCandidate[]`).
 *
 * @param input - whatever the agent passed (an entity ID or the on-screen display name)
 * @param candidates - the entities available in the ERD
 * @returns the matched candidate, or null on a miss
 */
export function resolveEntityByIdOrName(input, candidates) {
    const needle = input.trim().toLowerCase();
    if (!needle) {
        return null;
    }
    // 1. exact ID (case-insensitive)
    const byId = candidates.find(c => c.ID.toLowerCase() === needle);
    if (byId) {
        return byId;
    }
    // 2. exact registered Name (case-insensitive)
    const byName = candidates.find(c => c.Name.toLowerCase() === needle);
    if (byName) {
        return byName;
    }
    // 3. display name (DisplayName, else prefix-stripped Name)
    const byDisplay = candidates.find(c => entityDisplayName(c.Name, c.DisplayName).toLowerCase() === needle);
    if (byDisplay) {
        return byDisplay;
    }
    // 4. display name ignoring the "MJ: " prefix (covers an input that itself carries the prefix)
    const strippedNeedle = stripMJPrefix(input.trim()).toLowerCase();
    if (strippedNeedle !== needle) {
        const byStripped = candidates.find(c => entityDisplayName(c.Name, c.DisplayName).toLowerCase() === strippedNeedle);
        if (byStripped) {
            return byStripped;
        }
    }
    // 5. partial (contains) match on the display name
    const byContains = candidates.find(c => entityDisplayName(c.Name, c.DisplayName).toLowerCase().includes(needle));
    if (byContains) {
        return byContains;
    }
    return null;
}
/**
 * Build a tolerant "not found" error for an ID-or-name lookup miss, listing a bounded
 * sample of available entity DISPLAY names so the agent can correct itself.
 */
export function buildEntityNotFoundError(input, candidates) {
    const sample = candidates
        .slice(0, ENTITY_ADMIN_NAME_LIST_CAP)
        .map(c => entityDisplayName(c.Name, c.DisplayName))
        .join(', ');
    return `No entity matches "${input}". Available entities include: ${sample || '(none)'}.`;
}
/** Cap an array of names to {@link ENTITY_ADMIN_NAME_LIST_CAP} entries. Pure + deterministic. */
function capNames(names) {
    return names.slice(0, ENTITY_ADMIN_NAME_LIST_CAP);
}
/**
 * Build the agent-visible context object for the Entity Admin ERD browser.
 *
 * Reports how the ERD is currently scoped (total vs. filtered entity counts, whether a
 * filter is narrowing the set + the active filter values), what (if anything) is selected
 * (id + registered name + display name + schema + description + field count + a bounded
 * relationship summary), whether the filter panel is open, the bounded available-entity
 * DISPLAY-name list, and the schema-grouping landscape. `HasActiveFilters` is derived so
 * the agent can tell the user is looking at a subset without re-deriving it.
 *
 * Keeping this a pure function (no `this`) makes the context shape unit-testable and
 * decouples it from change-detection timing.
 *
 * @param input - the component's current ERD-browser state snapshot
 * @returns a flat key-value object suitable for `SetAgentContext`
 */
export function buildEntityAdminAgentContext(input) {
    const hasActiveFilters = input.FilteredEntityCount < input.TotalEntityCount ||
        !!input.SchemaFilter ||
        !!input.SearchText ||
        !!input.StatusFilter;
    const context = {
        TotalEntityCount: input.TotalEntityCount,
        FilteredEntityCount: input.FilteredEntityCount,
        SelectedEntityId: input.SelectedEntityId,
        SelectedEntityName: input.SelectedEntityName,
        SelectedEntityDisplayName: input.SelectedEntityDisplayName,
        FilterPanelVisible: input.FilterPanelVisible,
        HasActiveFilters: hasActiveFilters,
        AvailableEntities: capNames(input.AvailableEntityNames),
    };
    if (input.AvailableEntityNames.length > ENTITY_ADMIN_NAME_LIST_CAP) {
        context['AvailableEntityCount'] = input.AvailableEntityNames.length;
    }
    // Selected-entity detail — only when something is selected.
    if (input.SelectedEntityId) {
        if (input.SelectedEntitySchema) {
            context['SelectedEntitySchema'] = input.SelectedEntitySchema;
        }
        if (input.SelectedEntityDescription) {
            context['SelectedEntityDescription'] = input.SelectedEntityDescription;
        }
        if (input.SelectedEntityFieldCount != null) {
            context['SelectedEntityFieldCount'] = input.SelectedEntityFieldCount;
        }
        if (input.RelatedEntities.length > 0) {
            context['RelatedEntities'] = input.RelatedEntities.slice(0, ENTITY_ADMIN_NAME_LIST_CAP);
            context['RelatedEntityCount'] = input.RelatedEntities.length;
        }
    }
    // Active filter values — surfaced only when set, keeping the context lean.
    if (input.SchemaFilter) {
        context['SchemaFilter'] = input.SchemaFilter;
    }
    if (input.SearchText) {
        context['SearchText'] = input.SearchText;
    }
    if (input.StatusFilter) {
        context['StatusFilter'] = input.StatusFilter;
    }
    // Schema-grouping landscape — bounded; companion count when over the cap.
    if (input.SchemaGroups.length > 0) {
        context['SchemaGroups'] = input.SchemaGroups.slice(0, ENTITY_ADMIN_NAME_LIST_CAP);
        if (input.SchemaGroups.length > ENTITY_ADMIN_NAME_LIST_CAP) {
            context['SchemaGroupCount'] = input.SchemaGroups.length;
        }
    }
    return context;
}
//# sourceMappingURL=entity-admin-agent-context.js.map