// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { contextBridge, ipcRenderer } from "electron";
import {
    Client,
    ClientAPI,
    ShellUserSettings,
    UserExpression,
} from "./electronTypes.js"; // Custom APIs for renderer
import { Dispatcher } from "agent-dispatcher";
import { createChannelAdapter } from "@typeagent/agent-rpc/channel";
import { createDispatcherRpcClient } from "@typeagent/dispatcher-rpc/dispatcher/client";
import { createClientIORpcServer } from "@typeagent/dispatcher-rpc/clientio/server";

ipcRenderer.on("send-demo-event", (_, name) => {
    // bounce back to the main process
    ipcRenderer.send("send-demo-event", name);
});

let clientRegistered = false;
function registerClient(client: Client) {
    if (clientRegistered) {
        throw new Error("Client already registered");
    }

    // Establish the clientIO RPC
    clientRegistered = true;
    const clientIOChannel = createChannelAdapter((message: any) =>
        ipcRenderer.send("clientio-rpc-reply", message),
    );
    ipcRenderer.on("clientio-rpc-call", (_event, message) => {
        clientIOChannel.notifyMessage(message);
    });
    createClientIORpcServer(client.clientIO, clientIOChannel.channel);

    ipcRenderer.on("listen-event", (_, token, useLocalWhisper) => {
        client.listen(token, useLocalWhisper);
    });
    ipcRenderer.on("listen-always", (_event) => {
        client.toggleAlwaysListen(false);
    });
    ipcRenderer.on("wake-listen", (_event) => {
        client.toggleAlwaysListen(true);
    });
    ipcRenderer.on("setting-summary-changed", (_, updatedAgents) => {
        client.updateRegisterAgents(updatedAgents);
    });
    ipcRenderer.on("updated-title", (_, title) => {
        client.titleUpdated(title);
    });
    ipcRenderer.on("send-input-text", async (_, message) => {
        await client.showInputText(message);
        ipcRenderer.send("send-input-text-complete");
    });
    ipcRenderer.on("demo-state", (_, state) => {
        client.demoStateChanged?.(state);
    });
    ipcRenderer.on("show-dialog", (_, key) => {
        client.showDialog(key);
    });
    ipcRenderer.on("settings-changed", (_, value: ShellUserSettings) => {
        client.updateSettings(value);
    });
    ipcRenderer.on(
        "file-selected",
        (_, fileName: string, fileContent: string) => {
            client.fileSelected(fileName, fileContent);
        },
    );

    ipcRenderer.on("focus-chat-input", () => {
        client.focusInput();
    });

    ipcRenderer.on("search-menu-completion", (_event, id: number, item) => {
        client.searchMenuCompletion(id, item);
    });

    ipcRenderer.on(
        "search-menu-selection-changed",
        (_event, id: number, selected: number) => {
            client.searchMenuSelectionChanged?.(id, selected);
        },
    );

    ipcRenderer.on("dispatcher-initialized", () => {
        // Resolve the dispatcher promise when the dispatcher is initialized)
        // set up dispatch RPC client
        const dispatcherChannel = createChannelAdapter((message: any) =>
            ipcRenderer.send("dispatcher-rpc-call", message),
        );

        ipcRenderer.on("dispatcher-rpc-reply", (_event, message) => {
            dispatcherChannel.notifyMessage(message);
        });

        const dispatcher: Dispatcher = createDispatcherRpcClient(
            dispatcherChannel.channel,
        );

        client.dispatcherInitialized(dispatcher);
    });

    ipcRenderer.on("tab-restore-status", (_, count: number) => {
        client.tabRestoreStatus(count);
    });

    ipcRenderer.on(
        "system-notification",
        (_, data: { message: string; id: string; timestamp: number }) => {
            client.systemNotification?.(data.message, data.id, data.timestamp);
        },
    );

    ipcRenderer.on(
        "conversation-changed",
        (_, conversationId: string, name: string) => {
            client.conversationChanged?.(conversationId, name);
        },
    );

    ipcRenderer.on("mark-history", () => {
        client.markHistoryEntries?.();
    });

    // Signal the main process that the client has been registered
    ipcRenderer.send("chat-view-ready");

    ipcRenderer.on(
        "continuous-speech-processed",
        (_event, userExpressions: UserExpression[]) => {
            if (userExpressions !== undefined) {
                client.continuousSpeechProcessed(userExpressions);
            }
        },
    );
}

const api: ClientAPI = {
    registerClient,
    getSpeechToken: (silent: boolean) => {
        return ipcRenderer.invoke("get-speech-token", silent);
    },
    getLocalWhisperStatus: () => {
        return ipcRenderer.invoke("get-localWhisper-status");
    },
    openImageFile: () => {
        ipcRenderer.send("open-image-file");
    },
    getChatHistory: () => {
        return ipcRenderer.invoke("get-chat-history");
    },
    saveChatHistory: (html: string, seq: number) => {
        ipcRenderer.send("save-chat-history", html, seq);
    },
    saveSettings: (settings: ShellUserSettings) => {
        ipcRenderer.send("save-settings", settings);
    },
    openFolder: (path: string) => {
        ipcRenderer.send("open-folder", path);
    },
    openUrlInBrowserTab: (url: string) => {
        ipcRenderer.send("open-url-in-browser-tab", url);
    },
    openUrlExternal: (url: string) => {
        ipcRenderer.send("open-url-external", url);
    },

    searchMenuUpdate: (id: number, data) => {
        ipcRenderer.send("search-menu-update", id, data);
    },
    searchMenuAdjustSelection: (id: number, deltaY: number) => {
        ipcRenderer.send("search-menu-adjust-selection", id, deltaY);
    },
    searchMenuScroll: (id: number, deltaY: number) => {
        ipcRenderer.send("search-menu-scroll", id, deltaY);
    },
    searchMenuSelectCompletion: (id: number) => {
        ipcRenderer.send("search-menu-select-completion", id);
    },
    searchMenuClose: (id: number) => {
        ipcRenderer.send("search-menu-close", id);
    },
    continuousSpeechProcessing: (text: string) => {
        return ipcRenderer.invoke("continuous-speech-processing", text);
    },

    // Conversation management
    conversationList: () => {
        return ipcRenderer.invoke("conversation-list");
    },
    conversationCreate: (name: string) => {
        return ipcRenderer.invoke("conversation-create", name);
    },
    conversationSwitch: (conversationId: string) => {
        return ipcRenderer.invoke("conversation-switch", conversationId);
    },
    conversationRename: (conversationId: string, newName: string) => {
        return ipcRenderer.invoke(
            "conversation-rename",
            conversationId,
            newName,
        );
    },
    conversationDelete: (conversationId: string) => {
        return ipcRenderer.invoke("conversation-delete", conversationId);
    },
    conversationGetCurrent: () => {
        return ipcRenderer.invoke("conversation-get-current");
    },
};

// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
    try {
        contextBridge.exposeInMainWorld("api", api);
    } catch (error) {
        console.error(error);
    }
} else {
    // @ts-ignore (define in dts)
    window.api = api;
}
