import { jsx as _jsx } from "react/jsx-runtime";
// (C) 2022-2026 GoodData Corporation
import { union } from "lodash-es";
import { InvariantError } from "ts-invariant";
import { HiddenFilterBar } from "../../presentation/filterBar/filterBar/HiddenFilterBar.js";
import { RenderModeAwareFilterBar } from "../../presentation/filterBar/filterBar/RenderModeAwareFilterBar.js";
const DefaultFilterBarRendererProvider = () => {
    return RenderModeAwareFilterBar;
};
class FilterBarCustomizerState {
    /*
     * Chain of 'core' providers. Core providers are evaluated from last to first. As soon as some provider
     * returns a Component, then that component will be used for rendering. If provider returns undefined,
     * the evaluation continues to next provider in the chain.
     *
     * Note: the chain is 'primed' with a provider that always returns the default DateFilter renderer. This is
     * essential to allow the decorations to work - purely because decorations can only work if there is
     * something to decorate. See constructor.
     */
    coreProviderChain;
    /*
     * Core provider encapsulates resolution using the chain of core providers.
     */
    coreProvider = (filterBar) => {
        const providerStack = [...this.coreProviderChain].reverse();
        for (const provider of providerStack) {
            const Component = provider(filterBar);
            if (Component) {
                return Component;
            }
        }
        // if this happens then the provider chain got messed up. by default the chain contains the default
        // provider which never returns undefined
        throw new InvariantError();
    };
    /*
     * Root provider is THE provider that should be used in the dashboard extension properties. The
     * provider function included here will reflect the setup where there may be N registered decorators
     * sitting on top of a chain of core providers.
     *
     * In the initial state the root provider IS the core provider - meaning no decorations. As the
     * decorators get registered, the rootProvider changes.
     */
    rootProvider = this.coreProvider;
    renderingMode = undefined;
    logger;
    constructor(defaultProvider, logger) {
        this.coreProviderChain = [defaultProvider];
        this.logger = logger;
    }
    getRenderingMode = () => {
        return this.renderingMode ?? "default";
    };
    setRenderingMode = (renderingMode) => {
        if (this.renderingMode) {
            this.logger.warn(`Redefining filter bar rendering mode to "${renderingMode}". Previous definition will be discarded.`);
        }
        this.renderingMode = renderingMode;
    };
    addCustomProvider(provider) {
        this.coreProviderChain.push(provider);
    }
    getRootProvider() {
        return this.rootProvider;
    }
    switchRootProvider(provider) {
        this.rootProvider = provider;
    }
}
class SealedFilterBarCustomizerState {
    state;
    logger;
    constructor(state, logger) {
        this.state = state;
        this.logger = logger;
    }
    getRenderingMode = () => {
        return this.state.getRenderingMode();
    };
    setRenderingMode = (_renderingMode) => {
        this.logger.warn(`Attempting to set filter bar rendering mode outside of plugin registration. Ignoring.`);
    };
    addCustomProvider = (_provider) => {
        this.logger.warn(`Attempting to customize FilterBar rendering outside of plugin registration. Ignoring.`);
    };
    switchRootProvider = (_provider) => {
        this.logger.warn(`Attempting to customize FilterBar rendering outside of plugin registration. Ignoring.`);
    };
    getRootProvider = () => {
        return this.state.getRootProvider();
    };
}
/**
 * @internal
 */
export class DefaultFilterBarCustomizer {
    logger;
    mutationContext;
    state;
    updated = false;
    constructor(logger, mutationContext, defaultProvider = DefaultFilterBarRendererProvider) {
        this.logger = logger;
        this.mutationContext = mutationContext;
        this.state = new FilterBarCustomizerState(defaultProvider, logger);
    }
    setRenderingMode = (mode) => {
        this.state.setRenderingMode(mode);
        return this;
    };
    withCustomProvider = (provider) => {
        this.state.addCustomProvider(provider);
        this.mutationContext.filterBar = union(this.mutationContext.filterBar, ["provider"]);
        this.updated = true;
        return this;
    };
    withCustomDecorator(providerFactory) {
        // snapshot current root provider
        const rootSnapshot = this.state.getRootProvider();
        // call user's factory in order to obtain the actual provider - pass the current root so that user's
        // code can use it to obtain component to decorate
        const decoratorProvider = providerFactory(rootSnapshot);
        // construct new root provider; this will be using user's provider with a fallback to root provider
        // in case user's code does not return anything
        const newRootProvider = (props) => {
            const Component = decoratorProvider(props);
            if (Component) {
                return Component;
            }
            return rootSnapshot(props);
        };
        // finally modify the root provider; next time someone registers decorator, it will be on top of
        // this currently registered one
        this.state.switchRootProvider(newRootProvider);
        this.mutationContext.filterBar = union(this.mutationContext.filterBar, ["decorator"]);
        this.updated = true;
        return this;
    }
    getCustomizerResult = () => {
        const hidden = this.state.getRenderingMode() === "hidden";
        const updated = this.updated;
        return {
            // if rendering mode is "hidden", explicitly replace the component with HiddenFilterBar,
            // otherwise do nothing to allow the default or any custom component provided by the embedding application
            // to kick in
            FilterBarComponent: hidden
                ? HiddenFilterBar
                : updated
                    ? (props) => {
                        const Comp = this.state.getRootProvider()(props);
                        return _jsx(Comp, { ...props });
                    }
                    : undefined,
        };
    };
    sealCustomizer = () => {
        this.state = new SealedFilterBarCustomizerState(this.state, this.logger);
        return this;
    };
}
