/**
 * @fileoverview Pure, framework-agnostic helpers for the Lists dashboard's
 * AI-agent integration (Browse, Categories, Operations/Venn, My Lists).
 *
 * These functions are intentionally free of Angular / component dependencies
 * so they can be unit-tested in isolation (see {@link ./lists-agent-context.test.ts}).
 * Each component supplies a plain snapshot of its current state and these
 * helpers shape it into the key-value context object that flows to the async
 * chat agent and the realtime co-agent via `NavigationService.SetAgentContext`,
 * and resolve agent-supplied names/ids to concrete records for the client tools.
 *
 * 🔒 SAFETY: every function here is read-only and side-effect-free. They only
 * shape context and match strings — they never mutate, query, or render
 * anything. The destructive/append safety boundary is enforced by the
 * components, not here.
 *
 * The Venn region resolver lives separately in {@link ./components/lists-operations-region-resolver.ts}
 * (it predates this module); the Operations context builder below complements it.
 */
/**
 * Upper bound on how many names we publish in a name-list context field
 * (e.g. VisibleListNames, CategoryNames, OperandNames). Keeping the streamed
 * note bounded avoids flooding the co-agent with hundreds of names; when the
 * underlying list is larger the builder surfaces a companion total-count field.
 */
export const LISTS_AGENT_CONTEXT_NAME_LIST_CAP = 25;
/**
 * Cap an array of names to at most {@link LISTS_AGENT_CONTEXT_NAME_LIST_CAP}
 * entries. Pure + deterministic; never mutates the input (returns a new array).
 *
 * @param names - the full list of names (caller owns de-duplication / ordering)
 * @returns the first N names, where N is the cap
 */
export function capListNames(names) {
    return names.slice(0, LISTS_AGENT_CONTEXT_NAME_LIST_CAP);
}
/**
 * Resolve an agent-supplied reference to one of the available records, the way
 * a user names things. The agent may pass an exact ID, an exact name, or a
 * partial name — so we try, in order:
 *   1. exact ID match (case-insensitive, to tolerate UUID-casing differences)
 *   2. exact name match (case-insensitive, whitespace-trimmed)
 *   3. first case-insensitive *contains* match on the name
 *
 * Pure + deterministic over the supplied candidate list, so it's unit-testable
 * in isolation. Returns the matched record, or null on a miss.
 *
 * @param input - whatever the agent passed (an ID or a list/category name)
 * @param candidates - the records available on this surface
 */
export function resolveNamedRecord(input, candidates) {
    const needle = (input ?? '').trim().toLowerCase();
    if (!needle) {
        return null;
    }
    // 1. exact ID match (case-insensitive — SQL Server upper- vs PG lower-case UUIDs).
    const byId = candidates.find(c => c.ID.toLowerCase() === needle);
    if (byId) {
        return byId;
    }
    // 2. exact name match.
    const byName = candidates.find(c => c.Name.trim().toLowerCase() === needle);
    if (byName) {
        return byName;
    }
    // 3. first contains match on the name.
    const byContains = candidates.find(c => c.Name.toLowerCase().includes(needle));
    return byContains ?? null;
}
/**
 * Build a tolerant "not found" error message that lists a few of the available
 * names, so the agent can correct itself. Pure + deterministic.
 *
 * @param input - the agent-supplied reference that didn't resolve
 * @param candidates - the available records (their names are sampled)
 * @param noun - the kind of thing (e.g. "list", "category") for the message
 */
export function buildNotFoundError(input, candidates, noun) {
    const sample = candidates.slice(0, 6).map(c => c.Name).join(', ');
    return `No ${noun} matching "${input}" is available. Available ${noun}s include: ${sample || '(none)'}.`;
}
/**
 * Build the agent context for a list-browsing surface (Browse / My Lists).
 * Bounds the visible-name list and surfaces a companion total when truncated,
 * and only includes the optional filter fields the surface actually supplies.
 */
