import { AdaptableModuleBase } from './AdaptableModuleBase';
import * as ModuleConstants from '../Utilities/Constants/ModuleConstants';
import * as LayoutRedux from '../Redux/ActionsReducers/LayoutRedux';
import ArrayExtensions from '../Utilities/Extensions/ArrayExtensions';
import { LayoutRadioSelector } from '../View/Layout/LayoutRadioSelector';
import { LayoutStatusbar } from '../View/Layout/LayoutStatusBarSubPanelPopover';
import { EditCurrentLayoutButton } from '../View/Layout/EditCurrentLayoutButton';
import { LayoutCloneButton } from '../View/Layout/LayoutCloneButton';
import { LayoutWizard } from '../View/Layout/Wizard/LayoutWizard';
import { getLayoutFilterViewItems } from './Utilities/Layout/getLayoutFilterViewItems';
import { getLayoutSortViewItems } from './Utilities/Layout/getLayoutSortViewItems';
import { WEIGHTED_AVERAGE_AGG_FN_NAME } from '../AdaptableState/Common/AggregationColumns';
import { SHOW_PIVOT_COLUMN_DETAILS } from '../View/Components/Popups/WindowPopups/windowFactory';
import flattenDeep from 'lodash/flattenDeep';
import StringExtensions from '../Utilities/Extensions/StringExtensions';
import { getGridFilterViewItems } from '../View/Layout/Wizard/getGridFilterPreview';
import { RowSummaryService } from '../Utilities/Services/RowSummaryService';
import { isPivotLayout } from '../Api/Implementation/LayoutHelpers';
export class LayoutModule extends AdaptableModuleBase {
    constructor(api) {
        super(ModuleConstants.LayoutModuleId, ModuleConstants.LayoutFriendlyName, 'grid', 'LayoutPopup', 'Named sets of column visibility, order, groupings, aggregation, pivots etc.', api);
        this.rowSummaryService = new RowSummaryService(this.api);
    }
    onAdaptableReady() {
        this.rowSummaryService.onAdaptableReady();
        this.api.eventApi.on('LayoutChanged', (layoutChangedInfo) => {
            if (layoutChangedInfo.actionName === 'LAYOUT_SELECT' &&
                layoutChangedInfo.newLayoutState.CurrentLayout !==
                    layoutChangedInfo.oldLayoutState?.CurrentLayout) {
                this.handleLayoutSelection();
            }
        });
        requestAnimationFrame(() => {
            if (this.api.isDestroyed()) {
                return;
            }
            this.api.eventApi.internalApi.fireLayoutChangedEvent('ADAPTABLE_READY', null, this.api.layoutApi.getLayoutState());
        });
    }
    getModuleAdaptableObjects() {
        return this.api.layoutApi.getLayouts();
    }
    getExplicitlyReferencedColumnIds(layout) {
        const columnIds = [];
        if (layout.TableColumns) {
            const visibility = layout.ColumnVisibility || {};
            columnIds.push(...layout.TableColumns.filter((colId) => {
                return visibility[colId] !== false;
            }));
        }
        if (layout.TableAggregationColumns) {
            columnIds.push(...Object.keys(layout.TableAggregationColumns));
        }
        if (isPivotLayout(layout)) {
            if (layout.PivotColumns) {
                columnIds.push(...layout.PivotColumns);
            }
            if (layout.PivotAggregationColumns) {
                columnIds.push(...layout.PivotAggregationColumns.map((x) => x.ColumnId));
            }
            if (layout.PivotGroupedColumns) {
                columnIds.push(...layout.PivotGroupedColumns);
            }
        }
        if (layout.RowGroupedColumns) {
            columnIds.push(...layout.RowGroupedColumns);
        }
        return Array.from(new Set(columnIds));
    }
    getTeamSharingReferences(adaptableObject) {
        const teamSharingReferences = super.getTeamSharingReferences(adaptableObject);
        const layoutName = adaptableObject.Name;
        if (this.api.layoutApi.internalApi.hasLayoutSpecificObjects() && !!layoutName) {
            const layoutAssociatedObjectReferences = [];
            const loadConfig = {
                associatedWithLayout: layoutName,
            };
            // we ensured that there are layout specific objects, so all the "getAll*()" api methods will return only the objects available in the current layout
            this.api.alertApi.getAlertDefinitions(loadConfig).forEach((alertDefinition) => layoutAssociatedObjectReferences.push({
                Reference: alertDefinition,
                Module: 'Alert',
            }));
            this.api.customSortApi.getCustomSorts(loadConfig).forEach((customSort) => layoutAssociatedObjectReferences.push({
                Reference: customSort,
                Module: 'CustomSort',
            }));
            this.api.flashingCellApi.getFlashingCellDefinitions(loadConfig).forEach((flashingCell) => layoutAssociatedObjectReferences.push({
                Reference: flashingCell,
                Module: 'FlashingCell',
            }));
            this.api.formatColumnApi.getFormatColumns(loadConfig).forEach((formatColumn) => layoutAssociatedObjectReferences.push({
                Reference: formatColumn,
                Module: 'FormatColumn',
            }));
            this.api.styledColumnApi.getStyledColumns(loadConfig).forEach((styledcolumn) => layoutAssociatedObjectReferences.push({
                Reference: styledcolumn,
                Module: 'StyledColumn',
            }));
            this.api.plusMinusApi.getAllPlusMinus(loadConfig).forEach((plusMinusNudge) => layoutAssociatedObjectReferences.push({
                Reference: plusMinusNudge,
                Module: 'PlusMinus',
            }));
            this.api.shortcutApi.getShortcuts(loadConfig).forEach((shortcut) => layoutAssociatedObjectReferences.push({
                Reference: shortcut,
                Module: 'Shortcut',
            }));
            [
                ...this.api.scheduleApi.getReportSchedules(loadConfig),
                ...this.api.scheduleApi.getReminderSchedules(loadConfig),
                ...this.api.scheduleApi.getIPushPullSchedules(loadConfig),
                ...this.api.scheduleApi.getOpenFinSchedules(loadConfig),
            ].forEach((schedule) => layoutAssociatedObjectReferences.push({
                Reference: schedule,
                Module: 'Schedule',
            }));
            teamSharingReferences.push(...layoutAssociatedObjectReferences);
        }
        return teamSharingReferences;
    }
    hasNamedQueryReferences() {
        return true;
    }
    createColumnMenuItems(column) {
        if (!this.isModuleEditable()) {
            return;
        }
        let returnColumnMenuItems = [];
        const isReadOnlyLayout = this.api.layoutApi.isCurrentLayoutReadOnly();
        if (!isReadOnlyLayout) {
            returnColumnMenuItems.push(this.createMenuItemShowPopup('layout-edit', 'Edit Layout', this.moduleInfo.Popup, 'edit-table', {
                action: 'Edit',
                source: 'ColumnMenu',
                value: this.api.layoutApi.getCurrentLayout(),
            }));
            if (column && !column.isTreeColumn) {
                returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-column-caption-change', 'Change Caption', 'edit', () => this.api.layoutApi.showChangeColumnCaption(column)));
            }
        }
        if (column) {
            if (column.hideable) {
                returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-column-hide', 'Hide Column', 'visibility-off-bold', () => {
                    this.api.columnApi.hideColumn(column.columnId);
                }));
            }
            const hasExistingSelection = this.api.gridApi.getSelectedCellInfo()?.gridCells?.length;
            if (hasExistingSelection) {
                returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-column-select-preserve', 'Select Column (Preserve Selection)', 'select-fwd', () => {
                    this.api.columnApi.addColumnToSelection(column.columnId);
                }));
                returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-column-select-reset', 'Select Column (Reset Selection)', 'tab-unselected', () => {
                    this.api.columnApi.selectColumn(column.columnId);
                }));
            }
            else {
                returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-column-select', 'Select Column', 'tab-unselected', () => {
                    this.api.columnApi.selectColumn(column.columnId);
                }));
            }
        }
        returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-grid-select', 'Select Grid', 'select-all', () => {
            this.api.gridApi.selectAll();
        }));
        return returnColumnMenuItems;
    }
    createContextMenuItems(menuContext) {
        let returnColumnMenuItems = [];
        if (this.isModuleEditable() && !this.api.layoutApi.isCurrentLayoutReadOnly()) {
            returnColumnMenuItems.push(this.createMenuItemShowPopup('layout-edit', 'Edit Layout', this.moduleInfo.Popup, 'edit-table', {
                action: 'Edit',
                source: 'ColumnMenu',
            }));
        }
        if ((menuContext.selectedCellInfo &&
            ArrayExtensions.IsNotNullOrEmpty(menuContext.selectedCellInfo.columns)) ||
            (menuContext.selectedRowInfo &&
                ArrayExtensions.IsNotNullOrEmpty(menuContext.selectedRowInfo.gridRows))) {
            returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-clear-selection', 'Clear Selected Cells', 'select-off', () => {
                this.api.gridApi.deselectAll();
            }));
        }
        returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-select-all', 'Select Grid', 'select-all', () => {
            this.api.gridApi.selectAll();
        }));
        returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-auto-size', 'Auto Size', 'arrow-expand', () => {
            this.api.columnApi.autosizeAllColumns();
        }));
        const viewPivotItemsMenuItem = this.createViewPivotItemsMenuItem(menuContext);
        if (viewPivotItemsMenuItem) {
            returnColumnMenuItems.push(viewPivotItemsMenuItem);
        }
        return returnColumnMenuItems;
    }
    // TODO next time this method is touched, it should be extracted in an internal Api
    createViewPivotItemsMenuItem(menuContext) {
        // current group => menuContext.rowNode.field;
        const selectedCellInfo = menuContext.selectedCellInfo ?? this.api.gridApi.getSelectedCellInfo();
        if (selectedCellInfo?.gridCells?.length !== 1) {
            return;
        }
        const gridCell = menuContext.gridCell ?? selectedCellInfo.gridCells[0];
        const currentLayout = this.api.layoutApi.getCurrentLayout();
        if (!isPivotLayout(currentLayout)) {
            return;
        }
        const { agGridColumn } = menuContext;
        const columnId = agGridColumn.getColId();
        const isPivotColumn = this.api.columnApi.isPivotResultColumn(columnId);
        if (!isPivotColumn && this.api.columnApi.getColumnDataTypeForColumnId(columnId) !== 'number') {
            return;
        }
        /**
         * Cannot base the values on the pivot column id, because the value or the field may have underscrores
         *
         *
         * e.g. pivot_license_MIT License_stargazers_count
         */
        // pivot_status_Booked_price
        // price
        const aggColumn = agGridColumn?.getColDef?.().pivotValueColumn?.getColDef?.()?.field;
        // Booked
        const pivotValue = agGridColumn?.getColDef?.()?.pivotKeys?.[0];
        // status - the preview works only with one column
        const pivotColumnId = currentLayout?.PivotColumns?.[0];
        function getData(rows) {
            return [
                ...rows.map((row) => {
                    if (row.childrenAfterFilter) {
                        return getData(row.childrenAfterFilter);
                    }
                    return row.data;
                }),
            ];
        }
        const data = flattenDeep(getData(menuContext.rowNode.childrenAfterFilter));
        const rowGroups = currentLayout.RowGroupedColumns;
        const cellValue = isPivotColumn
            ? menuContext.rowNode?.aggData?.[columnId]
            : gridCell.displayValue;
        const popupProps = {
            hasPivotValue: isPivotColumn,
            columnId,
            rowGroups,
            rows: data,
            layout: currentLayout,
            aggColumn,
            pivotValue,
            pivotColumnId,
        };
        return {
            name: 'layout-aggregated-view',
            category: this.moduleInfo.ModuleName,
            isVisible: true,
            label: 'Expand Aggregated Value',
            onClick: () => {
                this.api.internalApi.showPopupWindow({
                    // force only one window instance opened
                    id: SHOW_PIVOT_COLUMN_DETAILS,
                    factoryId: SHOW_PIVOT_COLUMN_DETAILS,
                    title: `Rows for Aggregated Value: ${cellValue}`,
                    icon: undefined,
                    popupProps,
                });
            },
            icon: {
                name: 'arrow-expand',
            },
        };
    }
    getTeamSharingAction() {
        return {
            ModuleEntities: this.api.layoutApi.getLayouts(),
            AddAction: LayoutRedux.LayoutAdd,
            EditAction: LayoutRedux.LayoutSave,
        };
    }
    toViewAll() {
        return this.getModuleAdaptableObjects().map((layout) => this.toView(layout));
    }
    toView(layout) {
        const maxColumnsToDisplay = this.api.optionsApi.getLayoutOptions().layoutViewOptions?.maxColumnsToDisplay ?? 10;
        const columnIdToFriendlyName = (columnId) => {
            return this.api.columnApi.getFriendlyNameForColumnId(columnId);
        };
        let columns = (layout.TableColumns || [])
            .filter((c) => !this.api.columnApi.isAutoRowGroupColumn(c))
            .filter((c) => !this.api.columnApi.isPivotResultColumn(c));
        if (columns.length >
            maxColumnsToDisplay + 1 /* +1 is to show tag only beginning with 2, 'other 2' */) {
            const extraColumns = columns.length - maxColumnsToDisplay;
            const firstNColumns = columns
                .slice(0, maxColumnsToDisplay)
                .map((column) => columnIdToFriendlyName(column));
            columns = [...firstNColumns, `and ${extraColumns} more`];
        }
        else {
            columns = columns.map((column) => columnIdToFriendlyName(column));
        }
        return {
            items: [
                {
                    name: 'Grid Type',
                    values: [isPivotLayout(layout) ? 'Pivot' : 'Table'],
                },
                {
                    name: 'Name',
                    values: [layout.Name],
                },
                /**
                 * Hide pivot columns.
                 *
                 * The generated pivot columns cannot be prevented from beeing on the layout object.
                 * When they are removed/not added, they no longer appear in the grid.
                 */
                !isPivotLayout(layout) && {
                    name: 'Columns',
                    values: columns,
                },
                layout.ColumnSorts?.length && getLayoutSortViewItems(layout, this.api),
                layout?.ColumnFilters?.length && getLayoutFilterViewItems(layout, this.api),
                layout?.RowGroupedColumns?.length && {
                    name: 'Row Groups',
                    values: layout.RowGroupedColumns.map((colId) => columnIdToFriendlyName(colId)),
                },
                layout?.PivotColumns?.length && {
                    name: 'Pivot Columns',
                    values: layout.PivotColumns.map((colId) => columnIdToFriendlyName(colId)),
                },
                layout?.PivotGroupedColumns?.length && {
                    name: 'Pivot Row Groups',
                    values: layout.PivotGroupedColumns.map((colId) => columnIdToFriendlyName(colId)),
                },
                layout?.TableAggregationColumns &&
                    layout.TableAggregationColumns.length && {
                    name: 'Aggregations',
                    values: layout.TableAggregationColumns.map(({ ColumnId, AggFunc: aggFn }) => {
                        if (ColumnId === 'Source' || ColumnId === 'Uuid' || ColumnId === 'AdaptableVersion') {
                            return '';
                        }
                        let aggFnName = '';
                        if (typeof aggFn === 'string') {
                            aggFnName = aggFn;
                        }
                        else if (typeof aggFn === 'object' && aggFn.type === 'weightedAverage') {
                            aggFnName = WEIGHTED_AVERAGE_AGG_FN_NAME;
                        }
                        return `${aggFnName}(${columnIdToFriendlyName(ColumnId)})`;
                    }).filter(Boolean),
                },
                isPivotLayout(layout) &&
                    layout?.PivotAggregationColumns &&
                    layout.PivotAggregationColumns.length && {
                    name: 'Pivot Aggregations',
                    values: layout.PivotAggregationColumns.map(({ ColumnId, AggFunc: aggFn }) => {
                        if (ColumnId === 'Source' || ColumnId === 'Uuid' || ColumnId === 'AdaptableVersion') {
                            return '';
                        }
                        let aggFnName = '';
                        if (typeof aggFn === 'string') {
                            aggFnName = aggFn;
                        }
                        else if (typeof aggFn === 'object' && aggFn.type === 'weightedAverage') {
                            aggFnName = WEIGHTED_AVERAGE_AGG_FN_NAME;
                        }
                        return `${aggFnName}(${columnIdToFriendlyName(ColumnId)})`;
                    }).filter(Boolean),
                },
                layout?.GridFilter &&
                    StringExtensions.IsNotNullOrEmpty(layout.GridFilter.Expression) &&
                    getGridFilterViewItems(layout),
                layout &&
                    !isPivotLayout(layout) &&
                    layout.RowSummaries?.length && {
                    name: 'Row Summaries',
                    values: layout.RowSummaries.map((rowSummary) => {
                        const columns = Object.entries(rowSummary.ColumnsMap ?? {})
                            .map(([columnId, expression]) => {
                            if (columnId === 'Source' ||
                                columnId === 'Uuid' ||
                                columnId === 'AdaptableVersion') {
                                return '';
                            }
                            return `${expression}(${columnIdToFriendlyName(columnId)})`;
                        })
                            .filter(Boolean)
                            .join(', ');
                        return `${rowSummary.Position}: ${columns}`;
                    }),
                },
            ].filter(Boolean),
            abObject: layout,
        };
    }
    getViewProperties() {
        return {
            abObjectTypes: [
                {
                    name: 'Table Layout',
                    accessLevel: this.AccessLevel,
                },
                !this.api.gridApi.isTreeDataGrid() && {
                    name: 'Pivot Layout',
                    accessLevel: this.AccessLevel,
                },
            ].filter(Boolean),
            actions: [LayoutRadioSelector, LayoutCloneButton],
            getDeleteAction: (layout) => {
                // make sure we do not delete the last layout
                if (this.getModuleAdaptableObjects().length === 1) {
                    return null;
                }
                return LayoutRedux.LayoutDelete(layout);
            },
            getEditWizard: () => LayoutWizard,
            getStatusBarPanelProps: () => {
                return {
                    triggerActionOnWrapperClick: false,
                    content: LayoutStatusbar,
                    extraActions: [EditCurrentLayoutButton],
                };
            },
        };
    }
    handleLayoutSelection() {
        if (!this.api.layoutApi.internalApi.hasLayoutSpecificObjects()) {
            // no need if no callback implementation is provided or no auto checking
            return;
        }
        // PlusMinus
        this.api.internalApi
            .getModuleService()
            .getModuleById('PlusMinus')
            .checkListenToKeyDown();
        // Shortcut
        this.api.internalApi
            .getModuleService()
            .getModuleById('Shortcut')
            .checkListenToKeyDown();
        // Schedule
        this.api.internalApi
            .getModuleService()
            .getModuleById('Schedule')
            .setUpScheduleJobs();
        // CustomSort, FlashingCell, FormatColumn, StyledColumn
        // we need to re-setup the column defs, as some colDefs properties may be changed
        this.api.internalApi.getAdaptableInstance().updateColumnModelAndRefreshGrid();
    }
    getReferencedNamedQueryNames(layout) {
        if (!layout.GridFilter?.Expression) {
            return [];
        }
        return this.api.namedQueryApi.internalApi.getReferencedNamedQueryNames(layout.GridFilter.Expression);
    }
}
