/**
 * @fileoverview Classify · Content Item Grid (Phase 4 audit/analytics).
 *
 * Thin, reusable AG Grid listing of content items in one of two modes:
 *   - scoped to a single pipeline run via `[RunID]` (items processed in that run), or
 *   - all items (when `[RunID]` is null).
 *
 * Read-only: uses `ResultType:'simple'` + an explicit narrow `Fields` list, and a
 * load-more / page-size widening approach mirroring the host dashboard. Selecting a
 * row bubbles `(ItemSelected)` UP — this component owns no navigation (the host owns
 * NavigationService and opens the drilldown).
 */
import { Component, Input, Output, EventEmitter, ChangeDetectorRef, inject, } from '@angular/core';
import { RunView } from '@memberjunction/core';
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
import { ModuleRegistry, AllCommunityModule, themeAlpine, } from 'ag-grid-community';
import { deriveDisplayName, formatDate } from '../shared/classify.format';
import * as i0 from "@angular/core";
import * as i1 from "ag-grid-angular";
import * as i2 from "@memberjunction/ng-shared-generic";
import * as i3 from "@memberjunction/ng-ui-components";
function ClassifyItemGridComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "mj-loading", 1);
} }
function ClassifyItemGridComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 2);
    i0.ɵɵelement(1, "i", 3);
    i0.ɵɵelementStart(2, "p");
    i0.ɵɵtext(3, "No content items found.");
    i0.ɵɵelementEnd()();
} }
function ClassifyItemGridComponent_Conditional_3_Conditional_4_Conditional_1_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "i", 9);
    i0.ɵɵtext(1, " Loading\u2026 ");
} }
function ClassifyItemGridComponent_Conditional_3_Conditional_4_Conditional_2_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "i", 10);
    i0.ɵɵtext(1, " Load more ");
} }
function ClassifyItemGridComponent_Conditional_3_Conditional_4_Template(rf, ctx) { if (rf & 1) {
    const _r3 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "button", 8);
    i0.ɵɵlistener("click", function ClassifyItemGridComponent_Conditional_3_Conditional_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.LoadMore()); });
    i0.ɵɵconditionalCreate(1, ClassifyItemGridComponent_Conditional_3_Conditional_4_Conditional_1_Template, 2, 0)(2, ClassifyItemGridComponent_Conditional_3_Conditional_4_Conditional_2_Template, 2, 0);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext(2);
    i0.ɵɵproperty("disabled", ctx_r1.IsLoadingMore);
    i0.ɵɵadvance();
    i0.ɵɵconditional(ctx_r1.IsLoadingMore ? 1 : 2);
} }
function ClassifyItemGridComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
    const _r1 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "ag-grid-angular", 4);
    i0.ɵɵlistener("gridReady", function ClassifyItemGridComponent_Conditional_3_Template_ag_grid_angular_gridReady_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnGridReady($event)); })("rowClicked", function ClassifyItemGridComponent_Conditional_3_Template_ag_grid_angular_rowClicked_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnRowClicked($event)); });
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(1, "div", 5)(2, "span", 6);
    i0.ɵɵtext(3);
    i0.ɵɵelementEnd();
    i0.ɵɵconditionalCreate(4, ClassifyItemGridComponent_Conditional_3_Conditional_4_Template, 3, 2, "button", 7);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext();
    i0.ɵɵproperty("theme", ctx_r1.Theme)("columnDefs", ctx_r1.ColumnDefs)("defaultColDef", ctx_r1.DefaultColDef)("gridOptions", ctx_r1.GridOptions)("rowData", ctx_r1.Rows);
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate2("Showing ", ctx_r1.Rows.length, " of ", ctx_r1.TotalCount, " items");
    i0.ɵɵadvance();
    i0.ɵɵconditional(ctx_r1.HasMore ? 4 : -1);
} }
// Register AG Grid community modules once (idempotent across grids in the bundle).
ModuleRegistry.registerModules([AllCommunityModule]);
export class ClassifyItemGridComponent extends BaseAngularComponent {
    cdr = inject(ChangeDetectorRef);
    /** One page worth of additional rows fetched when "Load more" is clicked. */
    static PAGE_SIZE = 100;
    /**
     * When set, scope the grid to items processed by this pipeline run. When null,
     * show all content items. Re-fetches on change (setter pattern, not ngOnChanges).
     */
    _runID = null;
    set RunID(value) {
        if (value === this._runID)
            return;
        this._runID = value;
        this._pageSize = ClassifyItemGridComponent.PAGE_SIZE;
        void this.loadItems();
    }
    get RunID() {
        return this._runID;
    }
    /** When true, the grid loads its data on init. Lets a parent defer the query until the section is shown. */
    _autoLoad = true;
    set AutoLoad(value) {
        const was = this._autoLoad;
        this._autoLoad = value;
        if (value && !was && !this._loaded)
            void this.loadItems();
    }
    get AutoLoad() {
        return this._autoLoad;
    }
    /** Emits the selected content item's ID when a row is clicked. */
    ItemSelected = new EventEmitter();
    Rows = [];
    IsLoading = false;
    IsLoadingMore = false;
    TotalCount = 0;
    _loaded = false;
    _pageSize = ClassifyItemGridComponent.PAGE_SIZE;
    gridApi = null;
    get HasMore() {
        return this.TotalCount > this.Rows.length;
    }
    // ── AG Grid configuration ──
    Theme = themeAlpine.withParams({
        backgroundColor: 'var(--mj-bg-surface)',
        foregroundColor: 'var(--mj-text-primary)',
        textColor: 'var(--mj-text-primary)',
        borderColor: 'var(--mj-border-default)',
        chromeBackgroundColor: 'var(--mj-bg-surface-card)',
        headerBackgroundColor: 'var(--mj-bg-surface-card)',
        headerTextColor: 'var(--mj-text-secondary)',
        cellTextColor: 'var(--mj-text-primary)',
        subtleTextColor: 'var(--mj-text-muted)',
        dataBackgroundColor: 'var(--mj-bg-surface)',
        oddRowBackgroundColor: 'var(--mj-bg-surface-card)',
        rowHoverColor: 'var(--mj-bg-surface-hover, color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface)))',
        selectedRowBackgroundColor: 'color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface))',
        accentColor: 'var(--mj-brand-primary)',
        borderRadius: 'var(--mj-radius-sm)',
        browserColorScheme: 'inherit',
    });
    GridOptions = {
        animateRows: true,
        rowHeight: 36,
        headerHeight: 40,
        suppressCellFocus: true,
        enableCellTextSelection: true,
        suppressNoRowsOverlay: true,
    };
    DefaultColDef = {
        sortable: true,
        resizable: true,
        minWidth: 80,
    };
    ColumnDefs = [
        { field: 'DisplayName', headerName: 'Item', flex: 2, minWidth: 220, tooltipField: 'DisplayName' },
        { field: 'SourceName', headerName: 'Source', flex: 1, minWidth: 140 },
        { field: 'TagCount', headerName: 'Tags', width: 90, type: 'numericColumn' },
        { field: 'TaggingStatus', headerName: 'Status', width: 130 },
        { field: 'UpdatedAt', headerName: 'Updated', width: 160, sort: 'desc',
            comparator: (_a, _b, nodeA, nodeB) => (nodeA.data?.UpdatedAtRaw ?? '').localeCompare(nodeB.data?.UpdatedAtRaw ?? '') },
    ];
    OnGridReady(event) {
        this.gridApi = event.api;
        this.gridApi.setGridOption('rowData', this.Rows);
    }
    OnRowClicked(event) {
        if (event.data)
            this.ItemSelected.emit(event.data.ID);
    }
    /** Public refresh entry point (parent toolbar / load-more handler). */
    async Reload() {
        this._pageSize = ClassifyItemGridComponent.PAGE_SIZE;
        await this.loadItems();
    }
    async LoadMore() {
        if (this.IsLoadingMore || !this.HasMore)
            return;
        this.IsLoadingMore = true;
        this.cdr.detectChanges();
        this._pageSize += ClassifyItemGridComponent.PAGE_SIZE;
        await this.loadItems();
        this.IsLoadingMore = false;
        this.cdr.detectChanges();
    }
    async loadItems() {
        if (!this._autoLoad)
            return;
        this.IsLoading = true;
        this.cdr.detectChanges();
        // When scoped to a run, resolve the run's source + time window so we can
        // constrain the item query. There is no per-item run join table in the
        // schema, so we scope by ContentSource + the run's processing window
        // (same time-correlation basis the rest of the pipeline uses).
        let extraFilter = '';
        if (this._runID) {
            const scope = await this.resolveRunScope(this._runID);
            if (scope === null) {
                this.applyRows([], 0);
                return;
            }
            extraFilter = scope;
        }
        const rv = RunView.FromMetadataProvider(this.ProviderToUse);
        const result = await rv.RunView({
            EntityName: 'MJ: Content Items',
            ExtraFilter: extraFilter,
            OrderBy: '__mj_UpdatedAt DESC',
            MaxRows: this._pageSize,
            StartRow: 0,
            ResultType: 'simple',
            Fields: ['ID', 'Name', 'Description', 'ContentSource', 'TaggingStatus', 'EmbeddingStatus', '__mj_UpdatedAt'],
        }, this.ProviderToUse.CurrentUser);
        if (!result.Success) {
            this.applyRows([], 0);
            return;
        }
        const tagCounts = await this.loadTagCounts(result.Results.map(r => r['ID']));
        const rows = result.Results.map(r => this.toRow(r, tagCounts));
        this.applyRows(rows, result.TotalRowCount);
    }
    /**
     * Build the item-level filter that scopes to a pipeline run: the run's
     * ContentSource, optionally bounded by the run's processing window. Returns
     * null on a hard failure (so the caller short-circuits to an empty grid).
     */
    async resolveRunScope(runID) {
        const rv = RunView.FromMetadataProvider(this.ProviderToUse);
        const result = await rv.RunView({
            EntityName: 'MJ: Content Process Runs',
            ExtraFilter: `ID='${runID}'`,
            Fields: ['SourceID', 'StartTime', 'EndTime'],
            ResultType: 'simple',
        }, this.ProviderToUse.CurrentUser);
        if (!result.Success || result.Results.length === 0)
            return null;
        const run = result.Results[0];
        const fragments = [`ContentSourceID='${run.SourceID.replace(/'/g, '')}'`];
        if (run.StartTime)
            fragments.push(`__mj_UpdatedAt >= '${this.toSqlDate(run.StartTime)}'`);
        if (run.EndTime)
            fragments.push(`__mj_UpdatedAt <= '${this.toSqlDate(run.EndTime)}'`);
        return fragments.join(' AND ');
    }
    /** Format an ISO date value for a SQL string literal (UTC, no tz suffix surprises). */
    toSqlDate(value) {
        const d = new Date(value);
        return isNaN(d.getTime()) ? value.replace(/'/g, '') : d.toISOString().replace('T', ' ').replace('Z', '');
    }
    /** Count tags per item for the visible page (one RunView, aggregated client-side). */
    async loadTagCounts(itemIDs) {
        const counts = new Map();
        if (itemIDs.length === 0)
            return counts;
        const rv = RunView.FromMetadataProvider(this.ProviderToUse);
        const result = await rv.RunView({
            EntityName: 'MJ: Content Item Tags',
            ExtraFilter: this.buildItemIDInFilter(itemIDs, 'ItemID'),
            Fields: ['ItemID'],
            ResultType: 'simple',
        }, this.ProviderToUse.CurrentUser);
        if (result.Success) {
            for (const r of result.Results) {
                counts.set(r.ItemID, (counts.get(r.ItemID) ?? 0) + 1);
            }
        }
        return counts;
    }
    toRow(r, tagCounts) {
        const id = r['ID'];
        const updatedAt = r['__mj_UpdatedAt'] ?? '';
        return {
            ID: id,
            DisplayName: deriveDisplayName({ Name: r['Name'], Description: r['Description'] }),
            SourceName: r['ContentSource'] ?? 'Unknown',
            TagCount: tagCounts.get(id) ?? 0,
            EmbeddingStatus: r['EmbeddingStatus'] ?? '',
            TaggingStatus: r['TaggingStatus'] ?? '',
            UpdatedAt: updatedAt ? formatDate(updatedAt) : '—',
            UpdatedAtRaw: updatedAt,
        };
    }
    applyRows(rows, total) {
        this.Rows = rows;
        this.TotalCount = total;
        this.IsLoading = false;
        this._loaded = true;
        this.gridApi?.setGridOption('rowData', rows);
        this.cdr.detectChanges();
    }
    /** Build an `IN (...)` filter from item IDs, defensively stripping quotes. */
    buildItemIDInFilter(ids, column = 'ID') {
        const quoted = ids.map(id => `'${id.replace(/'/g, '')}'`).join(',');
        return `${column} IN (${quoted})`;
    }
    static ɵfac = /*@__PURE__*/ (() => { let ɵClassifyItemGridComponent_BaseFactory; return function ClassifyItemGridComponent_Factory(__ngFactoryType__) { return (ɵClassifyItemGridComponent_BaseFactory || (ɵClassifyItemGridComponent_BaseFactory = i0.ɵɵgetInheritedFactory(ClassifyItemGridComponent)))(__ngFactoryType__ || ClassifyItemGridComponent); }; })();
    static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ClassifyItemGridComponent, selectors: [["classify-item-grid"]], inputs: { RunID: "RunID", AutoLoad: "AutoLoad" }, outputs: { ItemSelected: "ItemSelected" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 4, vars: 1, consts: [[1, "cig-wrap"], ["text", "Loading items...", "size", "small"], [1, "cig-empty"], [1, "fa-solid", "fa-inbox"], [1, "cig-grid", 3, "gridReady", "rowClicked", "theme", "columnDefs", "defaultColDef", "gridOptions", "rowData"], [1, "cig-footer"], [1, "cig-count"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "disabled"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-chevron-down"]], template: function ClassifyItemGridComponent_Template(rf, ctx) { if (rf & 1) {
            i0.ɵɵelementStart(0, "div", 0);
            i0.ɵɵconditionalCreate(1, ClassifyItemGridComponent_Conditional_1_Template, 1, 0, "mj-loading", 1)(2, ClassifyItemGridComponent_Conditional_2_Template, 4, 0, "div", 2)(3, ClassifyItemGridComponent_Conditional_3_Template, 5, 8);
            i0.ɵɵelementEnd();
        } if (rf & 2) {
            i0.ɵɵadvance();
            i0.ɵɵconditional(ctx.IsLoading && ctx.Rows.length === 0 ? 1 : ctx.Rows.length === 0 ? 2 : 3);
        } }, dependencies: [i1.AgGridAngular, i2.LoadingComponent, i3.MJButtonDirective], styles: [".cig-wrap[_ngcontent-%COMP%] {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n    width: 100%;\n}\n\n.cig-grid[_ngcontent-%COMP%] {\n    width: 100%;\n    height: 360px;\n}\n\n.cig-footer[_ngcontent-%COMP%] {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding: 4px 2px;\n}\n\n.cig-count[_ngcontent-%COMP%] {\n    font-size: 0.8rem;\n    color: var(--mj-text-muted);\n}\n\n.cig-empty[_ngcontent-%COMP%] {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 8px;\n    padding: 32px 16px;\n    color: var(--mj-text-muted);\n}\n\n.cig-empty[_ngcontent-%COMP%]   i[_ngcontent-%COMP%] {\n    font-size: 1.6rem;\n    color: var(--mj-text-disabled);\n}\n\n.cig-empty[_ngcontent-%COMP%]   p[_ngcontent-%COMP%] {\n    margin: 0;\n    font-size: 0.85rem;\n}"] });
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ClassifyItemGridComponent, [{
        type: Component,
        args: [{ standalone: false, selector: 'classify-item-grid', template: "<div class=\"cig-wrap\">\n  @if (IsLoading && Rows.length === 0) {\n    <mj-loading text=\"Loading items...\" size=\"small\"></mj-loading>\n  } @else if (Rows.length === 0) {\n    <div class=\"cig-empty\">\n      <i class=\"fa-solid fa-inbox\"></i>\n      <p>No content items found.</p>\n    </div>\n  } @else {\n    <ag-grid-angular\n      class=\"cig-grid\"\n      [theme]=\"Theme\"\n      [columnDefs]=\"ColumnDefs\"\n      [defaultColDef]=\"DefaultColDef\"\n      [gridOptions]=\"GridOptions\"\n      [rowData]=\"Rows\"\n      (gridReady)=\"OnGridReady($event)\"\n      (rowClicked)=\"OnRowClicked($event)\">\n    </ag-grid-angular>\n\n    <div class=\"cig-footer\">\n      <span class=\"cig-count\">Showing {{ Rows.length }} of {{ TotalCount }} items</span>\n      @if (HasMore) {\n        <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"LoadMore()\" [disabled]=\"IsLoadingMore\">\n          @if (IsLoadingMore) {\n            <i class=\"fa-solid fa-spinner fa-spin\"></i> Loading\u2026\n          } @else {\n            <i class=\"fa-solid fa-chevron-down\"></i> Load more\n          }\n        </button>\n      }\n    </div>\n  }\n</div>\n", styles: [".cig-wrap {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n    width: 100%;\n}\n\n.cig-grid {\n    width: 100%;\n    height: 360px;\n}\n\n.cig-footer {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding: 4px 2px;\n}\n\n.cig-count {\n    font-size: 0.8rem;\n    color: var(--mj-text-muted);\n}\n\n.cig-empty {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 8px;\n    padding: 32px 16px;\n    color: var(--mj-text-muted);\n}\n\n.cig-empty i {\n    font-size: 1.6rem;\n    color: var(--mj-text-disabled);\n}\n\n.cig-empty p {\n    margin: 0;\n    font-size: 0.85rem;\n}\n"] }]
    }], null, { RunID: [{
            type: Input
        }], AutoLoad: [{
            type: Input
        }], ItemSelected: [{
            type: Output
        }] }); })();
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ClassifyItemGridComponent, { className: "ClassifyItemGridComponent", filePath: "src/AI/components/autotagging/components/classify-item-grid.component.ts", lineNumber: 46 }); })();
//# sourceMappingURL=classify-item-grid.component.js.map