// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
function mockInternals() {
    var _a, _b;
    window.__TAURI_INTERNALS__ = (_a = window.__TAURI_INTERNALS__) !== null && _a !== void 0 ? _a : {};
    window.__TAURI_EVENT_PLUGIN_INTERNALS__ =
        (_b = window.__TAURI_EVENT_PLUGIN_INTERNALS__) !== null && _b !== void 0 ? _b : {};
}
/**
 * Intercepts all IPC requests with the given mock handler.
 *
 * This function can be used when testing tauri frontend applications or when running the frontend in a Node.js context during static site generation.
 *
 * # Examples
 *
 * Testing setup using Vitest:
 * ```ts
 * import { mockIPC, clearMocks } from "@tauri-apps/api/mocks"
 * import { invoke } from "@tauri-apps/api/core"
 *
 * afterEach(() => {
 *    clearMocks()
 * })
 *
 * test("mocked command", () => {
 *  mockIPC((cmd, payload) => {
 *   switch (cmd) {
 *     case "add":
 *       return (payload.a as number) + (payload.b as number);
 *     default:
 *       break;
 *     }
 *  });
 *
 *  expect(invoke('add', { a: 12, b: 15 })).resolves.toBe(27);
 * })
 * ```
 *
 * The callback function can also return a Promise:
 * ```js
 * import { mockIPC, clearMocks } from "@tauri-apps/api/mocks"
 * import { invoke } from "@tauri-apps/api/core"
 *
 * afterEach(() => {
 *    clearMocks()
 * })
 *
 * test("mocked command", () => {
 *  mockIPC((cmd, payload) => {
 *   if(cmd === "get_data") {
 *    return fetch("https://example.com/data.json")
 *      .then((response) => response.json())
 *   }
 *  });
 *
 *  expect(invoke('get_data')).resolves.toBe({ foo: 'bar' });
 * })
 * ```
 *
 * `listen` can also be mocked with direct calls to the `emit` function. This functionality is opt-in via the `shouldMockEvents` option:
 * ```js
 * import { mockIPC, clearMocks } from "@tauri-apps/api/mocks"
 * import { emit, listen } from "@tauri-apps/api/event"
 *
 * afterEach(() => {
 *    clearMocks()
 * })
 *
 * test("mocked event", () => {
 *  mockIPC(() => {}, { shouldMockEvents: true }); // enable event mocking
 *
 *  const eventHandler = vi.fn();
 *  listen('test-event', eventHandler); // typically in component setup or similar
 *
 *  emit('test-event', { foo: 'bar' });
 *  expect(eventHandler).toHaveBeenCalledWith({
 *    event: 'test-event',
 *    payload: { foo: 'bar' }
 *  });
 * })
 * ```
 * `emitTo` is currently **not** supported by this mock implementation.
 *
 * @since 1.0.0
 */
