import { ApiBase } from '../Implementation/ApiBase';
import StringExtensions from '../../Utilities/Extensions/StringExtensions';
import FormatHelper from '../../Utilities/Helpers/FormatHelper';
import ObjectFactory from '../../Utilities/ObjectFactory';
import * as ModuleConstants from '../../Utilities/Constants/ModuleConstants';
import { errorOnce } from '../../agGrid/AdaptableLogger';
export class FormatColumnInternalApi extends ApiBase {
    /**
     * Retrieves all Format Columns in Adaptable State with the `Style` property set
     * @returns format columns
     */
    getAllFormatColumnWithStyle() {
        return this.getFormatColumnApi()
            .getFormatColumns()
            .filter((fc) => fc.Style);
    }
    /**
     * Retrieves all Format Columns in Adaptable State with the `Style` or the `CellAlignment` property set
     * @returns format columns
     */
    getAllFormatColumnWithStyleAndCellAlignment() {
        return this.getFormatColumnApi()
            .getFormatColumns()
            .filter((fc) => fc.Style || fc.CellAlignment);
    }
    /**
     * Retrieves all Format Columns in Adaptable State with `DisplayFormat` property set
     * @returns format columns
     */
    getAllFormatColumnWithDisplayFormat() {
        return this.getFormatColumnApi()
            .getFormatColumns()
            .filter((fc) => fc.DisplayFormat);
    }
    /**
     * Retrieves all Format Columns in Adaptable State with `CellAlignment` property set
     * @returns format columns
     */
    getAllFormatColumnWithCellAlignment() {
        return this.getFormatColumnApi()
            .getFormatColumns()
            .filter((fc) => fc.CellAlignment);
    }
    /**
     * Get all FormatColumns which are defined for this column and have a custom AdaptableStyle
     * @param column
     * @param config
     * @returns list of FormatColumn
     */
    getFormatColumnsWithStyleForColumn(column, config) {
        const formatColumns = this.getAllFormatColumnWithStyleAndCellAlignment().filter((formatColumn) => config?.includeSuspended || !formatColumn.IsSuspended);
        return this.getFormatColumnWithColumnInScope(formatColumns, column);
    }
    /**
     * Gets Format Column if any for given Column which includes Style element with ClassName
     * @param column The Column for which to retrieve the Format Column
     * @returns format columns
     */
    getFormatColumnWithStyleClassNameForColumn(column, config) {
        return this.getFormatColumnsWithStyleForColumn(column, config).filter((formatColumn) => StringExtensions.IsNotNullOrEmpty(formatColumn?.Style?.ClassName));
    }
    getFormatColumnWithColumnInScope(formatColumns, column) {
        return this.getFormatColumnInColumnScope(formatColumns).filter((scopedFormatColumn) => this.getColumnScopeApi().isColumnInScope(column, scopedFormatColumn.Scope));
    }
    // TODO is this really needed, I don't think it achieves anything
    getFormatColumnInColumnScope(formatColumns) {
        // we need to maintain the format columns order, therefore we will extract all 3 scope types in a single iteration
        return formatColumns.filter((fc) => {
            return (
                // this.getFormatColumnsWithColumnScope(formatColumns)
                (this.getColumnScopeApi().scopeHasColumns(fc.Scope) ||
                    // this.getFormatColumnsWithDataTypeScope(formatColumns)
                    this.getColumnScopeApi().scopeHasDataType(fc.Scope) ||
                    // this.getFormatColumnsWithAllScope(formatColumns)
                    this.getColumnScopeApi().scopeIsAll(fc.Scope) || this.getColumnScopeApi().scopeHasColumnType(fc.Scope))
            );
        });
    }
    /**
     * Get all FormatColumns which are defined for this column and have a custom DisplayFormat
     * @param column
     * @param config
     * @returns list of FormatColumn
     */
    getFormatColumnsWithDisplayFormatForColumn(column, config) {
        const formatColumns = this.getAllFormatColumnWithDisplayFormat().filter((formatColumn) => config?.includeSuspended || !formatColumn.IsSuspended);
        return this.getFormatColumnWithColumnInScope(formatColumns, column);
    }
    /**
     * Format value according to format options.
     *
     * @param customDisplayFormatterContext context that includes value to format
     * @param options formatter options
     */
    getNumberFormattedValue(value, node, column, options) {
        const preparedValue = this.applyCustomFormatters(value, node, column, options);
        return FormatHelper.NumberFormatter(preparedValue, options, node, column, this.getAdaptableApi());
    }
    /**
     * Format value according to format options.
     *
     * @param value  context that includes value to format
     * @param options formatter options
     */
    getStringFormattedValue(value, node, column, options) {
        const preparedValue = this.applyCustomFormatters(value, node, column, options);
        return FormatHelper.StringFormatter(preparedValue, options, node, column, this.getAdaptableApi());
    }
    /**
     * Format value according to format options.
     *
     * @param customDisplayFormatterContext  context that includes value to format
     * @param options formatter options
     */
    getDateFormattedValue(value, node, abColumn, options) {
        const preparedValue = this.applyCustomFormatters(value, node, abColumn, options);
        const dateFormatterOptions = options;
        // only use DateFormatter if we have a pattern
        return dateFormatterOptions.Pattern
            ? FormatHelper.DateFormatter(preparedValue, options)
            : preparedValue;
    }
    applyCustomFormatters(value, node, abColumn, options) {
        const columnCustomFormatters = options?.CustomDisplayFormats ?? [];
        if (!columnCustomFormatters?.length) {
            return value;
        }
        const customFormattersFromOptions = this.getFormatColumnOptions()?.customDisplayFormatters ?? [];
        // formatters are applied in the order they are defined in the options
        const customFormatters = customFormattersFromOptions.filter((customFormatterOption) => columnCustomFormatters.includes(customFormatterOption.id));
        const customDisplayFormatterContext = ObjectFactory.CreateCustomDisplayFormatterContext(value, node, abColumn, this.getAdaptableApi());
        return customFormatters.reduce((context, formatter) => {
            if (formatter && formatter.handler) {
                return formatter.handler(context);
            }
            return context.cellValue;
        }, customDisplayFormatterContext);
    }
    /**
     * Returns all Predicates appropriate for the given Scope
     * @param scope Scope to check
     */
    getFormatColumnDefsForScope(scope) {
        return this.getAdaptableApi()
            .predicateApi.internalApi.getFormatColumnPredicateDefs(scope)
            .filter((predicateDef) => this.getColumnScopeApi().isScopeInScope(scope, predicateDef.columnScope));
    }
    /**
     * Checks if format column is relevant for a given cell (intersection of given AdaptableColumn and RowNode)
     *
     * @param formatColumn
     * @param column
     * @param params
     */
    formatColumnShouldRender(formatColumn, column, rowNode, cellValue) {
        // suspended is important to be first
        if (formatColumn.IsSuspended) {
            return false;
        }
        const isSummaryNode = this.getGridApi().isSummaryNode(rowNode);
        const isGroupedRowNode = this.getGridApi().isGroupRowNode(rowNode);
        // For Summary Rows cannot be excluded
        if (isSummaryNode) {
            if (formatColumn.RowScope?.ExcludeSummaryRows) {
                return false;
            }
        }
        else if (isGroupedRowNode) {
            if (formatColumn.RowScope?.ExcludeGroupRows) {
                return false;
            }
        }
        else {
            if (formatColumn.RowScope?.ExcludeDataRows) {
                return false;
            }
        }
        if (!formatColumn.Rule) {
            return true;
        }
        // first run the predicate
        if (formatColumn.Rule.Predicates && formatColumn.Rule?.Predicates?.length) {
            const predicateDefHandlerContext = {
                value: cellValue,
                oldValue: null,
                displayValue: cellValue,
                node: rowNode,
                column: column,
                ...this.getAdaptableInternalApi().buildBaseContext(),
            };
            return this.evaluatePredicate(formatColumn, predicateDefHandlerContext);
        } // then run the Expression
        else if (formatColumn.Rule.BooleanExpression) {
            return this.evaluateExpression(formatColumn, rowNode);
        }
        // nothing has passed then return false
        return false;
    }
    evaluatePredicate(formatColumn, predicateDefHandlerContext) {
        return this.getPredicateApi().handleColumnPredicates(formatColumn.Rule?.Predicates, predicateDefHandlerContext, false);
    }
    evaluateExpression(formatColumn, node) {
        const isValidExpression = this.getExpressionApi().isValidBooleanExpression(formatColumn.Rule.BooleanExpression, ModuleConstants.FormatColumnModuleId, `Invalid format column rule '${formatColumn.Rule.BooleanExpression}'`);
        try {
            return (isValidExpression &&
                this.getAdaptableApi()
                    .internalApi.getQueryLanguageService()
                    .evaluateBooleanExpression(formatColumn.Rule.BooleanExpression, ModuleConstants.FormatColumnModuleId, node));
        }
        catch (error) {
            errorOnce(error.message);
            return false;
        }
    }
    /**
     * Extract from the given FormatColumns only the ones which are relevant for a given cell (intersection of given AdaptableColumn and RowNode)
     *
     * @param formatColumns
     * @param column
     * @param params
     */
    getFormatColumnsRelevantForColumn(formatColumns, column, params) {
        return formatColumns.filter((formatColumn) => this.formatColumnShouldRender(formatColumn, column, params.node, params.value));
    }
    /**
     * Extract from the given FormatColumns the one which is the most relevant  for a given cell (intersection of given AdaptableColumn and RowNode)
     *
     * @param formatColumns
     * @param column
     * @param params
     */
    getMostRelevantFormatColumnForColumn(formatColumns, column, params) {
        return formatColumns.find((formatColumn) => this.formatColumnShouldRender(formatColumn, column, params.node, params.value));
    }
    /**
     * Retrieves all Format Columns which have an Expression
     * @returns Format Columns with Expression
     */
    getFormatColumnsWithExpression() {
        return this.getFormatColumnApi()
            .getFormatColumns()
            .filter((fc) => !!fc.Rule?.BooleanExpression);
    }
    getFormatColumnsDependentOnColumns(columnSet) {
        return this.getFormatColumnApi()
            .getFormatColumns()
            .filter((fc) => fc.Rule?.Predicates?.some((p) => columnSet.has(p.ColumnId)));
    }
    /**
     * Retrieves the columns that need rerendering based on format column predicates.
     */
    getFormatColumnColumnsDependentOnColumnChange(column) {
        const impactedColumnIds = new Set();
        impactedColumnIds.add(column.columnId);
        this.getCalculatedColumnApi()
            .internalApi.getCalculatedColumnsDependentOnColumn(column)
            .forEach((calculatedColumnId) => {
            impactedColumnIds.add(calculatedColumnId);
        });
        const columnsThatNeedRefresh = new Set();
        this.getFormatColumnsDependentOnColumns(impactedColumnIds).forEach((formatColumn) => {
            this.getAdaptableApi()
                .columnScopeApi.getColumnsInScope(formatColumn.Scope)
                .forEach((col) => {
                columnsThatNeedRefresh.add(col.columnId);
            });
        });
        return [...columnsThatNeedRefresh];
    }
}
