import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/*
 * Copyright (c) 2023-2025 Datalayer, Inc.
 * Distributed under the terms of the Modified BSD License.
 */
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useIsMounted } from 'usehooks-ts';
import { Button, FormControl, Select, Spinner, Text, TextInput, ToggleSwitch, Tooltip, IconButton, } from '@primer/react';
import { Dialog } from '@primer/react/experimental';
import { AlertIcon } from '@primer/octicons-react';
import { Box } from '@datalayer/primer-addons';
import { useJupyterReactStore } from '@datalayer/jupyter-react';
import { USAGE_ROUTE } from '../../routes/index.js';
import { useNavigate } from '../../hooks/index.js';
import { NO_RUNTIME_AVAILABLE_LABEL } from '../../i18n/index.js';
import { iamStore, useCoreStore, useIAMStore } from '../../state/index.js';
import { createNotebook, sleep } from '../../utils/index.js';
import { Markdown } from '../display/index.js';
import { Timer } from '../progress/index.js';
import { FlashClosable } from '../flashes/index.js';
import { RuntimeReservationControl, MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES, } from './RuntimeReservationControl.js';
/**
 * Initial time in milliseconds before retrying in case no kernels are available
 */
const NOT_AVAILABLE_INIT_RETRY = 10_000;
/**
 * Number of trials in case of unavailable kernels
 */
const NOT_AVAILABLE_RETRIES = 5;
/**
 * Start Runtime Launcher Dialog.
 */
