import type {Proxy} from 'workerpool';

import type {
    DashWidgetConfig,
    ServerChartsConfig,
    Shared,
    StringParams,
    TenantSettings,
} from '../../../../../shared';
import {getFieldUISettings, getServerFeatures} from '../../../../../shared';
import type {SourceRequests} from '../../../../modes/charts/plugins/datalens/url/types';
import {addColorPaletteRequest} from '../../../../modes/charts/plugins/helpers/color-palettes';
import {registry} from '../../../../registry';
import {getDefaultColorPaletteId} from '../utils';
import type {WizardWorker} from '../wizard-worker/types';
import {getChartApiContext} from '../wizard-worker/utils';

import type {ChartBuilder, ChartBuilderResult} from './types';

const ONE_SECOND = 1000;
const PREPARE_EXECUTION_TIMEOUT = ONE_SECOND * 9.5;

type WizardChartBuilderArgs = {
    timeouts?: {
        params?: number;
        prepare?: number;
        config?: number;
        libraryConfig?: number;
        sources?: number;
    };
    userLogin: string | null;
    userLang: string;
    config: {
        data: {
            shared: string;
        };
    };
    widgetConfig?: DashWidgetConfig['widgetConfig'];
    isScreenshoter: boolean;
    worker: Proxy<WizardWorker>;
    tenantSettings: TenantSettings;
};

export const getWizardChartBuilder = async (
    args: WizardChartBuilderArgs,
): Promise<ChartBuilder> => {
    const {config, widgetConfig, userLang, worker, timeouts = {}, tenantSettings} = args;
    const wizardWorker = worker;
    let shared: Record<string, any>;

    const app = registry.getApp();
    const features = getServerFeatures(app.nodekit.ctx);
    const {getAvailablePalettesMap} = registry.common.functions.getAll();
    const palettes = getAvailablePalettesMap();
    const defaultColorPaletteId = getDefaultColorPaletteId({
        ctx: app.nodekit.ctx,
        tenantSettings,
    });

    // Nothing happens here - just for compatibility with the editor
    const emptyStep =
        (name: string) => async (options: {params: StringParams; actionParams: StringParams}) => {
            const {params, actionParams} = options;
            const timeStart = process.hrtime();
            const context = getChartApiContext({
                name,
                shared,
                params,
                actionParams,
                widgetConfig,
                userLang,
            });

            return {
                exports: {},
                executionTiming: process.hrtime(timeStart),
                name,
                runtimeMetadata: context.__runtimeMetadata,
            };
        };

    const chartBuilder: ChartBuilder = {
        type: 'WIZARD',
        buildShared: async () => {
            if (typeof config.data.shared === 'string') {
                shared = JSON.parse(config.data.shared);
            } else {
                shared = config.data.shared;
            }
        },
        buildModules: async () => {
            return {};
        },

        buildParams: async (args) => {
            if (typeof wizardWorker.buildParams === 'function') {
                const timeStart = process.hrtime();
                const execResult = await wizardWorker
                    .buildParams({shared, userLang})
                    .timeout(timeouts.params || ONE_SECOND);

                return {
                    executionTiming: process.hrtime(timeStart),
                    name: 'Params',
                    ...execResult,
                };
            }

            return emptyStep('Params')(args);
        },

        buildUrls: async (options) => {
            const {params, actionParams} = options;
            const timeStart = process.hrtime();
            const execResult = await wizardWorker
                .buildSources({
                    shared: shared as Shared,
                    params,
                    actionParams,
                    widgetConfig,
                    userLang,
                    palettes,
                    features,
                })
                .timeout(timeouts.sources || ONE_SECOND);

            return {
                executionTiming: process.hrtime(timeStart),
                name: 'Sources',
                ...execResult,
            };
        },

        buildPaletteSources: async (options) => {
            const {sources = {}} = options;
            const timeStart = process.hrtime();

            const result: SourceRequests = {};

            addColorPaletteRequest({result, colorPaletteId: defaultColorPaletteId, palettes});
            Object.values(sources).forEach((s) => {
                (s.datasetFields ?? []).forEach((d) => {
                    const uiSettings = getFieldUISettings({field: d});
                    const palette = uiSettings?.palette;

                    if (palette) {
                        addColorPaletteRequest({result, colorPaletteId: palette, palettes});
                    }
                });
            });

            return {
                exports: result,
                executionTiming: process.hrtime(timeStart),
                name: 'PaletteSources',
                runtimeMetadata: {},
            };
        },

        buildChartLibraryConfig: async (options) => {
            const {params, actionParams} = options;
            const timeStart = process.hrtime();
            const execResult = await wizardWorker
                .buildLibraryConfig({
                    shared: shared as ServerChartsConfig,
                    params,
                    actionParams,
                    widgetConfig,
                    userLang,
                    features,
                })
                .timeout(timeouts.libraryConfig || ONE_SECOND);

            return {
                executionTiming: process.hrtime(timeStart),
                name: 'Highcharts',
                ...execResult,
            } as ChartBuilderResult;
        },

        buildChartConfig: async (options) => {
            const {params, actionParams} = options;
            const timeStart = process.hrtime();
            const execResult = await wizardWorker
                .buildChartConfig({
                    shared: shared as ServerChartsConfig,
                    params,
                    actionParams,
                    widgetConfig,
                    userLang,
                    features,
                })
                .timeout(timeouts.config || ONE_SECOND);

            return {
                executionTiming: process.hrtime(timeStart),
                name: 'Config',
                ...execResult,
            };
        },

        buildChart: async (options) => {
            const {data, sources, params, actionParams} = options;
            const timeStart = process.hrtime();
            const execResult = await wizardWorker
                .buildChart({
                    shared: shared as ServerChartsConfig,
                    params,
                    actionParams,
                    widgetConfig,
                    userLang,
                    data,
                    palettes,
                    features,
                    defaultColorPaletteId,
                    sources,
                })
                .timeout(timeouts.prepare || PREPARE_EXECUTION_TIMEOUT);

            return {
                executionTiming: process.hrtime(timeStart),
                name: 'Prepare',
                ...execResult,
            };
        },

        buildUI: emptyStep('Controls'),
        dispose: () => {},
    };

    return chartBuilder;
};
