/*
 * Copyright (c) 2023-2025 Datalayer, Inc.
 * Distributed under the terms of the Modified BSD License.
 */
import { useStore } from 'zustand';
import { createStore } from 'zustand/vanilla';
import { JSONExt } from '@lumino/coreutils';
import { Poll } from '@lumino/polling';
import { getRuntimes } from '../../stateful/runtimes/index.js';
import { coreStore } from './CoreState.js';
import { iamStore } from './IAMState.js';
/**
 * Kernel store
 */
export const runtimesStore = createStore((set, get) => {
    return {
        configuration: {
            maxNotebookRuntimes: 5,
            maxCellRuntimes: 3,
        },
        setConfiguration: (configuration) => {
            set(state => JSONExt.deepEqual(state.configuration, configuration)
                ? {}
                : { configuration: { ...configuration } });
        },
        runtimesRunUrl: coreStore.getState().configuration?.runtimesRunUrl,
        tab: 0.0,
        getIntTab: () => Math.floor(get().tab),
        setTab: (tab) => set(state => ({ tab })),
        /**
         * Remote runtime pods.
         */
        runtimePods: [],
        /**
         * Refresh the runtime pods.
         */
        refreshRuntimePods: async () => {
            const servers = await getRuntimes();
            // Update the state with the Remote Kernels.
            if (!JSONExt.deepEqual(get().runtimePods, servers)) {
                set({ runtimePods: [...servers] });
            }
        },
        /**
         * Cached runtime models.
         */
        runtimeModels: [],
        /**
         * Add a runtime model
         */
        addRuntimeModel: (model) => {
            const kernels = get().runtimeModels;
            // TODO
            // We need to review the IRuntimeModel/IRuntimePod/Kernel.IModel and their id/uid handling.
            // The id is the kernel id, which is no more always present for some reasons.
            // So we need to also check the uid of the model.
            const index = kernels.findIndex(m => (model.id === m.id || model.uid === m.uid) ?? -1);
            if (index < 0) {
                set({ runtimeModels: [...kernels, model] });
            }
        },
        /**
         * Remove a runtime model by ID.
         */
        removeRuntimeModel: (id) => {
            const kernels = [...get().runtimeModels];
            const index = kernels?.findIndex(model => id === model.id) ?? -1;
            if (index >= 0) {
                kernels.splice(index, 1);
                set({ runtimeModels: kernels });
            }
        },
        setRuntimeModels: (models) => {
            if (!JSONExt.deepEqual(get().runtimeModels, models)) {
                set({ runtimeModels: [...models] });
            }
        },
        multiServiceManager: undefined,
        setMultiServiceManager: multiServiceManager => {
            set(state => ({ multiServiceManager }));
        },
        showDisclaimer: false,
        setShowDisclaimer: showDisclaimer => {
            set(state => ({ showDisclaimer }));
        },
        /**
         * Kernel Snapshots.
         */
        runtimeSnapshots: [],
        /**
         * Add a Kernel Snapshot
         */
        addRuntimeSnapshot: (snapshot) => {
            const snapshots = get().runtimeSnapshots;
            const index = snapshots.findIndex(s => s.id === snapshot.id);
            if (index < 0) {
                const kernelSnapshots = [...snapshots, snapshot];
                set({ runtimeSnapshots: kernelSnapshots });
            }
            else if (!JSONExt.deepEqual(snapshots[index], snapshot)) {
                const kernelSnapshots = [...snapshots];
                kernelSnapshots.splice(index, 1, snapshot);
                set({ runtimeSnapshots: kernelSnapshots });
            }
        },
        /**
         * Remove a Kernel Snapshot.
         */
        removeRuntimeSnapshot: (id) => {
            const snapshots = get().runtimeSnapshots;
            const index = snapshots.findIndex(s => s.id === id);
            if (index >= 0) {
                const kernelSnapshots = [...snapshots];
                kernelSnapshots.splice(index, 1);
                set({ runtimeSnapshots: kernelSnapshots });
            }
        },
        /**
         * Set Kernel Snapshots.
         */
        setRuntimeSnapshots: (snapshots) => {
            if (!JSONExt.deepEqual(get().runtimeSnapshots, snapshots)) {
                set({ runtimeSnapshots: [...snapshots] });
            }
        },
        version: '',
        setVersion: version => {
            if (version && !get().version) {
                set(state => ({ version }));
            }
        },
    };
});
// Poll remote kernels
const kernelsPoll = new Poll({
    auto: true,
    factory: () => runtimesStore.getState().refreshRuntimePods(),
    frequency: {
        interval: 61 * 1000,
        backoff: true,
        max: 300 * 1000,
    },
    name: '@datalayer/jupyter-kernels:KernelsManager#kernels',
    standby: () => iamStore.getState().token || runtimesStore.getState().runtimesRunUrl
        ? 'when-hidden'
        : true,
});
// Force refresh at expiration date if next tick is after it.
runtimesStore.subscribe((state, prevState) => {
    if (!JSONExt.deepEqual(state.runtimePods, prevState.runtimePods)) {
        const now = Date.now();
        const minExpiredAt = Math.min(...state.runtimePods.map(kernel => kernel.expired_at ? parseFloat(kernel.expired_at) : Infinity)) * 1_000;
        // Refresh 2 sec after the closest expiration time
        // to let some times to the system to dispose the resources.
        if (now + kernelsPoll.frequency.interval > minExpiredAt + 2_000) {
            setTimeout(() => {
                kernelsPoll.refresh();
            }, minExpiredAt + 2_000 - now);
        }
    }
});
coreStore.subscribe((state, prevState) => {
    if (state.configuration.runtimesRunUrl &&
        state.configuration.runtimesRunUrl !==
            prevState.configuration.runtimesRunUrl) {
        const runtimesRunUrl = state.configuration.runtimesRunUrl;
        console.log(`Updating runtimesRunUrl with new value ${runtimesRunUrl}`);
        runtimesStore.setState({ runtimesRunUrl });
        kernelsPoll
            .refresh()
            .then(() => kernelsPoll.tick)
            .catch(reason => {
            console.error('Failed to refresh kernel servers list following service URL changed.', reason);
        });
    }
});
export function useRuntimesStore(selector) {
    return useStore(runtimesStore, selector);
}
export default useRuntimesStore;
