import { DataSource, InfiniteTable, } from '../../../../components/InfiniteTable';
import * as React from 'react';
import { Box, Flex, Text } from 'rebass';
import DropdownButton from '../../../../components/DropdownButton';
import FormLayout, { FormRow } from '../../../../components/FormLayout';
import Input from '../../../../components/Input';
import SimpleButton from '../../../../components/SimpleButton';
import { Tabs } from '../../../../components/Tabs';
import { Tag } from '../../../../components/Tag';
import { isValidOrderForColumnGroups, } from '../../../../AdaptableState/Common/AdaptableColumn';
import { useAdaptable } from '../../../AdaptableContext';
import { renderSelectionSection } from '../../../Components/ValueSelector';
import { useOnePageAdaptableWizardContext } from '../../../Wizard/OnePageAdaptableWizard';
import { columnFilter } from './Utilities';
import { Icon, NaturallySizedIcon } from '../../../../components/icons';
import { CheckBox } from '../../../../components/CheckBox';
import { clamp } from '../../../../Utilities/Helpers/Helper';
import { ColumnLabels } from '../Components/ColumnLabels';
import ArrayExtensions from '../../../../Utilities/Extensions/ArrayExtensions';
import { generateAutoRowGroupColumnForColumn, generateAutoRowGroupSingleColumn, generateAutoTreeSingleColumn, } from '../../../../Api/Implementation/ColumnApiImpl';
import { ReorderDraggable } from '../../../Components/ReorderDraggable';
import { AdaptableFormControlTextClear } from '../../../Components/Forms/AdaptableFormControlTextClear';
import { sortColumnIdsByOrder } from '../../../../layout-manager/src/sortColumnIdsByOrder';
import HelpBlock from '../../../../components/HelpBlock';
const PropertyOrderText = (props) => (React.createElement(Text, { fontWeight: 600, fontSize: 2 }, props.children));
const columnTypes = {
    default: {
        defaultFlex: 1,
    },
};
export const ColumnsSectionSummary = () => {
    const adaptable = useAdaptable();
    const { data: layout } = useOnePageAdaptableWizardContext();
    const rowHeight = 40;
    const headerHeight = 40;
    const ColumnIds = layout.TableColumns || layout.PivotColumns || [];
    const tableHeight = headerHeight + clamp(rowHeight * ColumnIds.length, 100, 360);
    const data = React.useMemo(() => {
        return ColumnIds.map((columnId) => {
            if (adaptable.api.columnApi.isPivotResultColumn(columnId) ||
                adaptable.api.columnApi.isAutoRowGroupColumn(columnId)) {
                return null;
            }
            const friendlyName = adaptable.api.columnApi.getFriendlyNameForColumnId(columnId, layout);
            const header = layout.ColumnHeaders?.[columnId] ?? '';
            const columnWidth = layout.ColumnWidths?.[columnId];
            const columnPinning = layout.ColumnPinning?.[columnId];
            let agg = (layout.TableAggregationColumns || []).find((agg) => agg.ColumnId === columnId)?.AggFunc;
            agg = typeof agg === 'object' ? agg.type : agg;
            const sortIndex = (layout?.ColumnSorts ?? []).findIndex((sort) => sort.ColumnId === columnId);
            const sortOrder = sortIndex != -1
                ? {
                    index: sortIndex + 1,
                    sort: layout.ColumnSorts[sortIndex]?.SortOrder ?? null,
                }
                : {};
            let grouping = layout.RowGroupedColumns?.indexOf?.(columnId);
            grouping = grouping != -1 ? grouping + 1 : null;
            const filter = layout.ColumnFilters?.find((colFilter) => colFilter.ColumnId === columnId);
            return {
                columnId,
                agg,
                friendlyName,
                header,
                columnWidth,
                columnPinning,
                sortOrder,
                grouping,
                filter,
            };
        }).filter(Boolean);
    }, [layout]);
    const columns = React.useMemo(() => {
        return {
            name: { header: 'Name', field: 'friendlyName', minWidth: 100 },
            header: { header: 'Header', field: 'header', minWidth: 120 },
            colId: { header: 'ID', field: 'columnId' },
            agg: { header: 'Agg', field: 'agg', minWidth: 70 },
            width: { header: 'Width', field: 'columnWidth', minWidth: 70 },
            pin: { header: 'Pin', field: 'columnPinning', minWidth: 80 },
            sort: {
                header: 'Sort',
                field: 'sortOrder',
                minWidth: 50,
                align: 'center',
                render: (params) => {
                    if (!params.data?.sortOrder?.sort) {
                        return React.createElement(React.Fragment, null);
                    }
                    return (React.createElement(Flex, null,
                        React.createElement(Icon, { name: params.data?.sortOrder?.sort === 'Asc' ? 'arrow-up-long' : 'arrow-down-long' }),
                        ' ',
                        React.createElement(PropertyOrderText, null, params.data?.sortOrder?.index ?? '')));
                },
            },
            filter: {
                header: 'Filter',
                field: 'filter',
                align: 'center',
                minWidth: 50,
                render: (params) => {
                    if (!params.data?.filter) {
                        return React.createElement(React.Fragment, null);
                    }
                    return React.createElement(CheckBox, { checked: true });
                },
            },
            group: {
                header: 'Group',
                minWidth: 50,
                field: 'columnGroupping',
                align: 'center',
                render: (params) => {
                    if (!params.data.grouping) {
                        return React.createElement(React.Fragment, null);
                    }
                    return (React.createElement(Flex, null,
                        React.createElement(CheckBox, { margin: 0, mr: 1, checked: true }),
                        React.createElement(PropertyOrderText, null, params.data?.grouping)));
                },
            },
        };
    }, []);
    const tableDOMProps = {
        style: {
            height: '100%',
            minWidth: '10rem',
            minHeight: tableHeight,
        },
    };
    return (React.createElement(DataSource, { data: data, primaryKey: "columnId" },
        React.createElement(InfiniteTable, { columnTypes: columnTypes, rowHeight: rowHeight, columnHeaderHeight: headerHeight, domProps: tableDOMProps, columns: columns })));
};
const ColumnRow = (props) => {
    const [isExpanded, setIsExpanded] = React.useState(false);
    const adaptable = useAdaptable();
    const initialHeader = adaptable.api.columnApi.getFriendlyNameForColumnId(props.column.columnId, props.layout);
    const isRowGroupColumn = adaptable.api.columnApi.isAutoRowGroupColumn(props.column.columnId);
    const customHeader = props.layout.ColumnHeaders?.[props.column.columnId] ?? initialHeader;
    // column pinning
    const columnPinning = props.layout.ColumnPinning?.[props.column.columnId] || 'None';
    const pinningOptions = [
        { value: 'None', label: 'None', onClick: () => props.onPinChange(props.column.columnId, null) },
        {
            value: 'left',
            label: 'Left',
            onClick: () => props.onPinChange(props.column.columnId, 'left'),
        },
        {
            value: 'right',
            label: 'Right',
            onClick: () => props.onPinChange(props.column.columnId, 'right'),
        },
    ];
    const { column } = props;
    // width
    const columnWidth = props.layout.ColumnWidths?.[column.columnId];
    const visible = (props.layout.TableColumns.includes(column.columnId) &&
        props.layout.ColumnVisibility?.[column.columnId] !== false) ||
        isRowGroupColumn;
    return (React.createElement(Box, { "data-name": props.column.columnId, className: "ab-Layout-Wizard__ColumnRow" },
        React.createElement(Flex, { className: "ab-Layout-Wizard__ColumnRow__Header", mt: 1, mb: 1, onClick: () => setIsExpanded(!isExpanded) },
            React.createElement(CheckBox, { "data-name": column.columnId, onChange: (checked) => {
                    props.onColumnVisibilityChange(column.columnId, checked);
                }, disabled: column.hideable === false || isRowGroupColumn, onClick: (event) => {
                    event.stopPropagation();
                }, checked: visible }),
            React.createElement(Flex, { ml: 2, mr: 2, alignItems: "center", "data-name": "column-label" }, initialHeader),
            props.column.columnGroup && props.column.columnGroup.groupCount > 1 ? (React.createElement(Box, { className: "ab-Layout-Wizard__ColumnRow__Title", ml: 2, mr: 2, padding: 1 },
                "Column Group: ",
                props.column.columnGroup.friendlyName)) : null,
            React.createElement(Flex, { mr: 2 },
                React.createElement(ColumnLabels, { showTitle: false, sortable: props.column.sortable, filterable: props.column.filterable, pivotable: props.column.pivotable, moveable: props.column.moveable, groupable: props.column.groupable, aggregatable: props.column.aggregatable })),
            React.createElement(Box, { flex: 1 }),
            React.createElement(SimpleButton, { "data-name": "expand-collapse", "data-value": isExpanded ? 'expanded' : 'collapsed', ml: 10, padding: 0, iconSize: 24, variant: "text", onClick: () => setIsExpanded(!isExpanded), icon: isExpanded ? 'triangle-up' : 'triangle-down' })),
        isExpanded && (React.createElement(Box, { className: "ab-Layout-Wizard__ColumnRow__Expanded-Container", mb: 2, padding: 2, mt: 2 },
            React.createElement(Flex, { mb: 2 },
                React.createElement(FormLayout, { width: "100%", mr: 3 },
                    React.createElement(FormRow, { label: "ColumnId" },
                        React.createElement(Tag, null, props.column.columnId)),
                    React.createElement(FormRow, { label: "Header" },
                        React.createElement(Input, { "data-name": "column-header", className: "ab-Layout-Wizard__ColumnRow__Input", placeholder: "Custom name (optional)", onChange: () => {
                                props.onColumnNameChange(props.column.columnId, event.target.value);
                            }, value: customHeader })),
                    React.createElement(FormRow, { label: "Width" },
                        React.createElement(Input, { "data-name": "column-width", className: "ab-Layout-Wizard__ColumnRow__Input", type: "number", placeholder: "Column width", onChange: (event) => {
                                let value = parseFloat(event.target.value);
                                value = typeof value === 'number' && !isNaN(value) ? value : void 0;
                                props.onColumnWidthChange(props.column.columnId, value);
                            }, value: columnWidth })),
                    React.createElement(FormRow, { label: "Pinning" },
                        React.createElement(DropdownButton, { "data-name": "column-pinning", columns: ['label'], items: pinningOptions }, pinningOptions.find((option) => option.value === columnPinning).label))))))));
};
export const ColumnsSection = (props) => {
    const adaptable = useAdaptable();
    const { data: layout } = useOnePageAdaptableWizardContext();
    const [searchInputValue, setSearchInputValue] = React.useState('');
    // we don't want to rely on all the columns from the Grid itself
    // since the "Row Groups" section of the editor can determine a change
    // which is not reflected into AG Grid until we hit finish
    //
    // so we only rely on non-generated columns, which are the same
    const allColumns = adaptable.api.columnApi.getUIAvailableColumns().filter((col) => {
        return !col.isGeneratedRowGroupColumn;
    });
    // however, changes in RowGroupedColumns done in the previous step
    // are reflected into the layout, so we use the latest layout.RowGroupedColumns
    // to create new columns
    if (layout.RowGroupedColumns) {
        if (layout.RowGroupDisplayType === 'single') {
            allColumns.unshift(generateAutoRowGroupSingleColumn());
        }
        else {
            [...layout.RowGroupedColumns].reverse().forEach((col) => {
                const groupCol = generateAutoRowGroupColumnForColumn(adaptable.api.columnApi.getColumnWithColumnId(col));
                allColumns.unshift(groupCol);
            });
        }
    }
    if (adaptable.api.gridApi.isTreeDataGrid()) {
        allColumns.unshift(generateAutoTreeSingleColumn());
    }
    const colIdToCol = allColumns.reduce((acc, col) => {
        if (col) {
            acc[col.columnId] = col;
        }
        return acc;
    }, {});
    const TableColumns = layout.TableColumns;
    const ColumnOrderAllColumns = ArrayExtensions.sortArrayWithOrder(allColumns.map((col) => col.columnId), TableColumns, { sortUnorderedItems: false }).map((colId) => colIdToCol[colId]);
    const currentOrder = searchInputValue
        ? ColumnOrderAllColumns.filter((col) => {
            return columnFilter(col, searchInputValue);
        })
        : ColumnOrderAllColumns;
    const handleColumnsChange = (newColumns) => {
        if (searchInputValue) {
            // there's a search in-place
            // so the current `newColumns` does not actually contain all the columns
            // so we need to recompute it
            const newOrder = sortColumnIdsByOrder(
            // given those are all the columns
            ColumnOrderAllColumns.map((col) => col.columnId), 
            // we want to sort them to have this new order
            // - this sorting will leave all ids not found in this new order untouched
            newColumns.map((col) => col.columnId));
            // so basically we now have the correct order, with all columns
            newColumns = newOrder.map((colId) => colIdToCol[colId]);
        }
        const oldColumns = ColumnOrderAllColumns;
        const columnIds = newColumns.map((col) => col.columnId);
        if (!isValidOrderForColumnGroups({ oldColumns, newColumns })) {
            return;
        }
        const ColumnVisibility = { ...layout.ColumnVisibility };
        const columnOrderSet = new Set(TableColumns);
        columnIds.forEach((colId) => {
            if (!columnOrderSet.has(colId)) {
                ColumnVisibility[colId] = false;
            }
        });
        props.onChange({
            ...layout,
            TableColumns: columnIds,
            ColumnVisibility,
        });
    };
    const handleColumnVisibilityChange = (columnId, visible) => {
        const ColumnVisibility = { ...layout.ColumnVisibility };
        let TableColumns = [...layout.TableColumns];
        if (visible) {
            delete ColumnVisibility[columnId];
        }
        else {
            ColumnVisibility[columnId] = false;
        }
        const columnIds = ColumnOrderAllColumns.map((col) => col.columnId);
        const idsToIndexes = new Map(columnIds.map((colId, index) => [colId, index]));
        const columnOrderSet = new Set(TableColumns);
        if (!columnOrderSet.has(columnId)) {
            TableColumns = columnIds.filter((colId) => {
                const isCurrent = columnOrderSet.has(colId);
                if (isCurrent) {
                    return true;
                }
                // include all columns past the current column order
                // that reach until the index of the currently checked column
                const shouldInclude = idsToIndexes.get(colId) <= idsToIndexes.get(columnId);
                if (shouldInclude) {
                    if (colId !== columnId && visible) {
                        // this is a column that's between the current column order limit
                        // and the currently checked column
                        // so we need to include it in order
                        // but make it invisible
                        ColumnVisibility[colId] = false;
                    }
                }
                return shouldInclude;
            });
        }
        props.onChange({
            ...layout,
            TableColumns: TableColumns,
            ColumnVisibility,
        });
    };
    const handlePinChange = (columnId, pinning) => {
        props.onChange({
            ...layout,
            ColumnPinning: {
                ...layout.ColumnPinning,
                [columnId]: pinning,
            },
        });
    };
    const handleColumnNameChange = (columnId, headerName) => {
        props.onChange({
            ...layout,
            ColumnHeaders: {
                ...layout.ColumnHeaders,
                [columnId]: headerName,
            },
        });
    };
    const handleColumnWidthChange = (columnId, width) => {
        props.onChange({
            ...layout,
            ColumnWidths: {
                ...layout.ColumnWidths,
                [columnId]: width,
            },
        });
    };
    const visibleIds = layout.TableColumns.filter((colId) => {
        return layout.ColumnVisibility?.[colId] !== false;
    });
    const toLabel = (colId) => adaptable.api.columnApi.getFriendlyNameForColumnId(colId, layout);
    const toIdentifier = (colId) => colId;
    return (React.createElement(Tabs, null,
        React.createElement(Tabs.Tab, null, "Columns"),
        React.createElement(Tabs.Content, { style: { overflow: 'hidden' } },
            React.createElement(Flex, { mt: 2, mb: 3 },
                React.createElement(ColumnLabels, { flexDirection: "row", showBoth: false, labels: {
                        Sortable: 'Sortable',
                        Filterable: 'Filterable',
                        Aggregatable: 'Aggregatable',
                        Groupable: 'Groupable',
                        Moveable: 'Moveable',
                        Pivotable: 'Pivotable',
                    }, sortable: true, filterable: true, moveable: true, pivotable: true, groupable: true, aggregatable: true })),
            React.createElement(Flex, { mb: 1, alignItems: "center", flexDirection: "row" },
                React.createElement(AdaptableFormControlTextClear, { value: searchInputValue, OnTextChange: setSearchInputValue, placeholder: "Type to search", style: { border: 0, margin: 3, width: '100%' } }),
                React.createElement(Box, { flex: 1 })),
            React.createElement(HelpBlock, { mb: 1, fontSize: 2, display: 'flex', "data-name": "column-help", alignItems: 'center' },
                React.createElement(Box, null,
                    React.createElement(NaturallySizedIcon, { name: "info" })),
                React.createElement(Text, { ml: 1 }, "Expand each Column to set Width, Pinnning and a custom Header")),
            React.createElement(ReorderDraggable, { toIdentifier: (option) => `${option.columnId}`, order: currentOrder, renderOption: (option) => {
                    return (React.createElement(ColumnRow, { onColumnNameChange: handleColumnNameChange, onColumnWidthChange: handleColumnWidthChange, onColumnVisibilityChange: handleColumnVisibilityChange, onPinChange: handlePinChange, layout: layout, column: option }));
                }, onChange: handleColumnsChange }),
            renderSelectionSection({
                options: visibleIds,
                value: visibleIds,
                toLabel,
                toIdentifier,
                isOptionDisabled: (colId) => adaptable.api.columnApi.isAutoRowGroupColumn(colId) ||
                    adaptable.api.columnApi.getColumnWithColumnId(colId)?.hideable === false,
                xSelectedLabel: () => {
                    return `Selected Columns:`;
                },
                noSelectionLabel: `No Columns Selected`,
                onChange: handleColumnsChange,
                onSelectAll: () => {
                    props.onChange({
                        ...layout,
                        ColumnVisibility: {},
                        TableColumns: ColumnOrderAllColumns.map((col) => col.columnId),
                    });
                },
                onClear: () => {
                    handleColumnsChange([]);
                },
                onClearOption: (colId) => {
                    handleColumnVisibilityChange(colId, false);
                },
            }))));
};