export function RuntimeLauncherDialog(props) {
    const { dialogTitle, kernelSnapshot, manager, onSubmit, markdownParser, sanitizer, upgradeSubscription, startRuntime = true, } = props;
    const hasExample = startRuntime === 'with-example';
    const user = iamStore.getState().user;
    const environments = manager.environments.get();
    const { configuration } = useCoreStore();
    const { credits, refreshCredits } = useIAMStore();
    let navigate;
    try {
        // eslint-disable-next-line react-hooks/rules-of-hooks
        navigate = useNavigate();
    }
    catch (reason) {
        // TODO when would this component be shown outside of a react-router? navigation is only available within a react-router.
        console.warn(reason);
    }
    const jupyterReactStore = useJupyterReactStore();
    const jupyterLabAdapter = jupyterReactStore.jupyterLabAdapter;
    const [selection, setSelection] = useState((kernelSnapshot?.environment || environments[0]?.name) ?? '');
    const [timeLimit, setTimeLimit] = useState(Math.min(credits?.available ?? 0, 10));
    const [runtimeName, setRuntimeName] = useState(environments[0]?.runtime?.givenNameTemplate || environments[0]?.title || '');
    // Whether the runtim name has been changed by the user or not
    const [hasCustomRuntimeName, setHasCustomRuntimeName] = useState(false);
    const [userStorage, setUserStorage] = useState(false);
    const [openExample, setOpenExample] = useState(false);
    const [waitingForRuntime, setWaitingForRuntime] = useState(false);
    const [error, setError] = useState();
    const [flashLevel, setFlashLevel] = useState('danger');
    const isMounted = useIsMounted();
    useEffect(() => {
        if (startRuntime) {
            refreshCredits();
        }
    }, [startRuntime]);
    const spec = useMemo(() => environments.find(spec => spec.name === selection), [environments, selection]);
    const description = spec?.description ?? '';
    const burningRate = spec?.burning_rate ?? 1;
    const creditsToMinutes = 1.0 / burningRate / 60.0;
    const max = Math.floor((credits?.available ?? 0) * creditsToMinutes);
    const outOfCredits = startRuntime && (!credits?.available || max < Number.EPSILON);
    const handleSelectionChange = useCallback((e) => {
        const selection = e.target.value;
        setSelection(selection);
        if (!hasCustomRuntimeName) {
            const spec = environments.find(env => env.name === selection);
            setRuntimeName(spec?.runtime?.givenNameTemplate || spec?.title || '');
        }
    }, [setSelection, hasCustomRuntimeName]);
    const handleSubmitRuntime = useCallback(async () => {
        if (selection) {
            setError(undefined);
            setWaitingForRuntime(true);
            const spec = environments.find(s => s.name === selection);
            const desc = {
                name: selection,
                language: spec?.language ?? '',
                location: 'remote',
                displayName: runtimeName ?? spec?.title,
            };
            const creditsLimit = Math.min(timeLimit, MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES) /
                creditsToMinutes;
            desc.params = {};
            if (startRuntime === 'defer') {
                desc.params['creditsLimit'] = creditsLimit;
            }
            if (userStorage) {
                desc.params['capabilities'] = ['user_storage'];
            }
            let success = true;
            if (startRuntime && startRuntime !== 'defer') {
                success = false;
                let availableTrial = 1;
                let retryDelay = NOT_AVAILABLE_INIT_RETRY;
                // Should return success status.
                const startNewKernel = async () => {
                    try {
                        const connection = await manager.runtimesManager.startNew({
                            environmentName: selection,
                            type: 'notebook',
                            givenName: runtimeName,
                            creditsLimit: creditsLimit,
                            capabilities: userStorage ? ['user_storage'] : undefined,
                            snapshot: kernelSnapshot?.id,
                        }, {
                            username: user?.handle,
                            handleComms: true,
                        });
                        desc.kernelId = connection.id;
                        if (jupyterLabAdapter?.jupyterLab && hasExample && openExample) {
                            const example = environments.find(spec => spec.name === selection)?.example;
                            if (example) {
                                const options = {
                                    kernelId: connection.id,
                                    kernelName: connection.name,
                                };
                                createNotebook({
                                    app: jupyterLabAdapter.jupyterLab,
                                    name: selection,
                                    url: example,
                                    options,
                                });
                            }
                        }
                        // Close the connection as we are not using it.
                        connection.dispose();
                    }
                    catch (error) {
                        let msg = _jsx(Text, { children: "Failed to create the remote runtime\u2026" });
                        let level = 'danger';
                        let retry = false;
                        if (error.response?.status === 503) {
                            if (availableTrial++ <= NOT_AVAILABLE_RETRIES) {
                                retry = true;
                                msg = (_jsxs(Text, { children: ["The runtime you have requested is currently not available due to resource limitations. Leave this dialog open, new trial in ", _jsx(Timer, { duration: retryDelay * 0.001 }), ` (${availableTrial - 1}/${NOT_AVAILABLE_RETRIES}).`] }));
                            }
                            else {
                                msg = _jsx(Text, { children: NO_RUNTIME_AVAILABLE_LABEL });
                            }
                            level = 'warning';
                        }
                        else if (error.name === 'MaxRuntimesExceededError') {
                            msg = (_jsx(Text, { children: "You reached your remote runtime limits. Stop existing runtimes before starting new ones." }));
                            level = 'warning';
                        }
                        else if (error.name === 'RuntimeUnreachable') {
                            msg = (_jsx(Text, { children: "The runtime has been created but can not be accessed. Please contact your IT support team to report this issue." }));
                        }
                        setFlashLevel(level);
                        console.error(msg, error);
                        setError(msg);
                        if (retry) {
                            await sleep(retryDelay);
                            retryDelay *= 2;
                            if (isMounted()) {
                                return await startNewKernel();
                            }
                        }
                        return false;
                    }
                    finally {
                        setWaitingForRuntime(false);
                    }
                    return true;
                };
                // Start the kernel if the reservation succeeded.
                success = await startNewKernel();
            }
            if (success && isMounted()) {
                onSubmit(desc);
            }
        }
    }, [
        manager,
        selection,
        startRuntime,
        runtimeName,
        onSubmit,
        userStorage,
        openExample,
        jupyterLabAdapter,
        timeLimit,
        isMounted,
    ]);
    const handleUserStorageChange = useCallback((e) => {
        e.preventDefault();
        setUserStorage(!userStorage);
    }, [userStorage]);
    const handleSwitchClick = useCallback((e) => {
        e.preventDefault();
        setOpenExample(!openExample);
    }, [openExample]);
    const handleUpgrade = useCallback(() => {
        if (upgradeSubscription) {
            navigate?.(upgradeSubscription);
        }
    }, [navigate, upgradeSubscription]);
    const handleKernelNameChange = useCallback((e) => {
        if (typeof e.target.value === 'string') {
            setRuntimeName(e.target.value);
            setHasCustomRuntimeName(true);
        }
    }, []);
    // TODO title color is enforced for JupyterLab.
    // This may be fixed in the jupyter-react theme (Primer generates h1 for the dialog title).
    return (_jsx(Dialog, { title: _jsx("span", { style: { color: 'var(--fgColor-default)' }, children: dialogTitle || 'Launch a new Runtime' }), onClose: () => {
            onSubmit(undefined);
        }, footerButtons: [
            {
                buttonType: 'default',
                onClick: () => {
                    onSubmit(undefined);
                },
                content: 'Cancel',
                disabled: waitingForRuntime,
            },
            {
                buttonType: 'primary',
                onClick: handleSubmitRuntime,
                content: waitingForRuntime ? (_jsx(Spinner, { size: "small" })) : (startRuntime ?? true) ? ('Launch') : ('Assign from the Environment'),
                disabled: waitingForRuntime || outOfCredits || timeLimit < Number.EPSILON,
                autoFocus: true,
            },
        ], children: _jsxs(Box, { as: "form", onKeyDown: event => {
                if (event.defaultPrevented) {
                    return;
                }
                if (event.key === 'Enter') {
                    event.preventDefault();
                    handleSubmitRuntime();
                }
            }, children: [_jsxs(FormControl, { disabled: !!kernelSnapshot?.environment || environments.length === 0, children: [_jsx(FormControl.Label, { children: "Environment" }), _jsx(Select, { name: "environment", disabled: !!kernelSnapshot?.environment || environments.length === 0, value: selection, onChange: handleSelectionChange, block: true, children: environments.map(spec => (_jsxs(Select.Option, { value: spec.name, children: [spec.name, spec.title && (_jsxs(_Fragment, { children: [' - ', spec.title] }))] }, spec.name))) }), _jsx(FormControl.Caption, { children: _jsx(_Fragment, { children: markdownParser ? (_jsx(Box, { sx: { img: { maxWidth: '100%' } }, children: _jsx(Markdown, { text: description, markdownParser: markdownParser, sanitizer: sanitizer }) })) : (description) }) })] }), startRuntime && (_jsx(RuntimeReservationControl, { addCredits: navigate
                        ? () => {
                            navigate(USAGE_ROUTE);
                        }
                        : undefined, disabled: outOfCredits, label: 'Time reservation', max: max, time: timeLimit, burningRate: burningRate, onTimeChange: setTimeLimit, error: outOfCredits ? 'You must add credits to your account.' : undefined })), !configuration.whiteLabel && (_jsxs(FormControl, { layout: "horizontal", children: [_jsxs(FormControl.Label, { id: "user-storage-label", children: ["User storage", _jsx(Tooltip, { text: 'The runtime will be slower to start.', direction: "e", style: { marginLeft: 3 }, children: _jsx(IconButton, { icon: AlertIcon, "aria-label": "", variant: "invisible" }) })] }), _jsx(ToggleSwitch, { checked: userStorage, size: "small", onClick: handleUserStorageChange, "aria-labelledby": "user-storage-label" })] })), _jsxs(FormControl, { sx: { paddingTop: '10px' }, children: [_jsx(FormControl.Label, { children: "Runtime name" }), _jsx(TextInput, { name: "name", value: runtimeName, onChange: handleKernelNameChange, block: true })] }), hasExample &&
                    jupyterLabAdapter?.jupyterLab &&
                    !configuration.whiteLabel && (_jsxs(FormControl, { sx: { paddingTop: '10px' }, children: [_jsx(FormControl.Label, { id: "open-example-label", children: "Open example notebook" }), _jsx(ToggleSwitch, { disabled: !environments.find(spec => spec.name === selection)?.example, checked: openExample, size: "small", onClick: handleSwitchClick, "aria-labelledby": "open-example-label" })] })), error && (_jsx(FlashClosable, { variant: flashLevel, actions: navigate && upgradeSubscription && flashLevel === 'warning' ? (_jsx(Button, { onClick: handleUpgrade, title: 'Upgrade your subscription.', children: "Upgrade" })) : undefined, children: error }))] }) }));
}