function mockIPC(cb, options) {
    mockInternals();
    function isEventPluginInvoke(cmd) {
        return cmd.startsWith('plugin:event|');
    }
    function handleEventPlugin(cmd, args) {
        switch (cmd) {
            case 'plugin:event|listen':
                return handleListen(args);
            case 'plugin:event|emit':
                return handleEmit(args);
            case 'plugin:event|unlisten':
                return handleRemoveListener(args);
        }
    }
    const listeners = new Map();
    function handleListen(args) {
        if (!listeners.has(args.event)) {
            listeners.set(args.event, []);
        }
        listeners.get(args.event).push(args.handler);
        return args.handler;
    }
    function handleEmit(args) {
        const eventListeners = listeners.get(args.event) || [];
        for (const handler of eventListeners) {
            runCallback(handler, args);
        }
        return null;
    }
    function handleRemoveListener(args) {
        const eventListeners = listeners.get(args.event);
        if (eventListeners) {
            const index = eventListeners.indexOf(args.id);
            if (index !== -1) {
                eventListeners.splice(index, 1);
            }
        }
    }
    // eslint-disable-next-line @typescript-eslint/require-await
    async function invoke(cmd, args, _options) {
        if ((options === null || options === void 0 ? void 0 : options.shouldMockEvents) && isEventPluginInvoke(cmd)) {
            return handleEventPlugin(cmd, args);
        }
        return cb(cmd, args);
    }
    const callbacks = new Map();
    function registerCallback(callback, once = false) {
        const identifier = window.crypto.getRandomValues(new Uint32Array(1))[0];
        callbacks.set(identifier, (data) => {
            if (once) {
                unregisterCallback(identifier);
            }
            return callback && callback(data);
        });
        return identifier;
    }
    function unregisterCallback(id) {
        callbacks.delete(id);
    }
    function runCallback(id, data) {
        const callback = callbacks.get(id);
        if (callback) {
            callback(data);
        }
        else {
            // eslint-disable-next-line no-console
            console.warn(`[TAURI] Couldn't find callback id ${id}. This might happen when the app is reloaded while Rust is running an asynchronous operation.`);
        }
    }
    function unregisterListener(event, id) {
        unregisterCallback(id);
    }
    window.__TAURI_INTERNALS__.invoke = invoke;
    window.__TAURI_INTERNALS__.transformCallback = registerCallback;
    window.__TAURI_INTERNALS__.unregisterCallback = unregisterCallback;
    window.__TAURI_INTERNALS__.runCallback = runCallback;
    window.__TAURI_INTERNALS__.callbacks = callbacks;
    window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener =
        unregisterListener;
}
/**
 * Mocks one or many window labels.
 * In non-tauri context it is required to call this function *before* using the `@tauri-apps/api/window` module.
 *
 * This function only mocks the *presence* of windows,
 * window properties (e.g. width and height) can be mocked like regular IPC calls using the `mockIPC` function.
 *
 * # Examples
 *
 * ```js
 * import { mockWindows } from "@tauri-apps/api/mocks";
 * import { getCurrentWindow } from "@tauri-apps/api/window";
 *
 * mockWindows("main", "second", "third");
 *
 * const win = getCurrentWindow();
 *
 * win.label // "main"
 * ```
 *
 * ```js
 * import { mockWindows } from "@tauri-apps/api/mocks";
 *
 * mockWindows("main", "second", "third");
 *
 * mockIPC((cmd, args) => {
 *  if (cmd === "plugin:event|emit") {
 *    console.log('emit event', args?.event, args?.payload);
 *  }
 * });
 *
 * const { emit } = await import("@tauri-apps/api/event");
 * await emit('loaded'); // this will cause the mocked IPC handler to log to the console.
 * ```
 *
 * @param current Label of window this JavaScript context is running in.
 *
 * @since 1.0.0
 */
function mockWindows(current, ..._additionalWindows) {
    mockInternals();
    window.__TAURI_INTERNALS__.metadata = {
        currentWindow: { label: current },
        currentWebview: { windowLabel: current, label: current }
    };
}
/**
 * Mock `convertFileSrc` function
 *
 *
 * @example
 * ```js
 * import { mockConvertFileSrc } from "@tauri-apps/api/mocks";
 * import { convertFileSrc } from "@tauri-apps/api/core";
 *
 * mockConvertFileSrc("windows")
 *
 * const url = convertFileSrc("C:\\Users\\user\\file.txt")
 * ```
 *
 * @param osName The operating system to mock, can be one of linux, macos, or windows
 *
 * @since 1.6.0
 */
function mockConvertFileSrc(osName) {
    mockInternals();
    window.__TAURI_INTERNALS__.convertFileSrc = function (filePath, protocol = 'asset') {
        const path = encodeURIComponent(filePath);
        return osName === 'windows'
            ? `http://${protocol}.localhost/${path}`
            : `${protocol}://localhost/${path}`;
    };
}
/**
 * Clears mocked functions/data injected by the other functions in this module.
 * When using a test runner that doesn't provide a fresh window object for each test, calling this function will reset tauri specific properties.
 *
 * # Example
 *
 * ```js
 * import { mockWindows, clearMocks } from "@tauri-apps/api/mocks"
 *
 * afterEach(() => {
 *    clearMocks()
 * })
 *
 * test("mocked windows", () => {
 *    mockWindows("main", "second", "third");
 *
 *    expect(window.__TAURI_INTERNALS__).toHaveProperty("metadata")
 * })
 *
 * test("no mocked windows", () => {
 *    expect(window.__TAURI_INTERNALS__).not.toHaveProperty("metadata")
 * })
 * ```
 *
 * @since 1.0.0
 */
function clearMocks() {
    if (typeof window.__TAURI_INTERNALS__ !== 'object') {
        return;
    }
    // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case
    delete window.__TAURI_INTERNALS__.invoke;
    // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case
    delete window.__TAURI_INTERNALS__.transformCallback;
    // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case
    delete window.__TAURI_INTERNALS__.unregisterCallback;
    // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case
    delete window.__TAURI_INTERNALS__.runCallback;
    // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case
    delete window.__TAURI_INTERNALS__.callbacks;
    // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case
    delete window.__TAURI_INTERNALS__.convertFileSrc;
    // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case
    delete window.__TAURI_INTERNALS__.metadata;
    if (typeof window.__TAURI_EVENT_PLUGIN_INTERNALS__ !== 'object') {
        return;
    }
    // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case
    delete window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener;
}

export { clearMocks, mockConvertFileSrc, mockIPC, mockWindows };
