var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
/**
 * @fileoverview Knowledge Hub Clusters Dashboard Tab
 *
 * Full-page cluster visualization with:
 * - Left sidebar: saved cluster visualizations (persisted via UserInfoEngine)
 * - Main area: SVG scatter plot (mj-cluster-scatter)
 * - Floating config panel (mj-cluster-config-panel)
 * - Top metrics bar
 *
 * Registered as BaseResourceComponent for the Knowledge Hub application.
 */
import { Component, ChangeDetectorRef, inject, ViewChild } from '@angular/core';
import { Subject } from 'rxjs';
import { CompositeKey, Metadata } from '@memberjunction/core';
import { UserInfoEngine, KnowledgeHubMetadataEngine } from '@memberjunction/core-entities';
import { RegisterClass } from '@memberjunction/global';
import { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';
import { GraphQLAIClient } from '@memberjunction/graphql-dataprovider';
import { AIEngineBase } from '@memberjunction/ai-engine-base';
import { DefaultClusterConfig, } from '@memberjunction/ng-clustering';
import { ClusteringService } from '@memberjunction/ng-clustering';
import * as i0 from "@angular/core";
import * as i1 from "@memberjunction/ng-clustering";
const _c0 = ["scatterPlot"];
const _c1 = () => [];
const _c2 = () => ["Entity", "Name", "Description"];
function ClusterVisualizationResourceComponent_For_8_Template(rf, ctx) { if (rf & 1) {
    const _r2 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "div", 16);
    i0.ɵɵlistener("click", function ClusterVisualizationResourceComponent_For_8_Template_div_click_0_listener() { const saved_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnSelectSaved(saved_r3)); });
    i0.ɵɵelementStart(1, "div", 17);
    i0.ɵɵtext(2);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(3, "div", 18)(4, "span");
    i0.ɵɵtext(5);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(6, "span");
    i0.ɵɵtext(7);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(8, "button", 19);
    i0.ɵɵlistener("click", function ClusterVisualizationResourceComponent_For_8_Template_button_click_8_listener($event) { const saved_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnDeleteSaved(saved_r3, $event)); });
    i0.ɵɵelement(9, "i", 20);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const saved_r3 = ctx.$implicit;
    const ctx_r3 = i0.ɵɵnextContext();
    i0.ɵɵclassProp("active", ctx_r3.IsActiveSaved(saved_r3));
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(saved_r3.Name);
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(saved_r3.EntityName);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(ctx_r3.FormatDate(saved_r3.CreatedAt));
} }
function ClusterVisualizationResourceComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 7);
    i0.ɵɵelement(1, "i", 21);
    i0.ɵɵelementStart(2, "p");
    i0.ɵɵtext(3, "No saved visualizations yet");
    i0.ɵɵelementEnd()();
} }
function ClusterVisualizationResourceComponent_Conditional_17_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 22);
    i0.ɵɵelement(1, "i", 23);
    i0.ɵɵtext(2, " Records: ");
    i0.ɵɵelementStart(3, "span", 24);
    i0.ɵɵtext(4);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(5, "div", 22);
    i0.ɵɵelement(6, "i", 25);
    i0.ɵɵtext(7, " Clusters: ");
    i0.ɵɵelementStart(8, "span", 24);
    i0.ɵɵtext(9);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(10, "div", 22);
    i0.ɵɵelement(11, "i", 26);
    i0.ɵɵtext(12, " Silhouette: ");
    i0.ɵɵelementStart(13, "span", 24);
    i0.ɵɵtext(14);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(15, "div", 22);
    i0.ɵɵelement(16, "i", 27);
    i0.ɵɵtext(17, " Computed: ");
    i0.ɵɵelementStart(18, "span", 24);
    i0.ɵɵtext(19);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const ctx_r3 = i0.ɵɵnextContext();
    i0.ɵɵadvance(4);
    i0.ɵɵtextInterpolate(ctx_r3.Result.Metrics.RecordCount);
    i0.ɵɵadvance(5);
    i0.ɵɵtextInterpolate(ctx_r3.Result.Metrics.ClusterCount);
    i0.ɵɵadvance(4);
    i0.ɵɵclassProp("good", ctx_r3.IsSilhouetteGood);
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate1(" ", ctx_r3.SilhouetteScoreFormatted, " ");
    i0.ɵɵadvance(5);
    i0.ɵɵtextInterpolate(ctx_r3.ComputationTimeFormatted);
} }
const SAVED_CLUSTERS_KEY = 'KnowledgeHub_SavedClusters';
let ClusterVisualizationResourceComponent = class ClusterVisualizationResourceComponent extends BaseResourceComponent {
    scatterPlot;
    cdr = inject(ChangeDetectorRef);
    clusteringService = inject(ClusteringService);
    navigationService = inject(NavigationService);
    destroy$ = new Subject();
    /** LLM-generated cluster labels for the current result */
    ClusterLabels = [];
    // ================================================================
    // Resource overrides
    // ================================================================
    async GetResourceDisplayName(_data) {
        return 'Clusters';
    }
    async GetResourceIconClass(_data) {
        return 'fa-solid fa-circle-nodes';
    }
    // ================================================================
    // State
    // ================================================================
    /** Current visualization result */
    Result = null;
    /** Whether a clustering run is in progress */
    IsRunning = false;
    /** The active cluster config */
    ActiveConfig = DefaultClusterConfig();
    /** Title for the top bar */
    VisualizationTitle = 'New Cluster Analysis';
    /** Entity options for the config panel dropdown */
    EntityOptions = [];
    /** Entity document options for the selected entity (shown when 2+) */
    EntityDocOptions = [];
    // Saved visualizations
    SavedVisualizations = [];
    ActiveSavedId = null;
    // User setting entity for persistence
    userSettingEntity = null;
    // ================================================================
    // Lifecycle
    // ================================================================
    async ngAfterViewInit() {
        await this.loadEntityOptions();
        this.loadSavedVisualizations();
    }
    ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
    }
    // ================================================================
    // Getters
    // ================================================================
    get Metrics() {
        return this.Result?.Metrics ?? null;
    }
    get HasResult() {
        return this.Result != null && this.Result.Points.length > 0;
    }
    get SilhouetteScoreFormatted() {
        return this.Result?.Metrics?.SilhouetteScore?.toFixed(2) ?? '--';
    }
    get IsSilhouetteGood() {
        return (this.Result?.Metrics?.SilhouetteScore ?? 0) >= 0.5;
    }
    get ComputationTimeFormatted() {
        const ms = this.Result?.Metrics?.ComputationTimeMs ?? 0;
        return ms >= 1000 ? `${(ms / 1000).toFixed(1)}s` : `${ms}ms`;
    }
    // ================================================================
    // Public Methods
    // ================================================================
    /** Handle "Run Clustering" from the config panel */
    async OnRunClustering(config) {
        this.IsRunning = true;
        this.ActiveConfig = config;
        this.ClusterLabels = [];
        // Update entity doc options if entity changed
        this.updateEntityDocOptions(config.EntityName);
        this.cdr.detectChanges();
        try {
            // Fetch vectors from the vector database
            const vectors = await this.fetchVectorsForEntity(config);
            // Run clustering (client-side UMAP + K-Means/DBSCAN)
            this.Result = await this.clusteringService.RunClustering(vectors, config);
            this.VisualizationTitle = `${config.EntityName} — ${config.Algorithm === 'kmeans' ? 'K-Means' : 'DBSCAN'}`;
            // Fire LLM cluster naming in the background (non-blocking).
            // Clusters render immediately; labels appear when LLM responds.
            this.requestClusterLabelsFromLLM().catch(err => console.warn('[ClusterVisualization] Background naming failed:', err));
        }
        catch (error) {
            console.error('[ClusterVisualization] Pipeline error:', error);
            this.Result = null;
        }
        finally {
            this.IsRunning = false;
            this.cdr.detectChanges();
        }
    }
    /** Handle point click — log for now */
    OnPointClicked(_point) {
        // Detail panel is handled by the scatter component internally
    }
    /** Handle point hover */
    OnPointHovered(_point) {
        // Tooltip display handled by scatter component
    }
    /** Navigate to the entity record when "Open Record" is clicked in the detail panel */
    OnOpenRecord(point) {
        const entityName = point.Metadata?.['Entity'];
        const recordID = point.Metadata?.['RecordID'];
        if (!entityName || !recordID)
            return;
        const compositeKey = new CompositeKey();
        compositeKey.SimpleLoadFromURLSegment(recordID);
        this.navigationService.OpenEntityRecord(entityName, compositeKey);
    }
    /** Save the current visualization */
    async OnSaveVisualization() {
        if (!this.Result)
            return;
        const id = crypto.randomUUID();
        const saved = {
            Id: id,
            Name: this.VisualizationTitle,
            EntityName: this.ActiveConfig.EntityName,
            Algorithm: this.ActiveConfig.Algorithm,
            Params: { ...this.ActiveConfig },
            CreatedAt: new Date().toISOString(),
            Result: this.stripVectorsFromResult(this.Result),
            Viewport: this.scatterPlot?.GetViewportTransform(),
            ClusterLabels: this.ClusterLabels.length > 0 ? [...this.ClusterLabels] : undefined,
        };
        this.SavedVisualizations = [saved, ...this.SavedVisualizations];
        this.ActiveSavedId = id;
        await this.persistSavedVisualizations();
        this.cdr.detectChanges();
    }
    /** Select a saved visualization — restore from cache if available, otherwise re-run */
    async OnSelectSaved(saved) {
        this.ActiveSavedId = saved.Id;
        this.VisualizationTitle = saved.Name;
        // Reconstruct config
        const config = {
            ...DefaultClusterConfig(),
            ...saved.Params,
            EntityName: saved.EntityName,
            Algorithm: saved.Algorithm,
        };
        this.ActiveConfig = config;
        // If we have cached results, restore instantly without re-running
        if (saved.Result) {
            this.Result = saved.Result;
            this.ClusterLabels = saved.ClusterLabels ?? [];
            this.applyLabelsToResult();
            this.cdr.detectChanges();
            // Restore viewport after a tick (scatter needs to render first)
            if (saved.Viewport) {
                setTimeout(() => {
                    this.scatterPlot?.SetViewportTransform(saved.Viewport);
                    this.cdr.detectChanges();
                }, 50);
            }
            return;
        }
        // No cached results — re-run from scratch
        await this.OnRunClustering(config);
    }
    /** Delete a saved visualization */
    async OnDeleteSaved(saved, event) {
        event.stopPropagation();
        this.SavedVisualizations = this.SavedVisualizations.filter(s => s.Id !== saved.Id);
        if (this.ActiveSavedId === saved.Id) {
            this.ActiveSavedId = null;
        }
        await this.persistSavedVisualizations();
        this.cdr.detectChanges();
    }
    /** Start a new analysis (clear current) */
    OnNewAnalysis() {
        this.ActiveSavedId = null;
        this.Result = null;
        this.ClusterLabels = [];
        this.VisualizationTitle = 'New Cluster Analysis';
        this.ActiveConfig = DefaultClusterConfig();
        this.cdr.detectChanges();
    }
    IsActiveSaved(saved) {
        return this.ActiveSavedId === saved.Id;
    }
    TrackSavedBy(_index, saved) {
        return saved.Id;
    }
    FormatDate(iso) {
        try {
            return new Date(iso).toLocaleDateString();
        }
        catch {
            return iso;
        }
    }
    // ================================================================
    // Private Methods
    // ================================================================
    /** Populate the entity options for the config panel dropdown */
    async loadEntityOptions() {
        try {
            // Use KnowledgeHubMetadataEngine for cached entity document data
            const engine = KnowledgeHubMetadataEngine.Instance;
            await engine.Config(false);
            const entityNames = engine.GetEntitiesWithDocuments();
            if (entityNames.length > 0) {
                this.EntityOptions = entityNames.map(name => ({ Name: name }));
            }
            // Set default entity if config is blank
            if (this.EntityOptions.length > 0 && !this.ActiveConfig.EntityName) {
                this.ActiveConfig.EntityName = this.EntityOptions[0].Name;
            }
            // Populate entity doc options for the default entity
            this.updateEntityDocOptions(this.ActiveConfig.EntityName);
        }
        catch (error) {
            console.warn('[ClusterVisualization] Error loading entity options:', error);
        }
        this.cdr.detectChanges();
    }
    /** Update entity document options when the selected entity changes */
    updateEntityDocOptions(entityName) {
        if (!entityName) {
            this.EntityDocOptions = [];
            return;
        }
        const engine = KnowledgeHubMetadataEngine.Instance;
        const docs = engine.GetEntityDocumentsForEntity(entityName)
            .filter(d => d.Status === 'Active');
        this.EntityDocOptions = docs.map(d => ({ ID: d.ID, Name: d.Name }));
        // Auto-select the first doc (or clear if none)
        if (docs.length > 0 && !this.ActiveConfig.EntityDocumentID) {
            this.ActiveConfig.EntityDocumentID = docs[0].ID;
        }
        else if (docs.length === 0) {
            this.ActiveConfig.EntityDocumentID = '';
        }
    }
    /**
     * Fetch vectors from Entity Document Runs via RunView.
     * Entity Documents store vectorized entity data; we query for runs
     * that match the requested entity.
     */
    async fetchVectorsForEntity(config) {
        // Use the selected entity document, or fall back to first active one
        let entityDocID = config.EntityDocumentID;
        if (!entityDocID) {
            const engine = KnowledgeHubMetadataEngine.Instance;
            const entityDocs = engine.GetEntityDocumentsForEntity(config.EntityName)
                .filter(d => d.Status === 'Active');
            if (entityDocs.length === 0) {
                return [];
            }
            entityDocID = entityDocs[0].ID;
        }
        // Fetch vectors + metadata directly from the vector database (Pinecone)
        const provider = Metadata.Provider;
        const aiClient = new GraphQLAIClient(provider);
        const result = await aiClient.FetchEntityVectors({
            entityDocumentID: entityDocID,
            maxRecords: config.MaxRecords,
            filter: config.Filter || undefined,
        });
        if (!result.Success || result.Results.length === 0) {
            return [];
        }
        // Convert vector DB results to ClusterInputVector format
        const vectors = [];
        for (const item of result.Results) {
            if (!item.Values || item.Values.length === 0)
                continue;
            const metadata = this.parseVectorMetadata(item.Metadata);
            const label = this.buildLabel(metadata);
            vectors.push({
                Key: item.ID,
                Label: label,
                Vector: item.Values,
                Metadata: metadata,
            });
        }
        return vectors;
    }
    /** Parse the JSON metadata string from the vector DB into a record */
    parseVectorMetadata(metadataJson) {
        try {
            return JSON.parse(metadataJson);
        }
        catch {
            return {};
        }
    }
    /** Build a human-readable label from vector metadata (prefers Name field, falls back to RecordID) */
    buildLabel(metadata) {
        // The vector sync process stores display fields directly in metadata.
        // Prefer Name, then Title, then Description, then fall back to RecordID.
        return metadata['Name']
            || metadata['Title']
            || metadata['Description']?.substring(0, 60)
            || metadata['RecordID']
            || 'Unknown';
    }
    // ================================================================
    // LLM Cluster Naming
    // ================================================================
    /**
     * After clustering completes, send sample records per cluster to an LLM for naming.
     * Updates ClusterLabels and refreshes the view.
     */
    async requestClusterLabelsFromLLM() {
        if (!this.Result || this.Result.Clusters.length === 0)
            return;
        try {
            const clusterData = this.buildClusterDataForPrompt(this.Result);
            if (!clusterData)
                return;
            // Look up the "Cluster Naming" prompt from AIEngineBase cached metadata
            const promptEntity = AIEngineBase.Instance.Prompts.find(p => p.Name === 'Cluster Naming');
            if (!promptEntity) {
                console.warn('[ClusterVisualization] "Cluster Naming" prompt not found — run metadata sync');
                return;
            }
            const provider = Metadata.Provider;
            const aiClient = new GraphQLAIClient(provider);
            const result = await aiClient.RunAIPrompt({
                promptId: promptEntity.ID,
                data: { clusterData },
            });
            if (result.success && result.parsedResult) {
                const labels = result.parsedResult;
                this.ClusterLabels = labels.map(l => ({
                    ClusterId: l.clusterId,
                    Label: l.label,
                    IsUserEdited: false,
                }));
                this.applyLabelsToResult();
                this.cdr.detectChanges();
            }
        }
        catch (error) {
            console.warn('[ClusterVisualization] LLM cluster naming failed:', error);
        }
    }
    /** Build a text block describing sample records per cluster for the LLM prompt */
    buildClusterDataForPrompt(result) {
        if (!result.Clusters || result.Clusters.length === 0)
            return null;
        const lines = [];
        for (const cluster of result.Clusters) {
            // Get points in this cluster, take up to 5 samples
            const clusterPoints = result.Points.filter(p => p.ClusterId === cluster.Id);
            const samples = clusterPoints.slice(0, 5);
            if (samples.length === 0)
                continue;
            lines.push(`### Cluster ${cluster.Id} (${clusterPoints.length} records)`);
            for (const point of samples) {
                const meta = point.Metadata || {};
                // Include the most informative metadata fields
                const fields = ['Name', 'Title', 'Description', 'Entity', 'Status', 'Type']
                    .filter(f => meta[f])
                    .map(f => `${f}: ${meta[f]}`)
                    .join(', ');
                lines.push(`- ${fields || point.Label}`);
            }
            lines.push('');
        }
        return lines.join('\n');
    }
    /** Apply cluster labels to the result's Clusters array (sets the Label property) */
    applyLabelsToResult() {
        if (!this.Result || this.ClusterLabels.length === 0)
            return;
        for (const label of this.ClusterLabels) {
            const cluster = this.Result.Clusters.find(c => c.Id === label.ClusterId);
            if (cluster) {
                cluster.Label = label.Label;
            }
        }
    }
    /**
     * Strip full vector arrays from the result before saving (they're large and not needed for display).
     * Keeps points with 2D coordinates and metadata but removes the original high-dimensional vectors.
     */
    stripVectorsFromResult(result) {
        return {
            ...result,
            Points: result.Points.map(p => ({
                ...p,
                Vector: [], // Strip the high-dimensional vector — only 2D coords (X, Y) matter for display
            })),
        };
    }
    /** Load saved visualizations from UserInfoEngine settings */
    loadSavedVisualizations() {
        try {
            const engine = UserInfoEngine.Instance;
            const setting = engine.UserSettings.find(s => s.Setting === SAVED_CLUSTERS_KEY);
            if (setting?.Value) {
                this.userSettingEntity = setting;
                this.SavedVisualizations = JSON.parse(setting.Value);
            }
        }
        catch (error) {
            console.warn('[ClusterVisualization] Error loading saved visualizations:', error);
            this.SavedVisualizations = [];
        }
        this.cdr.detectChanges();
    }
    /** Persist saved visualizations to UserInfoEngine settings */
    async persistSavedVisualizations() {
        try {
            const md = new Metadata();
            const userId = md.CurrentUser?.ID;
            if (!userId)
                return;
            if (!this.userSettingEntity) {
                const engine = UserInfoEngine.Instance;
                const existing = engine.UserSettings.find(s => s.Setting === SAVED_CLUSTERS_KEY);
                if (existing) {
                    this.userSettingEntity = existing;
                }
                else {
                    this.userSettingEntity = await md.GetEntityObject('MJ: User Settings');
                    this.userSettingEntity.UserID = userId;
                    this.userSettingEntity.Setting = SAVED_CLUSTERS_KEY;
                }
            }
            this.userSettingEntity.Value = JSON.stringify(this.SavedVisualizations);
            await this.userSettingEntity.Save();
        }
        catch (error) {
            console.warn('[ClusterVisualization] Error saving visualizations:', error);
        }
    }
    static ɵfac = /*@__PURE__*/ (() => { let ɵClusterVisualizationResourceComponent_BaseFactory; return function ClusterVisualizationResourceComponent_Factory(__ngFactoryType__) { return (ɵClusterVisualizationResourceComponent_BaseFactory || (ɵClusterVisualizationResourceComponent_BaseFactory = i0.ɵɵgetInheritedFactory(ClusterVisualizationResourceComponent)))(__ngFactoryType__ || ClusterVisualizationResourceComponent); }; })();
    static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ClusterVisualizationResourceComponent, selectors: [["app-cluster-visualization-resource"]], viewQuery: function ClusterVisualizationResourceComponent_Query(rf, ctx) { if (rf & 1) {
            i0.ɵɵviewQuery(_c0, 5);
        } if (rf & 2) {
            let _t;
            i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.scatterPlot = _t.first);
        } }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 22, vars: 14, consts: [["scatterPlot", ""], [1, "cluster-viz-container"], [1, "sidebar"], [1, "sidebar-header"], [1, "fa-solid", "fa-circle-nodes"], [1, "saved-list"], [1, "saved-item", 3, "active"], [1, "no-saved"], [1, "new-btn", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "main-area"], [1, "topbar"], [1, "topbar-title"], [1, "scatter-area"], [3, "PointClicked", "PointHovered", "OpenRecordRequested", "Points", "Clusters", "IsLoading", "TooltipFields"], [3, "RunClustering", "SaveVisualization", "IsRunning", "Metrics", "EntityOptions", "EntityDocOptions"], [1, "saved-item", 3, "click"], [1, "saved-name"], [1, "saved-meta"], ["title", "Delete", 1, "delete-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "fa-solid", "fa-bookmark"], [1, "metric"], [1, "fa-solid", "fa-circle-dot"], [1, "metric-value"], [1, "fa-solid", "fa-layer-group"], [1, "fa-solid", "fa-chart-line"], [1, "fa-solid", "fa-clock"]], template: function ClusterVisualizationResourceComponent_Template(rf, ctx) { if (rf & 1) {
            const _r1 = i0.ɵɵgetCurrentView();
            i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "div", 3)(3, "h2");
            i0.ɵɵelement(4, "i", 4);
            i0.ɵɵtext(5, " Saved Clusters");
            i0.ɵɵelementEnd()();
            i0.ɵɵelementStart(6, "div", 5);
            i0.ɵɵrepeaterCreate(7, ClusterVisualizationResourceComponent_For_8_Template, 10, 5, "div", 6, ctx.TrackSavedBy, true);
            i0.ɵɵconditionalCreate(9, ClusterVisualizationResourceComponent_Conditional_9_Template, 4, 0, "div", 7);
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(10, "button", 8);
            i0.ɵɵlistener("click", function ClusterVisualizationResourceComponent_Template_button_click_10_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnNewAnalysis()); });
            i0.ɵɵelement(11, "i", 9);
            i0.ɵɵtext(12, " New Cluster Analysis ");
            i0.ɵɵelementEnd()();
            i0.ɵɵelementStart(13, "div", 10)(14, "div", 11)(15, "div", 12);
            i0.ɵɵtext(16);
            i0.ɵɵelementEnd();
            i0.ɵɵconditionalCreate(17, ClusterVisualizationResourceComponent_Conditional_17_Template, 20, 6);
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(18, "div", 13)(19, "mj-cluster-scatter", 14, 0);
            i0.ɵɵlistener("PointClicked", function ClusterVisualizationResourceComponent_Template_mj_cluster_scatter_PointClicked_19_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnPointClicked($event)); })("PointHovered", function ClusterVisualizationResourceComponent_Template_mj_cluster_scatter_PointHovered_19_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnPointHovered($event)); })("OpenRecordRequested", function ClusterVisualizationResourceComponent_Template_mj_cluster_scatter_OpenRecordRequested_19_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnOpenRecord($event)); });
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(21, "mj-cluster-config-panel", 15);
            i0.ɵɵlistener("RunClustering", function ClusterVisualizationResourceComponent_Template_mj_cluster_config_panel_RunClustering_21_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnRunClustering($event)); })("SaveVisualization", function ClusterVisualizationResourceComponent_Template_mj_cluster_config_panel_SaveVisualization_21_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnSaveVisualization()); });
            i0.ɵɵelementEnd()()()();
        } if (rf & 2) {
            i0.ɵɵadvance(7);
            i0.ɵɵrepeater(ctx.SavedVisualizations);
            i0.ɵɵadvance(2);
            i0.ɵɵconditional(ctx.SavedVisualizations.length === 0 ? 9 : -1);
            i0.ɵɵadvance(7);
            i0.ɵɵtextInterpolate(ctx.VisualizationTitle);
            i0.ɵɵadvance();
            i0.ɵɵconditional(ctx.HasResult ? 17 : -1);
            i0.ɵɵadvance(2);
            i0.ɵɵproperty("Points", (ctx.Result == null ? null : ctx.Result.Points) ?? i0.ɵɵpureFunction0(11, _c1))("Clusters", (ctx.Result == null ? null : ctx.Result.Clusters) ?? i0.ɵɵpureFunction0(12, _c1))("IsLoading", ctx.IsRunning)("TooltipFields", i0.ɵɵpureFunction0(13, _c2));
            i0.ɵɵadvance(2);
            i0.ɵɵproperty("IsRunning", ctx.IsRunning)("Metrics", ctx.Metrics)("EntityOptions", ctx.EntityOptions)("EntityDocOptions", ctx.EntityDocOptions);
        } }, dependencies: [i1.ClusterScatterComponent, i1.ClusterConfigPanelComponent], styles: ["[_nghost-%COMP%] {\n    display: flex;\n    width: 100%;\n    height: 100%;\n}\n\n.cluster-viz-container[_ngcontent-%COMP%] {\n    display: flex;\n    width: 100%;\n    height: 100%;\n    overflow: hidden;\n}\n\n\n\n\n\n\n.sidebar[_ngcontent-%COMP%] {\n    width: 260px;\n    background: var(--mj-bg-surface);\n    border-right: 1px solid var(--mj-border-default);\n    display: flex;\n    flex-direction: column;\n    flex-shrink: 0;\n}\n\n.sidebar-header[_ngcontent-%COMP%] {\n    padding: 20px;\n    border-bottom: 1px solid var(--mj-border-default);\n}\n\n.sidebar-header[_ngcontent-%COMP%]   h2[_ngcontent-%COMP%] {\n    font-size: 0.95rem;\n    font-weight: 600;\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    margin: 0;\n    color: var(--mj-text-primary);\n}\n\n.sidebar-header[_ngcontent-%COMP%]   h2[_ngcontent-%COMP%]   i[_ngcontent-%COMP%] {\n    color: var(--mj-brand-primary);\n}\n\n.saved-list[_ngcontent-%COMP%] {\n    flex: 1;\n    overflow-y: auto;\n    padding: 12px;\n}\n\n.saved-item[_ngcontent-%COMP%] {\n    padding: 12px 14px;\n    border-radius: 8px;\n    cursor: pointer;\n    margin-bottom: 4px;\n    transition: background 0.15s;\n    position: relative;\n}\n\n.saved-item[_ngcontent-%COMP%]:hover {\n    background: var(--mj-bg-surface-hover);\n}\n\n.saved-item.active[_ngcontent-%COMP%] {\n    background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n    border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.saved-name[_ngcontent-%COMP%] {\n    font-size: 0.85rem;\n    font-weight: 500;\n    margin-bottom: 3px;\n    color: var(--mj-text-primary);\n    padding-right: 20px;\n}\n\n.saved-meta[_ngcontent-%COMP%] {\n    font-size: 0.7rem;\n    color: var(--mj-text-muted);\n    display: flex;\n    gap: 10px;\n}\n\n.delete-btn[_ngcontent-%COMP%] {\n    position: absolute;\n    top: 10px;\n    right: 10px;\n    background: none;\n    border: none;\n    color: var(--mj-text-muted);\n    cursor: pointer;\n    font-size: 0.7rem;\n    padding: 2px 4px;\n    opacity: 0;\n    transition: opacity 0.15s, color 0.15s;\n}\n\n.saved-item[_ngcontent-%COMP%]:hover   .delete-btn[_ngcontent-%COMP%] {\n    opacity: 1;\n}\n\n.delete-btn[_ngcontent-%COMP%]:hover {\n    color: var(--mj-status-error);\n}\n\n.no-saved[_ngcontent-%COMP%] {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    padding: 40px 20px;\n    color: var(--mj-text-muted);\n    text-align: center;\n}\n\n.no-saved[_ngcontent-%COMP%]   i[_ngcontent-%COMP%] {\n    font-size: 1.5rem;\n    margin-bottom: 8px;\n    opacity: 0.4;\n}\n\n.no-saved[_ngcontent-%COMP%]   p[_ngcontent-%COMP%] {\n    font-size: 0.8rem;\n    margin: 0;\n}\n\n.new-btn[_ngcontent-%COMP%] {\n    margin: 12px;\n    padding: 10px;\n    border: 1px dashed var(--mj-border-strong);\n    border-radius: 8px;\n    background: transparent;\n    color: var(--mj-text-secondary);\n    font-size: 0.8rem;\n    cursor: pointer;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 6px;\n    transition: all 0.15s;\n}\n\n.new-btn[_ngcontent-%COMP%]:hover {\n    border-color: var(--mj-brand-primary);\n    color: var(--mj-brand-primary);\n}\n\n\n\n\n\n\n.main-area[_ngcontent-%COMP%] {\n    flex: 1;\n    position: relative;\n    display: flex;\n    flex-direction: column;\n    min-width: 0;\n}\n\n\n\n.topbar[_ngcontent-%COMP%] {\n    height: 48px;\n    background: var(--mj-bg-surface);\n    border-bottom: 1px solid var(--mj-border-default);\n    display: flex;\n    align-items: center;\n    padding: 0 20px;\n    gap: 24px;\n    flex-shrink: 0;\n}\n\n.topbar-title[_ngcontent-%COMP%] {\n    font-size: 0.9rem;\n    font-weight: 600;\n    margin-right: auto;\n    color: var(--mj-text-primary);\n}\n\n.metric[_ngcontent-%COMP%] {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    font-size: 0.75rem;\n    color: var(--mj-text-secondary);\n}\n\n.metric[_ngcontent-%COMP%]   i[_ngcontent-%COMP%] {\n    font-size: 0.7rem;\n}\n\n.metric-value[_ngcontent-%COMP%] {\n    color: var(--mj-text-primary);\n    font-weight: 600;\n}\n\n.metric-value.good[_ngcontent-%COMP%] {\n    color: var(--mj-status-success);\n}\n\n\n\n.scatter-area[_ngcontent-%COMP%] {\n    flex: 1;\n    position: relative;\n    overflow: hidden;\n}"] });
};
ClusterVisualizationResourceComponent = __decorate([
    RegisterClass(BaseResourceComponent, 'ClusterVisualizationResource')
], ClusterVisualizationResourceComponent);
export { ClusterVisualizationResourceComponent };
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ClusterVisualizationResourceComponent, [{
        type: Component,
        args: [{ standalone: false, selector: 'app-cluster-visualization-resource', template: "<div class=\"cluster-viz-container\">\n    <!-- Left Sidebar: Saved Visualizations -->\n    <div class=\"sidebar\">\n        <div class=\"sidebar-header\">\n            <h2><i class=\"fa-solid fa-circle-nodes\"></i> Saved Clusters</h2>\n        </div>\n        <div class=\"saved-list\">\n            @for (saved of SavedVisualizations; track TrackSavedBy($index, saved)) {\n                <div class=\"saved-item\"\n                     [class.active]=\"IsActiveSaved(saved)\"\n                     (click)=\"OnSelectSaved(saved)\">\n                    <div class=\"saved-name\">{{ saved.Name }}</div>\n                    <div class=\"saved-meta\">\n                        <span>{{ saved.EntityName }}</span>\n                        <span>{{ FormatDate(saved.CreatedAt) }}</span>\n                    </div>\n                    <button class=\"delete-btn\"\n                            title=\"Delete\"\n                            (click)=\"OnDeleteSaved(saved, $event)\">\n                        <i class=\"fa-solid fa-times\"></i>\n                    </button>\n                </div>\n            }\n\n            @if (SavedVisualizations.length === 0) {\n                <div class=\"no-saved\">\n                    <i class=\"fa-solid fa-bookmark\"></i>\n                    <p>No saved visualizations yet</p>\n                </div>\n            }\n        </div>\n        <button class=\"new-btn\" (click)=\"OnNewAnalysis()\">\n            <i class=\"fa-solid fa-plus\"></i> New Cluster Analysis\n        </button>\n    </div>\n\n    <!-- Main Area -->\n    <div class=\"main-area\">\n        <!-- Top metrics bar -->\n        <div class=\"topbar\">\n            <div class=\"topbar-title\">{{ VisualizationTitle }}</div>\n            @if (HasResult) {\n                <div class=\"metric\">\n                    <i class=\"fa-solid fa-circle-dot\"></i>\n                    Records: <span class=\"metric-value\">{{ Result!.Metrics.RecordCount }}</span>\n                </div>\n                <div class=\"metric\">\n                    <i class=\"fa-solid fa-layer-group\"></i>\n                    Clusters: <span class=\"metric-value\">{{ Result!.Metrics.ClusterCount }}</span>\n                </div>\n                <div class=\"metric\">\n                    <i class=\"fa-solid fa-chart-line\"></i>\n                    Silhouette:\n                    <span class=\"metric-value\" [class.good]=\"IsSilhouetteGood\">\n                        {{ SilhouetteScoreFormatted }}\n                    </span>\n                </div>\n                <div class=\"metric\">\n                    <i class=\"fa-solid fa-clock\"></i>\n                    Computed: <span class=\"metric-value\">{{ ComputationTimeFormatted }}</span>\n                </div>\n            }\n        </div>\n\n        <!-- Scatter plot area -->\n        <div class=\"scatter-area\">\n            <mj-cluster-scatter\n                #scatterPlot\n                [Points]=\"Result?.Points ?? []\"\n                [Clusters]=\"Result?.Clusters ?? []\"\n                [IsLoading]=\"IsRunning\"\n                [TooltipFields]=\"['Entity', 'Name', 'Description']\"\n                (PointClicked)=\"OnPointClicked($event)\"\n                (PointHovered)=\"OnPointHovered($event)\"\n                (OpenRecordRequested)=\"OnOpenRecord($event)\">\n            </mj-cluster-scatter>\n\n            <!-- Floating config panel -->\n            <mj-cluster-config-panel\n                [IsRunning]=\"IsRunning\"\n                [Metrics]=\"Metrics\"\n                [EntityOptions]=\"EntityOptions\"\n                [EntityDocOptions]=\"EntityDocOptions\"\n                (RunClustering)=\"OnRunClustering($event)\"\n                (SaveVisualization)=\"OnSaveVisualization()\">\n            </mj-cluster-config-panel>\n        </div>\n    </div>\n</div>\n", styles: [":host {\n    display: flex;\n    width: 100%;\n    height: 100%;\n}\n\n.cluster-viz-container {\n    display: flex;\n    width: 100%;\n    height: 100%;\n    overflow: hidden;\n}\n\n/* ============================================================\n   Left Sidebar\n   ============================================================ */\n\n.sidebar {\n    width: 260px;\n    background: var(--mj-bg-surface);\n    border-right: 1px solid var(--mj-border-default);\n    display: flex;\n    flex-direction: column;\n    flex-shrink: 0;\n}\n\n.sidebar-header {\n    padding: 20px;\n    border-bottom: 1px solid var(--mj-border-default);\n}\n\n.sidebar-header h2 {\n    font-size: 0.95rem;\n    font-weight: 600;\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    margin: 0;\n    color: var(--mj-text-primary);\n}\n\n.sidebar-header h2 i {\n    color: var(--mj-brand-primary);\n}\n\n.saved-list {\n    flex: 1;\n    overflow-y: auto;\n    padding: 12px;\n}\n\n.saved-item {\n    padding: 12px 14px;\n    border-radius: 8px;\n    cursor: pointer;\n    margin-bottom: 4px;\n    transition: background 0.15s;\n    position: relative;\n}\n\n.saved-item:hover {\n    background: var(--mj-bg-surface-hover);\n}\n\n.saved-item.active {\n    background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n    border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.saved-name {\n    font-size: 0.85rem;\n    font-weight: 500;\n    margin-bottom: 3px;\n    color: var(--mj-text-primary);\n    padding-right: 20px;\n}\n\n.saved-meta {\n    font-size: 0.7rem;\n    color: var(--mj-text-muted);\n    display: flex;\n    gap: 10px;\n}\n\n.delete-btn {\n    position: absolute;\n    top: 10px;\n    right: 10px;\n    background: none;\n    border: none;\n    color: var(--mj-text-muted);\n    cursor: pointer;\n    font-size: 0.7rem;\n    padding: 2px 4px;\n    opacity: 0;\n    transition: opacity 0.15s, color 0.15s;\n}\n\n.saved-item:hover .delete-btn {\n    opacity: 1;\n}\n\n.delete-btn:hover {\n    color: var(--mj-status-error);\n}\n\n.no-saved {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    padding: 40px 20px;\n    color: var(--mj-text-muted);\n    text-align: center;\n}\n\n.no-saved i {\n    font-size: 1.5rem;\n    margin-bottom: 8px;\n    opacity: 0.4;\n}\n\n.no-saved p {\n    font-size: 0.8rem;\n    margin: 0;\n}\n\n.new-btn {\n    margin: 12px;\n    padding: 10px;\n    border: 1px dashed var(--mj-border-strong);\n    border-radius: 8px;\n    background: transparent;\n    color: var(--mj-text-secondary);\n    font-size: 0.8rem;\n    cursor: pointer;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 6px;\n    transition: all 0.15s;\n}\n\n.new-btn:hover {\n    border-color: var(--mj-brand-primary);\n    color: var(--mj-brand-primary);\n}\n\n/* ============================================================\n   Main Area\n   ============================================================ */\n\n.main-area {\n    flex: 1;\n    position: relative;\n    display: flex;\n    flex-direction: column;\n    min-width: 0;\n}\n\n/* Top bar */\n.topbar {\n    height: 48px;\n    background: var(--mj-bg-surface);\n    border-bottom: 1px solid var(--mj-border-default);\n    display: flex;\n    align-items: center;\n    padding: 0 20px;\n    gap: 24px;\n    flex-shrink: 0;\n}\n\n.topbar-title {\n    font-size: 0.9rem;\n    font-weight: 600;\n    margin-right: auto;\n    color: var(--mj-text-primary);\n}\n\n.metric {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    font-size: 0.75rem;\n    color: var(--mj-text-secondary);\n}\n\n.metric i {\n    font-size: 0.7rem;\n}\n\n.metric-value {\n    color: var(--mj-text-primary);\n    font-weight: 600;\n}\n\n.metric-value.good {\n    color: var(--mj-status-success);\n}\n\n/* Scatter area */\n.scatter-area {\n    flex: 1;\n    position: relative;\n    overflow: hidden;\n}\n"] }]
    }], null, { scatterPlot: [{
            type: ViewChild,
            args: ['scatterPlot']
        }] }); })();
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ClusterVisualizationResourceComponent, { className: "ClusterVisualizationResourceComponent", filePath: "src/KnowledgeHub/components/clusters/cluster-visualization-resource.component.ts", lineNumber: 44 }); })();
/** Tree-shaking prevention */
export function LoadClusterVisualizationResource() {
    // Prevents tree-shaking of the component
}
//# sourceMappingURL=cluster-visualization-resource.component.js.map