export function buildListBrowseAgentContext(input) {
    const context = {
        SearchTerm: input.SearchTerm,
        ViewMode: input.ViewMode,
        AllListCount: input.AllListCount,
        FilteredListCount: input.FilteredListCount,
        VisibleListNames: capListNames(input.VisibleListNames),
    };
    if (input.VisibleListNames.length > LISTS_AGENT_CONTEXT_NAME_LIST_CAP) {
        context['VisibleListNameCount'] = input.VisibleListNames.length;
    }
    if (input.SelectedSort != null) {
        context['SelectedSort'] = input.SelectedSort;
    }
    if (input.ActiveFilterCount != null) {
        context['ActiveFilterCount'] = input.ActiveFilterCount;
    }
    if (input.SelectedOwner != null) {
        context['SelectedOwner'] = input.SelectedOwner;
    }
    if (input.SelectedEntity != null) {
        context['SelectedEntity'] = input.SelectedEntity;
    }
    if (input.ShowOnlyFavorites != null) {
        context['ShowOnlyFavorites'] = input.ShowOnlyFavorites;
    }
    return context;
}
/**
 * Build the agent context for the Categories surface: the selection (id+name),
 * counts, the bounded member-list names of the selection, and a bounded view of
 * the category tree (names / list counts / expanded), plus the names of the
 * groups currently expanded.
 */
export function buildListCategoriesAgentContext(input) {
    const context = {
        SelectedCategoryId: input.SelectedCategoryId,
        SelectedCategoryName: input.SelectedCategoryName,
        CategoryCount: input.CategoryCount,
        SelectedCategoryListCount: input.SelectedCategoryListCount,
    };
    if (input.SelectedCategoryListNames.length > 0) {
        context['SelectedCategoryListNames'] = capListNames(input.SelectedCategoryListNames);
        if (input.SelectedCategoryListNames.length > LISTS_AGENT_CONTEXT_NAME_LIST_CAP) {
            context['SelectedCategoryListNameCount'] = input.SelectedCategoryListNames.length;
        }
    }
    if (input.CategoryNodes.length > 0) {
        context['CategoryNodes'] = input.CategoryNodes.slice(0, LISTS_AGENT_CONTEXT_NAME_LIST_CAP);
        context['ExpandedCategoryNames'] = input.CategoryNodes.filter(n => n.Expanded).map(n => n.Name);
        if (input.CategoryNodes.length > LISTS_AGENT_CONTEXT_NAME_LIST_CAP) {
            context['CategoryNodeCount'] = input.CategoryNodes.length;
        }
    }
    return context;
}
/**
 * Build the agent context for the Operations (Venn) surface: the operands
 * (names / kind / entity), counts, the locked entity, the compose op, the
 * available entity-filter names, the available regions, the selected region +
 * a preview of its records, and the last computed operation. All name lists are
 * bounded with companion counts when truncated.
 */
export function buildListOperationsAgentContext(input) {
    const context = {
        ListOperandCount: input.ListOperandCount,
        ViewOperandCount: input.ViewOperandCount,
        TotalOperandCount: input.TotalOperandCount,
        MaxOperands: input.MaxOperands,
        LockedEntityName: input.LockedEntityName,
        ComposeOp: input.ComposeOp,
        SelectedRegionLabel: input.SelectedRegionLabel,
        SelectedRegionSize: input.SelectedRegionSize,
        LastOperation: input.LastOperation,
    };
    if (input.Operands.length > 0) {
        context['Operands'] = input.Operands.slice(0, LISTS_AGENT_CONTEXT_NAME_LIST_CAP);
    }
    if (input.AvailableEntityNames.length > 0) {
        context['AvailableEntityNames'] = capListNames(input.AvailableEntityNames);
        if (input.AvailableEntityNames.length > LISTS_AGENT_CONTEXT_NAME_LIST_CAP) {
            context['AvailableEntityNameCount'] = input.AvailableEntityNames.length;
        }
    }
    if (input.AvailableRegions.length > 0) {
        context['AvailableRegions'] = input.AvailableRegions.slice(0, LISTS_AGENT_CONTEXT_NAME_LIST_CAP);
        if (input.AvailableRegions.length > LISTS_AGENT_CONTEXT_NAME_LIST_CAP) {
            context['AvailableRegionCount'] = input.AvailableRegions.length;
        }
    }
    if (input.PreviewRecordNames.length > 0) {
        context['PreviewRecordNames'] = capListNames(input.PreviewRecordNames);
    }
    return context;
}
//# sourceMappingURL=lists-agent-context.js.map