/**
 * @fileoverview Tags dashboard — the canonical home for everything tag-related
 * across MJ. Tags aren't classification-only: any entity / workload can assign
 * them, so this dashboard lives at the Knowledge Hub top level rather than
 * being buried inside the Classify (autotag pipeline) dashboard.
 *
 * Four tabs:
 *   - Tag Library — table + drill-down + word cloud (moved from Classify).
 *   - Taxonomy — tree / Duplicates / Orphans / Treemap / Audit Log
 *     (moved from Classify; Tree's right-detail panel now also surfaces
 *     governance + scope + synonym editing; Duplicates + Orphans are now
 *     server-driven from `MJ:Tag Suggestions`).
 *   - Suggestions — human-in-the-loop review queue (`MJ:Tag Suggestions`).
 *   - Health — Tag Health 3-card summary, threshold tuning, run history,
 *     "Rebuild stale embeddings".
 *
 * The Classify dashboard still owns the autotag pipeline run-management
 * surface (Pipeline / Sources / Types / History).
 */
import { OnDestroy, AfterViewInit } from '@angular/core';
import { Subject } from 'rxjs';
import { CompositeKey } from '@memberjunction/core';
import { TreeBranchConfig, TreeLeafConfig } from '@memberjunction/ng-trees';
import { ResourceData, MJContentSourceTypeEntity_IContentSourceTypeField, MJTagEntity } from '@memberjunction/core-entities';
import { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';
import { MJLeftNavItem, MJLeftNavSection, TabConfig } from '@memberjunction/ng-ui-components';
import { WordCloudItem } from '@memberjunction/ng-word-cloud';
import * as i0 from "@angular/core";
/**
 * Tab union — wide for now because this component is in transition: the
 * existing autotagging-pipeline tabs (pipeline / sources / types / history)
 * are still referenced by helpers we share. NavItems only emits the 4
 * tag-related entries, so the user only ever sees Tags / Taxonomy /
 * Suggestions / Health. The dead-code strip is a follow-up.
 */
type TabName = 'pipeline' | 'sources' | 'types' | 'tags' | 'taxonomy' | 'history' | 'suggestions' | 'health';
/** Conventional `Reason` values from MJ:Tag Suggestions. */
type TagSuggestionReason = 'BelowThreshold' | 'ConstrainedMode' | 'AmbiguousMatch' | 'ParentFrozen' | 'AutoGrowDisabled' | 'MaxChildrenExceeded' | 'MaxDepthExceeded' | 'BelowMinWeight' | 'RequiresReview' | 'MaxItemTagsExceeded' | 'MergeCandidate' | 'LowUsage' | 'WideNode';
interface SuggestionRow {
    ID: string;
    ProposedName: string;
    Reason: string;
    BestMatchTagID: string | null;
    BestMatchName: string | null;
    BestMatchPath: string | null;
    BestMatchScore: number | null;
    SourceContentSourceID: string | null;
    SourceContentItemID: string | null;
    SourceText: string | null;
    CreatedAt: Date;
    Status: string;
    selected: boolean;
    dispositionInProgress: 'create-new' | 'merge' | 'reject' | null;
}
interface HealthRunHistoryRow {
    When: Date;
    Trigger: string;
    TagsScanned: number;
    Merge: number;
    LowUsage: number;
    WideNode: number;
    DurationMs: number;
}
interface SynonymRow {
    ID: string;
    Synonym: string;
    Source: string;
    CreatedAt: Date;
}
interface TagScopeRowView {
    ID: string;
    ScopeEntityID: string;
    ScopeRecordID: string;
    EntityName: string;
    DisplayName: string;
}
interface NavItem {
    Tab: TabName;
    Icon: string;
    Label: string;
    BadgeText: string;
    BadgeClass: string;
}
interface KPIMetric {
    Label: string;
    Value: number;
    Icon: string;
    Trend: string;
    TrendUp: boolean;
}
interface PipelineStageInfo {
    Name: string;
    Icon: string;
    Status: 'idle' | 'active' | 'complete';
    Count: string;
}
interface FeedItem {
    Name: string;
    SourceName: string;
    Tags: string[];
    TimeAgo: string;
    Status: 'complete' | 'processing' | 'error';
}
interface SourceMini {
    ID: string;
    Name: string;
    Icon: string;
    Meta: string;
    StatusClass: 'active' | 'error' | 'inactive';
}
interface SourceCard {
    ID: string;
    Name: string;
    SourceTypeName: string;
    ContentTypeName: string;
    FileTypeName: string;
    Icon: string;
    StatusClass: 'active' | 'error' | 'inactive';
    StatusLabel: string;
    URL: string;
    ItemCount: number;
    TagCount: number;
    AvgTags: string;
    LastRunAgo: string;
    ContentSourceTypeID: string;
    ContentTypeID: string;
    ContentFileTypeID: string;
    EmbeddingModelID: string;
    VectorIndexID: string;
    EntityID: string;
    EntityDocumentID: string;
    RequiresFileType: boolean;
    /** FK to ScheduledAction entity, null if no schedule configured */
    ScheduledActionID: string | null;
    /** Denormalized name of the linked ScheduledAction */
    ScheduledActionName: string | null;
    /** Human-readable schedule description (parsed from cron) */
    ScheduleDescription: string | null;
}
interface ContentTypeCard {
    ID: string;
    Name: string;
    Description: string;
    AIModelName: string;
    AIModelID: string;
    MinTags: number;
    MaxTags: number;
    SourcesUsing: number;
    ItemsTagged: number;
    RangeLeftPct: number;
    RangeRightPct: number;
    EmbeddingModelID: string;
    VectorIndexID: string;
}
interface TagRow {
    Tag: string;
    UsageCount: number;
    AvgWeight: number;
    BarWidthPct: number;
    TopSource: string;
    FirstSeen: string;
}
interface TagCloudItem {
    Tag: string;
    AvgWeight: number;
    SizeClass: 'large' | '' | 'small';
}
interface TagBySource {
    SourceName: string;
    Count: number;
}
interface RunHistoryRow {
    ID: string;
    Status: string;
    StatusClass: string;
    SourceName: string;
    StartedDisplay: string;
    Duration: string;
    Items: string;
    Tags: string;
    Errors: string;
    ErrorClass: string;
}
interface DropdownOption {
    ID: string;
    Name: string;
}
/** G3: Content item duplicate pair for review */
interface ContentDuplicateRow {
    ID: string;
    ItemAName: string;
    ItemASource: string;
    ItemBName: string;
    ItemBSource: string;
    SimilarityScore: number;
    DetectionMethod: string;
    Status: 'Pending' | 'Confirmed' | 'Dismissed' | 'Merged';
}
/** D2/D3/D8: Per-source detail row for a specific pipeline run */
interface RunDetailRow {
    SourceName: string;
    SourceType: string;
    Status: string;
    StatusClass: string;
    ItemsProcessed: number;
    ItemsTagged: number;
    ItemsVectorized: number;
    ErrorCount: number;
    TotalTokens: number;
    TotalCost: number;
    Duration: string;
}
type TaxonomySubTab = 'tree' | 'duplicates' | 'orphans' | 'treemap' | 'audit';
interface TaxTreeNode {
    ID: string;
    Name: string;
    DisplayName: string;
    Description: string;
    ParentID: string | null;
    Depth: number;
    Children: TaxTreeNode[];
    ItemCount: number;
    AvgWeight: number;
    HealthColor: 'green' | 'yellow' | 'red';
    IsExpanded: boolean;
    IsSelected: boolean;
    FirstSeen: string;
}
interface TaxDuplicatePair {
    TagA: string;
    TagB: string;
    TagAID: string;
    TagBID: string;
    Similarity: number;
    SeverityClass: 'high' | 'moderate';
    /** True when multiple Tag records share the same name (case-insensitive) */
    IsExactDuplicate: boolean;
    /** How many Tag records share this exact name (only set when IsExactDuplicate) */
    ExactDuplicateCount: number;
    /** All Tag IDs sharing the exact name (only set when IsExactDuplicate) */
    AllIDs: string[];
}
interface TaxOrphanCard {
    ID: string;
    Name: string;
    UsageCount: number;
    AvgWeight: number;
    FirstSeen: string;
    LastSeen: string;
    IsSelected: boolean;
}
interface TaxTreemapCell {
    ID: string;
    Name: string;
    ItemCount: number;
    ColorClass: string;
    RowSpan: number;
}
/** Supported audit action types — matches DB Action values (lowercased) */
type TaxAuditAction = 'created' | 'merged' | 'moved' | 'deleted' | 'renamed' | 'deprecated' | 'descriptionchanged' | 'reactivated' | 'split';
interface TaxAuditEvent {
    Type: TaxAuditAction;
    Description: string;
    TagRef: string;
    User: string;
    Timestamp: string;
    DayHeader: string;
}
interface TaxHealthStat {
    Total: number;
    Healthy: number;
    NeedAttention: number;
    Orphaned: number;
    Duplicates: number;
}
interface WeightedTag {
    Tag: string;
    Weight: number;
}
/** Status value for embedding or tagging pipeline phases */
type ItemPipelineStatus = 'Complete' | 'Processing' | 'Failed' | 'Pending';
interface ContentItemDetail {
    ID: string;
    Name: string;
    SourceName: string;
    SourceTypeName: string;
    ContentTypeName: string;
    FileTypeName: string;
    URL: string;
    TextContent: string;
    Checksum: string;
    Tags: WeightedTag[];
    CreatedAt: string;
    UpdatedAt: string;
    ContentSourceID: string;
    ContentSourceTypeID: string;
    StatusDot: 'complete' | 'processing' | 'error';
    TagCount: number;
    /** Whether this source type uses content/file type (false for Entity sources) */
    RequiresContentType: boolean;
    /** Entity record ID if source is entity type (for direct entity navigation) */
    EntityRecordID: string | null;
    /** Entity name if source is entity type */
    EntityName: string | null;
    /** Embedding pipeline status for this content item */
    EmbeddingStatus: ItemPipelineStatus;
    /** Tagging pipeline status for this content item */
    TaggingStatus: ItemPipelineStatus;
}
interface SourceDetailInfo {
    ID: string;
    Name: string;
    SourceTypeName: string;
    FileTypeName: string;
    ContentTypeName: string;
    RequiresFileType: boolean;
    StatusClass: 'active' | 'error' | 'inactive';
    StatusLabel: string;
    Icon: string;
    URL: string;
    EmbeddingModelName: string;
    VectorIndexName: string;
    ItemCount: number;
    TagCount: number;
    AvgTags: string;
    LastRunAgo: string;
    ErrorCount: number;
    ContentItems: ContentItemDetail[];
    RunHistory: RunHistoryRow[];
}
type FormMode = 'none' | 'add-source' | 'edit-source' | 'add-type' | 'edit-type';
export declare class TagsResourceComponent extends BaseResourceComponent implements AfterViewInit, OnDestroy {
    protected destroy$: Subject<void>;
    private cdr;
    private confirmService;
    private activityService;
    protected navigationService: NavigationService;
    IsLoading: boolean;
    private totalContentItemCount;
    private totalContentTagCount;
    ActiveTab: TabName;
    private tabDataLoaded;
    NavItems: NavItem[];
    KPIMetrics: KPIMetric[];
    PipelineStages: PipelineStageInfo[];
    FeedItems: FeedItem[];
    SourceMinis: SourceMini[];
    TrendingTags: TagCloudItem[];
    FeedSearchQuery: string;
    FeedPage: number;
    readonly FeedPageSize = 20;
    /** Sort order for the feed: 'newest' (default) or 'oldest' */
    FeedSortOrder: 'newest' | 'oldest';
    /** Feed items filtered by search query and sorted */
    get FilteredFeedItems(): FeedItem[];
    /** Toggle feed sort order */
    ToggleFeedSort(): void;
    /** Paginated feed items for the current page */
    get PaginatedFeedItems(): FeedItem[];
    /** Total pages for the feed */
    get FeedTotalPages(): number;
    /** Handle feed search input change */
    OnFeedSearchChange(): void;
    /** Navigate to previous feed page */
    FeedPrevPage(): void;
    /** Navigate to next feed page */
    FeedNextPage(): void;
    /** Get the index in the original FeedItems array for a paginated item */
    GetFeedItemOriginalIndex(item: FeedItem): number;
    IsRunning: boolean;
    IsPaused: boolean;
    RunProgress: number;
    RunStage: string;
    RunCurrentItem: string;
    /** The current pipeline run ID (from GraphQL subscription) */
    CurrentPipelineRunID: string | null;
    /** The process run ID (server-side ContentProcessRun) for pause/cancel/resume */
    CurrentProcessRunID: string | null;
    ShowPipelineConfig: boolean;
    PipelineConfig: {
        Pipeline?: {
            BatchSize?: number;
            MaxConcurrentBatches?: number;
            DelayBetweenBatchesMs?: number;
            ResumeFromLastBatch?: boolean;
            ErrorThresholdPercent?: number;
        };
        RateLimits?: {
            LLM?: {
                RequestsPerMinute?: number;
                TokensPerMinute?: number;
            };
            Embedding?: {
                RequestsPerMinute?: number;
                TokensPerMinute?: number;
            };
            VectorDB?: {
                RequestsPerMinute?: number;
            };
        };
    };
    TogglePipelineConfig(): void;
    private persistClassifyPreferences;
    private loadClassifyPreferences;
    FormatTokenCount(tokens: number): string;
    SourceCards: SourceCard[];
    ContentDuplicates: ContentDuplicateRow[];
    IsLoadingDuplicates: boolean;
    ContentTypeCards: ContentTypeCard[];
    TagRows: TagRow[];
    TagCloud: TagCloudItem[];
    TagCloudWordItems: WordCloudItem[];
    TagsBySource: TagBySource[];
    TagSearchQuery: string;
    FilteredTagRows: TagRow[];
    SelectedDrillDownTag: string | null;
    TagDrillDownItems: {
        ID: string;
        Name: string;
        SourceName: string;
        Weight: number;
        UpdatedAt: string;
        FeedIndex: number;
    }[];
    RunHistoryRows: RunHistoryRow[];
    HistorySourceFilter: string;
    HistoryStatusFilter: string;
    FilteredRunRows: RunHistoryRow[];
    HistorySourceOptions: string[];
    SelectedRunID: string | null;
    RunDetailRows: RunDetailRow[];
    IsLoadingRunDetail: boolean;
    LiveRunDetailRows: RunDetailRow[];
    IsLoadingLiveDetails: boolean;
    TaxSubTab: TaxonomySubTab;
    TaxTreeNodes: TaxTreeNode[];
    TaxFlatNodes: TaxTreeNode[];
    TaxFilteredNodes: TaxTreeNode[];
    TaxSelectedNode: TaxTreeNode | null;
    TaxTreeSearch: string;
    TaxDuplicates: TaxDuplicatePair[];
    TaxOrphans: TaxOrphanCard[];
    TaxAllOrphansSelected: boolean;
    TaxTreemapCells: TaxTreemapCell[];
    TaxAuditEvents: TaxAuditEvent[];
    TaxAuditFilterTypes: Set<string>;
    TaxHealth: TaxHealthStat;
    TaxRecentItems: {
        Name: string;
        Weight: number;
        Date: string;
        Icon: string;
    }[];
    TaxTreemapKPIs: {
        Label: string;
        Value: string;
    }[];
    TaxIsEditing: boolean;
    ShowCreateTagDialog: boolean;
    CreateTagName: string;
    CreateTagDescription: string;
    CreateTagParentID: string | null;
    /** Label shown in the create dialog to indicate context (e.g., "under AI Agent") */
    CreateTagParentLabel: string;
    TaxMultiSelectMode: boolean;
    TaxSelectedIDs: Set<string>;
    /** The node currently being dragged over (drop target highlight) */
    TaxDragOverNodeID: string | null;
    /** True while a drag-reparent operation is saving */
    TaxTreeSaving: boolean;
    /** Count of high-confidence duplicate pairs (>85% similarity) */
    get TaxHighConfidenceDupeCount(): number;
    /** Count of moderate-confidence duplicate pairs (70-85% similarity) */
    get TaxModerateDupeCount(): number;
    TaxEditName: string;
    TaxEditDescription: string;
    SuggestionRows: SuggestionRow[];
    SuggestionRowsFiltered: SuggestionRow[];
    SuggestionFilterReason: string;
    SuggestionFilterMinScore: number | null;
    SuggestionSearch: string;
    SuggestionSelected: SuggestionRow | null;
    SuggestionBulkInProgress: boolean;
    PendingSuggestionCount: number;
    ReasonOptions: TagSuggestionReason[];
    HealthThresholds: {
        minCoOccurrence: number;
        minNameSimilarity: number;
        minEmbeddingSimilarity: number;
        maxUsage: number;
        maxImplicitChildren: number;
    };
    HealthRunning: boolean;
    RebuildEmbeddingsRunning: boolean;
    LastHealthSummary: {
        mergeCount: number;
        lowUsageCount: number;
        wideNodeCount: number;
        durationMs: number;
        runAt: Date | null;
    };
    HealthRunHistory: HealthRunHistoryRow[];
    SelectedTagSavingField: Record<string, boolean>;
    SelectedTagSynonyms: SynonymRow[];
    SelectedTagScopes: TagScopeRowView[];
    NewSynonymName: string;
    NewSynonymSource: 'Manual' | 'Imported' | 'Merged' | 'LLM';
    /** Public so the template's Health tab can read tag count for the runbar. */
    tagsRaw: Record<string, unknown>[];
    private taggedItemsRaw;
    private tagAuditLogsRaw;
    /** Cached per-tag aggregates from server-side SQL (weights + counts) */
    private tagAggregateWeights;
    private tagAggregateCounts;
    FormMode: FormMode;
    FormSaving: boolean;
    ShowNoContentTypeWarning: boolean;
    FormSourceName: string;
    FormSourceTypeID: string;
    FormContentTypeID: string;
    FormFileTypeID: string;
    FormSourceURL: string;
    EditingSourceID: string;
    FormTypeName: string;
    FormTypeDescription: string;
    FormTypeAIModelID: string;
    FormTypeMinTags: number;
    FormTypeMaxTags: number;
    EditingTypeID: string;
    FormTypeEmbeddingModelID: string;
    FormTypeVectorIndexID: string;
    FormSourceEntityID: string;
    FormSourceEntityDocID: string;
    FormSourceEmbeddingModelID: string;
    FormSourceVectorIndexID: string;
    /** Whether the schedule creation dialog is visible */
    ShowScheduleDialog: boolean;
    /** Whether a schedule save operation is in progress */
    ScheduleSaving: boolean;
    /** Source card currently being scheduled */
    SchedulingSourceCard: SourceCard | null;
    /** Cron expression for the schedule form */
    ScheduleCron: string;
    /** Whether the schedule should be active immediately */
    ScheduleEnabled: boolean;
    /** Whether a generic confirmation overlay is visible */
    ShowConfirmDialog: boolean;
    /** Title text for the confirmation dialog */
    ConfirmDialogTitle: string;
    /** Body message for the confirmation dialog */
    ConfirmDialogMessage: string;
    /** Callback invoked when user confirms */
    private confirmDialogAction;
    /** Whether the split-tag dialog is visible */
    ShowSplitDialog: boolean;
    /** Comma-separated new child tag names for the split operation */
    SplitChildNames: string;
    /** The node currently being split */
    private splitTargetNode;
    /** Whether the move-tag dialog is visible */
    ShowMoveDialog: boolean;
    /** Selected new parent tag ID for the move operation */
    MoveNewParentID: string | null;
    /** The node currently being moved */
    private moveTargetNode;
    ShowMergeIntoDialog: boolean;
    MergeSourceTag: TaxTreeNode | null;
    MergeTargetID: string | null;
    MergeTargetData: {
        ID: string;
        Label: string;
    }[];
    /** Whether the treemap drill-in panel is visible */
    ShowTreemapDrillIn: boolean;
    /** Tag node currently displayed in treemap drill-in */
    TreemapDrillInNode: TaxTreeNode | null;
    /** Cached ScheduledAction entities, keyed by normalized ID */
    private scheduledActionsCache;
    /**
     * Whether the currently selected source type is "Entity", which switches
     * the URL field to Entity/EntityDocument pickers.
     */
    /** Whether the selected source type is the Entity type (name-based check) */
    get IsEntitySourceTypeSelected(): boolean;
    /** Whether the selected source type requires Content Type / File Type selection */
    get SelectedSourceTypeRequiresContentType(): boolean;
    /** Entities that have at least one EntityDocument configured */
    get EntitiesWithDocuments(): {
        ID: string;
        Name: string;
    }[];
    /** Entity documents for the selected entity */
    get EntityDocOptionsForSelectedEntity(): {
        ID: string;
        Name: string;
    }[];
    /** Stores source-type-specific config values keyed by RequiredFields[].Key */
    FormSourceSpecificConfig: Record<string, string>;
    /** Available MJ Storage provider keys for the storage-provider-picker widget */
    StorageProviderOptions: string[];
    /**
     * The RequiredFields array from the selected source type's ConfigurationObject.
     * Drives dynamic form rendering — each field becomes a widget.
     */
    get SelectedSourceTypeFields(): MJContentSourceTypeEntity_IContentSourceTypeField[];
    /**
     * Get dependent options for a field (e.g., entity-doc-picker depends on entity-picker).
     * Returns entity documents for the entity selected in the dependent field.
     */
    GetDependentOptions(field: MJContentSourceTypeEntity_IContentSourceTypeField): {
        ID: string;
        Name: string;
    }[];
    /**
     * Handle a source-specific field value change.
     * For entity-picker: auto-select the first entity doc if only one exists.
     */
    OnSourceFieldChanged(fieldKey: string): void;
    SelectedFeedItem: ContentItemDetail | null;
    ShowItemDetail: boolean;
    SelectedSource: SourceDetailInfo | null;
    ShowSourceDetail: boolean;
    SourceDetailLoading: boolean;
    /** Filter content items in source detail by pipeline status */
    SourceDetailStatusFilter: ItemPipelineStatus | 'All';
    /** Available status options for the filter dropdown */
    readonly SourceDetailStatusOptions: (ItemPipelineStatus | 'All')[];
    /** Current page index (0-based) for source detail content list */
    SourceDetailPage: number;
    /** Number of content items per page in source detail */
    readonly SourceDetailPageSize = 10;
    /**
     * Returns source detail content items filtered by SourceDetailStatusFilter,
     * then sliced to the current page.
     */
    get FilteredSourceDetailItems(): ContentItemDetail[];
    /** Total number of filtered items (before pagination) */
    get FilteredSourceDetailTotal(): number;
    /** Total pages for source detail pagination */
    get SourceDetailTotalPages(): number;
    /** Navigate to the previous page in source detail content list */
    SourceDetailPrevPage(): void;
    /** Navigate to the next page in source detail content list */
    SourceDetailNextPage(): void;
    /** Handle change of the source detail status filter dropdown */
    OnSourceDetailStatusFilterChange(): void;
    /**
     * D4: Placeholder handler for retrying failed items in source detail.
     * Re-runs the pipeline for the current source. In the future this could
     * target only Failed items.
     */
    RetryFailedItems(): void;
    /** Returns the CSS class for a pipeline status badge color */
    GetStatusBadgeClass(status: ItemPipelineStatus): string;
    SourceTypeOptions: DropdownOption[];
    ContentTypeOptions: DropdownOption[];
    FileTypeOptions: DropdownOption[];
    AIModelOptions: DropdownOption[];
    EmbeddingModelOptions: DropdownOption[];
    VectorIndexOptions: DropdownOption[];
    AIModelVendorBranch: TreeBranchConfig;
    AllModelsLeaf: TreeLeafConfig;
    /** Branch config filtered to only vendors that have at least one embedding model */
    EmbeddingVendorBranch: TreeBranchConfig;
    EmbeddingModelsLeaf: TreeLeafConfig;
    private contentSourcesRaw;
    private contentItemsRaw;
    private contentTagsRaw;
    private contentRunsRaw;
    private contentSourceTypesRaw;
    private contentTypesRaw;
    private contentFileTypesRaw;
    private aiModelsRaw;
    private static readonly PREFS_KEY;
    private static readonly VALID_TABS;
    ngAfterViewInit(): Promise<void>;
    /**
     * D9: Read incoming configuration from ResourceData (e.g. from Analytics tag navigation).
     * Sets the initial tab and tag search query if provided.
     */
    private applyIncomingConfiguration;
    /**
     * D9: If a tagSearch parameter was provided via configuration, switch to the tags tab
     * and apply the search filter after tag data loads.
     */
    private handleInitialTagSearch;
    ngOnDestroy(): void;
    /** Report current Tags dashboard state to the agent (deep, bounded). */
    private emitAgentContext;
    /** Build the resolver candidate list from the flattened taxonomy tree. */
    private getTaxNodeCandidates;
    /**
     * 🚨 SAFETY BOUNDARY 🚨
     * Register client tools the agent can invoke on the Tags dashboard. Exposed:
     *  - SwitchClassifyTab (read-only tab navigation; tolerant tab validation)
     *  - SearchClassifyTags (filters the tag library — read-only)
     *  - RunClassificationPipeline (the documented "run autotagging" happy path — parallels
     *    RunDuplicateDetection)
     *  - SelectTaxonomyTag (id/name resolution → selects a taxonomy node, read-only)
     *  - OpenTagRecord (id/name resolution → opens the tag entity record for VIEWING)
     *  - RefreshTags / RefreshTaxonomy (read-only reloads)
     *
     * INTENTIONALLY NOT EXPOSED (gated): create / rename / edit / delete / move / merge tag,
     * promote-to-global, synonym add/remove, scope edits, and suggestion approve/merge/reject —
     * all destructive taxonomy mutations the user owns via the governance UI.
     */
    private registerAgentTools;
    GetResourceDisplayName(_data: ResourceData): Promise<string>;
    GetResourceIconClass(_data: ResourceData): Promise<string>;
    /** Wraps `NavItems` for `<mj-left-nav>`. */
    get navSections(): MJLeftNavSection[];
    /** Title rendered in the per-section `<mj-page-header-interior>`. */
    get currentTabTitle(): string;
    /** Subtitle rendered in the per-section `<mj-page-header-interior>`. */
    get currentTabSubtitle(): string;
    /** Adapter for `<mj-left-nav>`'s `(ItemClicked)` output. */
    onNavItemClicked(item: MJLeftNavItem): void;
    SwitchTab(tab: TabName): Promise<void>;
    private loadTabData;
    LoadPipelineData(): Promise<void>;
    private buildNavItems;
    private buildKPIMetrics;
    private buildPipelineStages;
    private buildFeedItems;
    private buildSourceMinis;
    /**
     * D6: Build trending tags weighted by recency (last 7 days).
     * Counts tags from ContentItemTag records created in the last 7 days
     * and weights by frequency. Falls back to all-time data if no recent tags.
     */
    private buildTrendingTags;
    /**
     * Compute scored trending tags from a given set of tag records.
     * Returns top 12 tags sorted by score (frequency x avg weight).
     */
    private computeTrendingFromTags;
    private loadSourcesData;
    private buildSourceCards;
    /** Resolve the entity record ID from the EntityRecordDocument for entity-sourced content items */
    private resolveEntityRecordID;
    /** Resolve the entity name for an entity-sourced content source */
    private resolveEntityName;
    /** Check if a source type's Configuration says RequiresFileType !== false */
    private sourceTypeRequiresFileType;
    private loadContentTypesData;
    private buildContentTypeCards;
    private loadTagLibraryData;
    private buildTagRows;
    private buildTagCloud;
    private buildTagsBySource;
    /** Convert a string ID to a CompositeKey for tree-dropdown binding */
    ToCompositeKey(id: string | null | undefined): CompositeKey | null;
    /** Extract the ID string from a CompositeKey (from tree-dropdown ValueChange) */
    FromCompositeKey(key: CompositeKey | CompositeKey[] | null): string;
    FilterTags(): void;
    /** Drill down into content items matching a specific tag */
    DrillDownTag(tagName: string): void;
    CloseDrillDownTag(): void;
    private loadRunHistoryData;
    private buildRunHistoryRows;
    private buildHistorySourceOptions;
    FilterRunHistory(): void;
    /**
     * D3/D8: Open the detail view for a specific run, loading ContentProcessRunDetail records.
     */
    OpenRunDetail(runID: string): Promise<void>;
    /** D3/D8: Close the run detail slide-in */
    CloseRunDetail(): void;
    /**
     * D2/D3/D8: Load ContentProcessRunDetail records for a given run and map to RunDetailRow[].
     */
    private loadRunDetailRows;
    /**
     * D2: Load live per-source progress for the currently running pipeline.
     * Called when the pipeline is actively running.
     */
    LoadLiveRunDetails(): Promise<void>;
    /** Map raw ContentProcessRunDetail records to RunDetailRow[] */
    private mapRunDetailRecords;
    OpenAddSourceForm(): Promise<void>;
    OpenEditSourceForm(card: SourceCard): Promise<void>;
    SaveSource(): Promise<void>;
    DeleteSource(card: SourceCard): Promise<void>;
    /**
     * Opens the schedule dialog pre-filled for the given source card.
     * Defaults to a daily 2 AM cron expression.
     * @param card The source card to create a schedule for
     */
    OpenScheduleDialog(card: SourceCard): void;
    /** Closes the schedule dialog without saving */
    CloseScheduleDialog(): void;
    /**
     * Saves a new ScheduledAction for the current source, links it
     * via ContentSource.ScheduledActionID, and creates the default
     * action params for the Autotag and Vectorize action.
     */
    SaveSchedule(): Promise<void>;
    /**
     * Removes the schedule from a source card by unlinking the ScheduledActionID.
     * @param card The source card to remove the schedule from
     */
    RemoveSchedule(card: SourceCard): Promise<void>;
    /**
     * Returns the human-readable schedule description for a source card.
     * Used in the template to display schedule indicators.
     */
    GetScheduleLabel(card: SourceCard): string;
    /**
     * Returns a human-readable preview of a cron expression for the schedule dialog.
     * @param cron The cron expression to preview
     */
    GetCronPreview(cron: string): string;
    /** Looks up the cron expression for a cached ScheduledAction by ID */
    private getScheduledActionCron;
    /**
     * Loads ScheduledAction entities referenced by content sources into the local cache.
     * Called during initial data load so cron descriptions are available for card rendering.
     */
    private loadScheduledActionsForSources;
    /** Finds the Action ID for "Autotag and Vectorize Content" by querying actions */
    private findAutotagActionID;
    /**
     * Creates a ScheduledActionParam that passes the source ID to the action.
     * Looks up the "EntityNames" action param and sets the source ID as its value.
     */
    private createSourceIDParam;
    /**
     * Links (or unlinks) a ScheduledAction to a ContentSource by updating
     * the ContentSource.ScheduledActionID field.
     */
    private linkScheduleToSource;
    OpenAddTypeForm(): Promise<void>;
    OpenEditTypeForm(card: ContentTypeCard): Promise<void>;
    SaveContentType(): Promise<void>;
    CloseForm(): void;
    /**
     * Run the pipeline for a specific content source only.
     */
    RunPipelineForSource(contentSourceID: string): Promise<void>;
    RunPipeline(contentSourceIDs?: string[]): Promise<void>;
    /**
     * Pause a running classification pipeline. The server sets CancellationRequested
     * on the process run so the engine pauses gracefully after the current batch.
     */
    PausePipeline(): Promise<void>;
    /**
     * Resume a paused classification pipeline from its last completed offset.
     */
    ResumePipeline(): Promise<void>;
    /**
     * Cancel a running or paused pipeline. Uses pause mechanism and resets UI state.
     */
    CancelPipeline(): Promise<void>;
    private subscribeToPipelineProgress;
    private updateStagesForActiveRun;
    private formatStageName;
    private countTagsByItem;
    private getTopTagsForItem;
    private countItemsBySource;
    private countTagsBySource;
    private getLastRunBySource;
    private countAllTags;
    /** Compute average weight per tag across all occurrences */
    private computeAvgWeights;
    private getTopSourcePerTag;
    private getFirstSeenPerTag;
    private countSourcesByContentType;
    private countItemsByContentType;
    private inferItemStatus;
    formatRelativeTime(dateStr: string | null | undefined): string;
    formatNumber(n: number): string;
    /** Returns font size in rem for a tag based on its weight (0.0-1.0). Range: 0.7rem to 1.0rem */
    TagFontSize(weight: number): string;
    /** Format weight as percentage for display in tag chip */
    FormatWeight(weight: number): string;
    private formatShortDate;
    private formatDate;
    private computeDuration;
    private displayStatus;
    GetSourceTypeIcon(typeName: string): string;
    private ensureBaseDataLoaded;
    private ensureFormDropdownsLoaded;
    private resetSourceForm;
    private resetTypeForm;
    private refreshSourcesTab;
    private refreshContentTypesTab;
    OpenFeedItemDetail(index: number): void;
    OpenContentItemDetail(item: ContentItemDetail): void;
    /** Open content item detail slide-in by content item ID (used from tag drill-down) */
    OpenItemDetailByID(contentItemID: string): void;
    CloseItemDetail(): void;
    OpenRecordFromItem(item: ContentItemDetail): void;
    /** Cache: EntityRecordDocument ID → RecordID */
    private entityRecordDocCache;
    /** Load ERD RecordIDs for entity-sourced content items */
    private loadEntityRecordDocCache;
    private getAllWeightedTagsForItem;
    /**
     * D4: Infer embedding and tagging pipeline statuses for a content item.
     * Uses tag count and run history heuristics since dedicated status columns
     * are not yet available on the ContentItem entity.
     */
    private inferPipelineStatuses;
    /** Map a raw status string to the ItemPipelineStatus union */
    private mapStatusString;
    OpenSourceDetail(card: SourceCard): Promise<void>;
    CloseSourceDetail(): void;
    OpenRecordFromSource(source: SourceDetailInfo): void;
    OpenEditSourceFromDetail(): void;
    RunSourceFromDetail(): Promise<void>;
    DeleteSourceFromDetail(): Promise<void>;
    private loadContentItemsForSource;
    private loadRunHistoryForSource;
    private resolveEmbeddingModelName;
    private resolveVectorIndexName;
    /** Taxonomy sub-tabs as `TabConfig[]` for `<mj-tab-nav>`. Counts on
     *  Duplicates and Orphans surface as warning/error-variant badges. */
    get taxSubTabsConfig(): TabConfig[];
    /** Adapter for `<mj-tab-nav>`'s string-typed `(TabChange)` output. */
    onTaxSubTabChange(key: string): void;
    SwitchTaxSubTab(sub: TaxonomySubTab): void;
    /**
     * Loads all data needed by the Taxonomy Governance sub-tabs:
     * Tags, Tagged Items, Tag Audit Logs, and cross-references content item tags.
     */
    private loadTaxonomyData;
    /**
     * Server-driven replacement for buildTaxDuplicates(). Reads pending
     * `MJ:Tag Suggestions` rows with `Reason='MergeCandidate'` and projects
     * each into the `TaxDuplicatePair` shape the existing Duplicates sub-tab
     * UI expects. The UI's "Merge" button still calls `MergeTags()` (which
     * we now route through the server-side `PromoteTagSuggestion` mutation
     * via the wrapper below).
     */
    private populateDuplicatesFromSuggestions;
    /**
     * Server-driven replacement for buildTaxOrphans(). Reads pending
     * `MJ:Tag Suggestions` with `Reason='LowUsage'` and projects each into
     * the `TaxOrphanCard` shape. The existing Orphans UI's bulk-delete
     * actions still hit `MJ:Tags.Delete()` directly — those go through the
     * server-side `MJTagEntityServer` Delete override (which cleans up FK
     * references) so the workflow is unchanged.
     */
    private populateOrphansFromSuggestions;
    /**
     * Builds the taxonomy tree from raw tag data, wiring up parent-child
     * relationships and attaching item counts and average weights.
     */
    private buildTaxTree;
    private computeTreeDepths;
    private propagateItemCounts;
    private sortTreeNodes;
    private flattenTree;
    private computeTagHealth;
    /**
     * Counts the number of items referencing each tag, combining both
     * Tagged Items and Content Item Tags. Uses NormalizeUUID for
     * cross-platform UUID case consistency.
     */
    private countItemsByTag;
    /**
     * Loads per-tag average weights and item counts via a server-side SQL aggregation.
     * This avoids loading all 17K+ TaggedItem rows to the browser.
     */
    /**
     * Loads per-tag average weights and item counts via the "Tag Aggregates" saved query.
     * This runs a SQL GROUP BY on the server, avoiding loading 17K+ TaggedItem rows to the browser.
     */
    private loadTagAggregates;
    ToggleTaxNode(node: TaxTreeNode): void;
    SelectTaxNode(node: TaxTreeNode): void;
    /**
     * MJTagEntity backing the currently-selected TaxTreeNode. Exposed for the
     * governance / scope / synonyms detail panel that lives below the existing
     * Tree right-side detail. Populated by `syncSelectedTagFull` whenever the
     * tree selection changes.
     */
    SelectedTagFull: MJTagEntity | null;
    private syncSelectedTagFull;
    IsGlobalLocked(): boolean;
    /**
     * Toggle a per-tag governance flag. Persists via a fresh MJTagEntity load
     * + Save so the server-side `MJTagEntityServer` Save() override can run
     * the IsGlobal⊕TagScope invariant check.
     */
    ToggleGovernanceFlag(field: 'AllowAutoGrow' | 'IsFrozen' | 'RequiresReview' | 'IsGlobal'): Promise<void>;
    SaveSelectedTagNumber(field: 'MaxChildren' | 'MaxDescendantDepth' | 'MinWeight', raw: string): Promise<void>;
    private persistTagField;
    /** Inline synonym add. Server-side TagSynonymEntity Save handles uniqueness. */
    AddSynonym(): Promise<void>;
    RemoveSynonym(row: SynonymRow): Promise<void>;
    /**
     * Open the scope dialog. Implementation note: a full dual-pane scope
     * editor is on the roadmap; for the first pass we surface a simple
     * "remove all scope rows + flip to global" button so admins can promote
     * a scoped tag to global, plus a notification pointing them to the
     * Suggestions/admin path for finer-grained scope assignments.
     */
    OpenScopeDialog(): Promise<void>;
    FilterTaxTree(): void;
    private applyTaxTreeFilter;
    GetTaxBreadcrumb(node: TaxTreeNode): {
        ID: string;
        Name: string;
    }[];
    NavigateToBreadcrumb(tagId: string): void;
    private loadRecentItemsForTag;
    StartEditTag(): void;
    CancelEditTag(): void;
    SaveEditTag(): Promise<void>;
    MoveTag(node: TaxTreeNode, newParentId: string | null): Promise<void>;
    DeleteTag(node: TaxTreeNode): void;
    /** Open create tag dialog for a root-level tag */
    OpenCreateRootTag(): void;
    /** Open create tag dialog as child of the selected node */
    OpenCreateChildTag(): void;
    /** Open create tag dialog as child of a specific node */
    OpenCreateChildTagFor(node: TaxTreeNode): void;
    /** Close create tag dialog */
    CloseCreateTagDialog(): void;
    /** Save the new tag */
    SaveNewTag(): Promise<void>;
    /** Toggle multi-select mode on/off */
    ToggleMultiSelectMode(): void;
    /** Toggle a node's selection in multi-select mode */
    ToggleNodeSelection(node: TaxTreeNode, event: Event): void;
    /** Check if a node is selected in multi-select mode */
    IsNodeMultiSelected(nodeID: string): boolean;
    /** Handle drag start on a tree node */
    OnTreeNodeDragStart(event: DragEvent, node: TaxTreeNode): void;
    /** Handle drag over a tree node (drop target) */
    OnTreeNodeDragOver(event: DragEvent, node: TaxTreeNode): void;
    /** Handle drag leave */
    OnTreeNodeDragLeave(): void;
    /** Handle drop — reparent dragged node(s) under the drop target */
    OnTreeNodeDrop(event: DragEvent, targetNode: TaxTreeNode): Promise<void>;
    /** Handle drop on the "Root" drop zone (make root-level) */
    OnDropToRoot(event: DragEvent): Promise<void>;
    IsMerging: boolean;
    MergeTags(sourceTagId: string, targetTagId: string, sourceName: string, targetName: string): Promise<void>;
    MakeChildTag(childTagId: string, parentTagId: string): Promise<void>;
    DismissDuplicate(pair: TaxDuplicatePair): void;
    private buildTaxDuplicates;
    /** Group tags by case-insensitive name, returning only groups with 2+ members */
    private groupExactNameDuplicates;
    /**
     * Enhanced string similarity: separator normalization, abbreviation,
     * pluralization, token-overlap Jaccard, containment, and Levenshtein.
     * Returns the highest score among all heuristics.
     */
    private computeStringSimilarity;
    /** Scores simple English pluralization differences */
    private computePluralizationScore;
    /** Jaccard similarity on word tokens */
    private computeTokenJaccardSimilarity;
    private isAbbreviationOf;
    private levenshteinDistance;
    /**
     * Finds orphan tags: no parent, no children, and zero usage.
     * A tag with even 1 connection is not orphaned — it's just a leaf.
     * Uses NormalizeUUID for consistent cross-platform UUID comparisons.
     */
    private buildTaxOrphans;
    ToggleOrphanSelection(orphan: TaxOrphanCard): void;
    ToggleAllOrphans(): void;
    /**
     * Clean up TagCoOccurrence records that reference a tag before deleting it.
     * Without this, the FK constraint on TagCoOccurrence blocks the delete.
     */
    private cleanupTagReferences;
    DeleteOrphan(orphan: TaxOrphanCard): void;
    BulkDeleteOrphans(): void;
    /** Delete all orphan tags at once with a styled confirmation dialog */
    DeleteAllOrphans(): void;
    private buildTaxTreemap;
    /**
     * Builds the audit timeline from the MJ: Tag Audit Logs entity.
     * Falls back to synthesizing "created" events from tag creation dates
     * if no real audit log records exist yet.
     */
    private buildTaxAuditLog;
    /**
     * Maps real Tag Audit Log records into TaxAuditEvent objects.
     * Uses the view's denormalized Tag, PerformedByUser, and RelatedTag fields.
     */
    private buildAuditEventsFromLogs;
    /**
     * Synthesizes audit events from tag __mj_CreatedAt dates when no
     * real audit log records exist. Used as a fallback only.
     */
    private synthesizeAuditEventsFromTags;
    /**
     * Maps a PascalCase DB Action value (e.g. "Renamed") to the lowercase
     * TaxAuditAction type used in the UI. Returns null for unrecognized values.
     */
    private mapAuditActionToType;
    /**
     * Safely parses the JSON Details column from a Tag Audit Log record.
     * Returns an empty object on parse failure.
     */
    private parseAuditDetails;
    /**
     * Builds a human-readable description for an audit event based on the
     * action type and any additional context from the related tag or details JSON.
     */
    private buildAuditDescription;
    /**
     * Adds a local-only audit entry to the top of the timeline for
     * immediate UI feedback after a user action (merge, delete, etc.).
     */
    private addTaxAuditEntry;
    ToggleTaxAuditFilter(type: string): void;
    GetFilteredAuditEvents(): TaxAuditEvent[];
    private formatDayHeader;
    private buildTaxHealth;
    /** Returns the Font Awesome icon class for an audit event action type. */
    GetTaxAuditIcon(type: string): string;
    /** Opens a styled confirmation dialog, replacing browser `confirm()`. */
    OpenConfirmDialog(title: string, message: string, action: () => Promise<void>): void;
    /** User confirmed the dialog action */
    ConfirmDialogAccept(): Promise<void>;
    /** User cancelled the confirmation dialog */
    ConfirmDialogCancel(): void;
    /** Opens the split-tag dialog for a given tree node */
    OpenSplitDialog(node: TaxTreeNode): void;
    /** Closes the split-tag dialog without action */
    CloseSplitDialog(): void;
    /** Executes the split operation, creating child tags from comma-separated names */
    ExecuteSplit(): Promise<void>;
    /** Opens the move-tag dialog for a given tree node */
    OpenMoveDialog(node: TaxTreeNode): void;
    /** Closes the move-tag dialog without action */
    CloseMoveDialog(): void;
    /** Returns flat list of tags eligible as move targets (excludes the node being moved and its descendants) */
    GetMoveTargetOptions(): {
        ID: string;
        Name: string;
        Depth: number;
    }[];
    /** Collects IDs of all descendants of a node */
    private collectDescendantIds;
    /** Executes the move operation */
    ExecuteMove(): Promise<void>;
    OpenMergeIntoDialog(node: TaxTreeNode): void;
    OnMergeTargetSelected(value: unknown): void;
    CloseMergeIntoDialog(): void;
    /** Returns flat list of tags eligible as merge targets (excludes the source tag) */
    GetMergeTargetOptions(): {
        ID: string;
        Name: string;
        Depth: number;
        ItemCount: number;
    }[];
    ExecuteMergeInto(): Promise<void>;
    /** Opens treemap drill-in panel for the given cell */
    OpenTreemapDrillIn(cell: TaxTreemapCell): void;
    /** Closes treemap drill-in panel */
    CloseTreemapDrillIn(): void;
    /** Navigate from treemap drill-in to the tag in the tree view */
    DrillInToTreeView(node: TaxTreeNode): void;
    RefreshTaxonomyData(): Promise<void>;
    /** Load pending content item duplicates for review */
    LoadContentDuplicates(): Promise<void>;
    /** Resolve content source names for a list of content item IDs */
    private resolveItemSourceNames;
    /** Confirm a content duplicate pair */
    ConfirmContentDuplicate(dupRow: ContentDuplicateRow): Promise<void>;
    /** Dismiss a content duplicate pair */
    DismissContentDuplicate(dupRow: ContentDuplicateRow): Promise<void>;
    /** Load all pending suggestions and refresh the filtered view + nav badge. */
    loadSuggestions(): Promise<void>;
    /** Walk the parent chain to build a › -separated breadcrumb for a tag. */
    private computeTagPath;
    /**
     * Coerce the min-score number-input event into either a number or null,
     * then re-apply filters. Inline arrow expressions in the template can't
     * disambiguate `Event` vs `number` so we route through this helper.
     */
    OnMinScoreChange(value: number | string | null | undefined): void;
    applySuggestionFilters(): void;
    ToggleSuggestionSelected(row: SuggestionRow, ev: Event): void;
    ToggleAllSuggestions(checked: boolean): void;
    SelectedSuggestionCount(): number;
    SelectSuggestion(row: SuggestionRow): void;
    CloseDrawer(): void;
    /** Server-driven: routes through TagGovernanceResolver mutations. */
    DispositionSuggestion(row: SuggestionRow, kind: 'create-new' | 'merge' | 'reject'): Promise<void>;
    BulkApprove(): Promise<void>;
    BulkReject(): Promise<void>;
    ReasonClass(reason: string): string;
    RunHealthNow(): Promise<void>;
    RebuildEmbeddings(): Promise<void>;
    static ɵfac: i0.ɵɵFactoryDeclaration<TagsResourceComponent, never>;
    static ɵcmp: i0.ɵɵComponentDeclaration<TagsResourceComponent, "mj-tags-resource", never, {}, {}, never, never, false, never>;
}
export declare function LoadTagsResource(): void;
/**
 * Converts a 5-part or 6-part cron expression to a human-readable English string.
 *
 * Handles common patterns:
 *   `0 * * * *`      -> "Every hour"
 *   `0 2 * * *`      -> "Daily at 2:00 AM"
 *   `0 2 * * 1`      -> "Weekly on Monday at 2:00 AM"
 *   `star/15 * * * *` -> "Every 15 minutes"  (where star = asterisk)
 *   `0 0 1 * *`      -> "Monthly on day 1 at 12:00 AM"
 *
 * Falls back to returning the raw cron string for unrecognized patterns.
 *
 * @param cron A cron expression string (5 or 6 parts)
 * @returns A human-readable description or the raw cron if unrecognized
 */
export declare function CronToHumanReadable(cron: string): string;
export {};
//# sourceMappingURL=tags-resource.component.d.ts.map