/**
 * mailxapi — IPC bridge injected into WebView by the Rust launcher.
 * Follows the msgapi pattern: callNode → Promise → _msgapiServiceResolve/Reject.
 *
 * When running in a browser (no IPC), this file is not loaded.
 * api-client.ts auto-detects and falls back to HTTP fetch.
 */

(function() {
    "use strict";

    var _callbacks = {};
    var _callbackId = 0;
    var _eventHandlers = [];
    function callNode(action, params) {
        var id = String(++_callbackId);
        return new Promise(function(resolve, reject) {
            var timer = setTimeout(function() {
                delete _callbacks[id];
                reject(new Error("mailxapi timeout: " + action));
            }, 120000);
            _callbacks[id] = { resolve: resolve, reject: reject, timer: timer };
            var msg = Object.assign({ _action: action, _cbid: id }, params || {});
            if (window.ipc && window.ipc.postMessage) {
                window.ipc.postMessage(JSON.stringify(msg));
            } else {
                clearTimeout(timer);
                delete _callbacks[id];
                reject(new Error("No IPC channel available"));
            }
        });
    }

    // Called by host (msger Rust / msgview preload) to resolve service-channel promises
    window._msgapiServiceResolve = function(id, value) {
        var cb = _callbacks[id];
        if (!cb) return;
        delete _callbacks[id];
        clearTimeout(cb.timer);
        cb.resolve(value);
    };

    // Called by host to reject service-channel promises
    window._msgapiServiceReject = function(id, error) {
        var cb = _callbacks[id];
        if (!cb) return;
        delete _callbacks[id];
        clearTimeout(cb.timer);
        cb.reject(new Error(error));
    };

    // Called by host to push events (new mail, sync progress, etc.)
    window._msgapiServiceEvent = function(event) {
        for (var i = 0; i < _eventHandlers.length; i++) {
            try { _eventHandlers[i](event); } catch(e) { /* ignore */ }
        }
    };

    window.mailxapi = {
        isApp: true,
        platform: "webview2",

        // Accounts
        getAccounts: function() { return callNode("getAccounts"); },
        getFolders: function(accountId) { return callNode("getFolders", { accountId: accountId }); },

        // Messages
        getMessages: function(accountId, folderId, page, pageSize, sort, sortDir, search, flaggedOnly) {
            return callNode("getMessages", { accountId: accountId, folderId: folderId, page: page, pageSize: pageSize, sort: sort, sortDir: sortDir, search: search, flaggedOnly: flaggedOnly });
        },
        getUnifiedInbox: function(page, pageSize) {
            return callNode("getUnifiedInbox", { page: page, pageSize: pageSize });
        },
        getMessage: function(accountId, uid, allowRemote, folderId) {
            return callNode("getMessage", { accountId: accountId, uid: uid, allowRemote: allowRemote, folderId: folderId });
        },

        // Actions
        updateFlags: function(accountId, uid, flags) {
            return callNode("updateFlags", { accountId: accountId, uid: uid, flags: flags });
        },
        deleteMessage: function(accountId, uid) {
            return callNode("deleteMessage", { accountId: accountId, uid: uid });
        },
        undeleteMessage: function(accountId, uid, folderId) {
            return callNode("undeleteMessage", { accountId: accountId, uid: uid, folderId: folderId });
        },

        // Diagnostic tracing — callers (compose iframe, app, components) can
        // ship arbitrary named events to the Node log. Surfaces as
        // `[client] <tag> <data>` so a failing pipeline can be traced end
        // to end in a single log file.
        logClientEvent: function(tag, data) { return callNode("logClientEvent", { tag: tag, data: data }); },

        // Compose
        sendMessage: function(msg) { return callNode("sendMessage", msg); },
        saveDraft: function(params) { return callNode("saveDraft", params); },
        deleteDraft: function(accountId, draftUid, draftId) {
            return callNode("deleteDraft", { accountId: accountId, draftUid: draftUid, draftId: draftId });
        },

        // Search
        searchMessages: function(query, page, pageSize) {
            return callNode("searchMessages", { query: query, page: page, pageSize: pageSize });
        },
        addContact: function(name, email) {
            return callNode("addContact", { name: name, email: email });
        },
        getThreadMessages: function(accountId, threadId) {
            return callNode("getThreadMessages", { accountId: accountId, threadId: threadId });
        },
        // Calendar / Tasks two-way cache. Reads return local DB immediately;
        // writes commit locally and queue a push-to-Google action.
        getCalendarEvents: function(fromMs, toMs) {
            return callNode("getCalendarEvents", { fromMs: fromMs, toMs: toMs });
        },
        createCalendarEvent: function(ev) { return callNode("createCalendarEvent", ev); },
        updateCalendarEvent: function(uuid, patch) { return callNode("updateCalendarEvent", { uuid: uuid, patch: patch }); },
        deleteCalendarEvent: function(uuid) { return callNode("deleteCalendarEvent", { uuid: uuid }); },
        getTasks: function(includeCompleted) { return callNode("getTasks", { includeCompleted: !!includeCompleted }); },
        createTask: function(t) { return callNode("createTask", t); },
        updateTask: function(uuid, patch) { return callNode("updateTask", { uuid: uuid, patch: patch }); },
        deleteTask: function(uuid) { return callNode("deleteTask", { uuid: uuid }); },
        drainStoreSync: function() { return callNode("drainStoreSync"); },
        recordSpamReport: function(accountId, uid, folderId) {
            return callNode("recordSpamReport", { accountId: accountId, uid: uid, folderId: folderId });
        },
        readJsoncFile: function(name) {
            return callNode("readJsoncFile", { name: name });
        },
        writeJsoncFile: function(name, content) {
            return callNode("writeJsoncFile", { name: name, content: content });
        },
        formatJsonc: function(content) {
            return callNode("formatJsonc", { content: content });
        },
        readConfigHelp: function(name) {
            return callNode("readConfigHelp", { name: name });
        },
        unsubscribeOneClick: function(url) {
            return callNode("unsubscribeOneClick", { url: url });
        },
        openInWord: function(editId, html) {
            return callNode("openInWord", { editId: editId, html: html });
        },
        closeWordEdit: function(editId) {
            return callNode("closeWordEdit", { editId: editId });
        },
        aiTransform: function(req) {
            return callNode("aiTransform", req);
        },
        // Restart the background service so edits to accounts.jsonc /
        // allowlist.jsonc / other live config take effect without the user
        // having to type `mailx -kill`. The service spawns a fresh detached
        // daemon and then exits; the new daemon takes over.
        restartDaemon: function() {
            return new Promise(function(resolve) {
                var id = String(++_callbackId);
                _callbacks[id] = { resolve: resolve, reject: resolve, timer: setTimeout(function() {
                    delete _callbacks[id];
                    resolve({ ok: true, status: "service-gone" });
                }, 5000) };
                try { window.ipc.postMessage(JSON.stringify({ _action: "restartDaemon", _cbid: id })); } catch(e) { resolve({ ok: false }); }
            });
        },
        searchContacts: function(query) {
            return callNode("searchContacts", { query: query });
        },
        listContacts: function(query, page, pageSize) {
            return callNode("listContacts", { query: query || "", page: page || 1, pageSize: pageSize || 100 });
        },
        upsertContact: function(name, email) {
            return callNode("upsertContact", { name: name, email: email });
        },
        deleteContact: function(email) {
            return callNode("deleteContact", { email: email });
        },
        addPreferredContact: function(name, email, source, organization) {
            return callNode("addPreferredContact", {
                name: name || "", email: email,
                source: source || "", organization: organization || "",
            });
        },
        addToDenylist: function(email) {
            return callNode("addToDenylist", { email: email });
        },
        openLocalPath: function(which) {
            return callNode("openLocalPath", { which: which });
        },

        // Item 17: platform-aware external openers. On Android (MAUI shell)
        // these fire native Intents so Contacts/Calendar links jump to the
        // OS apps instead of the browser. On desktop the MAUI shell isn't
        // in play; we fall through to a URL open via the navigation path
        // that msger / external launcher handles the same as any http link.
        openContact: function(email) {
            try {
                // MAUI intercepts this scheme in OnNavigating and fires a
                // native ACTION_VIEW intent on the mailto: URI — system
                // Contacts registers for it on stock Android.
                if (/Android/i.test(navigator.userAgent)) {
                    window.location.href = "mailxapi-intent://contact/" + encodeURIComponent(email);
                    return;
                }
            } catch (e) { /* navigation blocked — fall through */ }
            // Desktop / browser fallback: open mailto: — msger forwards
            // mailto URLs to the system default handler, which on a mailx
            // install is mailx itself (our own compose window takes over).
            window.location.href = "mailto:" + email;
        },
        openCalendarEvent: function(htmlLink) {
            if (!htmlLink) return;
            try {
                if (/Android/i.test(navigator.userAgent)) {
                    // MAUI dispatches this to the native Calendar app via
                    // ACTION_VIEW on the Google Calendar event URL.
                    window.location.href = "mailxapi-intent://calendar/" + encodeURIComponent(htmlLink);
                    return;
                }
            } catch (e) { /* */ }
            // Desktop: existing openExternal path (browser, new tab).
            window.open(htmlLink, "_blank");
        },

        // Sync
        syncAll: function() { return callNode("syncAll"); },
        syncAccount: function(accountId) { return callNode("syncAccount", { accountId: accountId }); },
        getSyncPending: function() { return callNode("getSyncPending"); },
        getOutboxStatus: function() { return callNode("getOutboxStatus"); },
        getDiagnostics: function() { return callNode("getDiagnostics"); },
        getPrimaryAccount: function(feature) { return callNode("getPrimaryAccount", { feature: feature }); },
        listQueuedOutgoing: function() { return callNode("listQueuedOutgoing"); },
        cancelQueuedOutgoing: function(p) { return callNode("cancelQueuedOutgoing", { path: p }); },
        reauthenticate: function(accountId) { return callNode("reauthenticate", { accountId: accountId }); },
        reauthGoogleScopes: function() { return callNode("reauthGoogleScopes"); },
        hasCcHistoryTo: function(email) { return callNode("hasCcHistoryTo", { email: email }); },

        // Bulk operations
        deleteMessages: function(accountId, uids) {
            return callNode("deleteMessages", { accountId: accountId, uids: uids });
        },
        moveMessage: function(accountId, uid, targetFolderId, targetAccountId) {
            return callNode("moveMessage", { accountId: accountId, uid: uid, targetFolderId: targetFolderId, targetAccountId: targetAccountId });
        },
        moveMessages: function(accountId, uids, targetFolderId) {
            return callNode("moveMessages", { accountId: accountId, uids: uids, targetFolderId: targetFolderId });
        },
        markAsSpamMessages: function(accountId, uids) {
            return callNode("markAsSpamMessages", { accountId: accountId, uids: uids });
        },
        performUpdate: function() {
            return callNode("performUpdate", {});
        },
        markFolderRead: function(accountId, folderId) {
            return callNode("markFolderRead", { folderId: folderId });
        },
        createFolder: function(accountId, parentPath, name) {
            return callNode("createFolder", { accountId: accountId, parentPath: parentPath, name: name });
        },
        renameFolder: function(accountId, folderId, newName) {
            return callNode("renameFolder", { accountId: accountId, folderId: folderId, newName: newName });
        },
        deleteFolder: function(accountId, folderId) {
            return callNode("deleteFolder", { accountId: accountId, folderId: folderId });
        },
        emptyFolder: function(accountId, folderId) {
            return callNode("emptyFolder", { accountId: accountId, folderId: folderId });
        },

        // Settings
        allowRemoteContent: function(type, value) {
            return callNode("allowRemoteContent", { type: type, value: value });
        },
        flagSenderOrDomain: function(type, value) {
            return callNode("flagSenderOrDomain", { type: type, value: value });
        },
        getSettings: function() { return callNode("getSettings"); },
        saveSettingsData: function(data) { return callNode("saveSettingsData", data); },
        getVersion: function() { return callNode("getVersion"); },
        getAutocompleteSettings: function() { return callNode("getAutocompleteSettings"); },
        saveAutocompleteSettings: function(settings) { return callNode("saveAutocompleteSettings", settings); },

        // Attachments
        getAttachment: function(accountId, uid, attachmentId, folderId) {
            return callNode("getAttachment", { accountId: accountId, uid: uid, attachmentId: attachmentId, folderId: folderId });
        },

        // Setup & Repair
        setupAccount: function(name, email, password) {
            return callNode("setupAccount", { name: name, email: email, password: password });
        },
        repairAccounts: function() { return callNode("repairAccounts"); },

        // Window geometry
        saveWindowGeometry: function(geom) {
            return callNode("saveWindowGeometry", geom);
        },

        // Events
        onEvent: function(handler) { _eventHandlers.push(handler); },
    };

    window.dispatchEvent(new CustomEvent("mailxapiready"));
})();
