/**
 * Worker entry point — runs the entire mailx service layer off the main thread.
 *
 * Hosts: wa-sqlite DB, sync manager, providers, service, settings.
 * Main thread only handles UI rendering and proxies TCP/native bridge calls.
 */
import { initWorkerTcp, handleTcpMessage, WorkerTcpTransport } from "./worker-tcp-transport.js";
import { WebMailxDB } from "./db.js";
import { WebMessageStore } from "./web-message-store.js";
import { WebMailxService } from "./web-service.js";
import { SyncManager } from "./sync-manager.js";
import { dispatch } from "./web-jsonrpc.js";
import { loadAccounts, loadAccountsFromCloud, setGDriveTokenProvider, setGDriveFolderId } from "./web-settings.js";
// ── Globals ──
let db;
let bodyStore;
let syncManager;
let service;
const post = (msg) => self.postMessage(msg);
// ── Event emission → main thread ──
function emitEvent(event) {
    post({ type: "event", event });
}
function vlog(msg) {
    // Route through main thread for logit (fetch works in workers but keep consistent)
    try {
        fetch(`https://rmf39.aaz.lt/logit/${encodeURIComponent("V/" + msg.substring(0, 800))}?log=mailx-android&silent=true`).catch(() => { });
    }
    catch { /* ignore */ }
}
// ── Native bridge proxy ──
let nativeReqCounter = 0;
const pendingNative = new Map();
function nativeRequest(op, args) {
    const reqId = ++nativeReqCounter;
    return new Promise((resolve, reject) => {
        pendingNative.set(reqId, { resolve, reject });
        post({ type: "native", reqId, op, args });
        setTimeout(() => {
            if (pendingNative.has(reqId)) {
                pendingNative.delete(reqId);
                reject(new Error(`Native ${op} timeout`));
            }
        }, 60000);
    });
}
function handleNativeResponse(msg) {
    const pending = pendingNative.get(msg.reqId);
    if (pending) {
        pendingNative.delete(msg.reqId);
        if (msg.error)
            pending.reject(new Error(msg.error));
        else
            pending.resolve(msg.result);
    }
}
// ── Token provider (proxied through main thread → C# OAuth) ──
function createTokenProvider(email) {
    return async () => {
        console.log(`[oauth] Refreshing token for ${email}`);
        return nativeRequest("refreshToken", [email]);
    };
}
// ── GDrive folder lookup ──
async function findGDriveMailxFolder(tokenProvider) {
    const token = await tokenProvider();
    const headers = { "Authorization": `Bearer ${token}` };
    // Two-step path traversal: My Drive root → home/ → .rmfmail
    const homeQ = encodeURIComponent("name='home' and mimeType='application/vnd.google-apps.folder' and 'root' in parents and trashed=false");
    const homeRes = await fetch(`https://www.googleapis.com/drive/v3/files?q=${homeQ}&fields=files(id,name)&spaces=drive`, { headers });
    if (!homeRes.ok)
        return null;
    const home = (await homeRes.json()).files?.[0];
    if (!home?.id)
        return null;
    const q = encodeURIComponent(`name='.rmfmail' and mimeType='application/vnd.google-apps.folder' and '${home.id}' in parents and trashed=false`);
    const r = await fetch(`https://www.googleapis.com/drive/v3/files?q=${q}&fields=files(id,name,owners(emailAddress))&spaces=drive`, { headers });
    if (!r.ok)
        return null;
    const data = await r.json();
    const folder = data.files?.[0];
    if (!folder?.id)
        return null;
    const homeName = home.name || "home";
    const folderName = folder.name || ".rmfmail";
    return {
        id: folder.id,
        name: folderName,
        path: `My Drive/${homeName}/${folderName}`,
        ownerEmail: folder.owners?.[0]?.emailAddress,
    };
}
// ── Message handler ──
self.onmessage = async (e) => {
    const msg = e.data;
    if (msg.type === "tcp-response" || msg.type === "tcp-data" ||
        msg.type === "tcp-close" || msg.type === "tcp-error") {
        handleTcpMessage(msg);
        return;
    }
    if (msg.type === "native-response") {
        handleNativeResponse(msg);
        return;
    }
    if (msg.type === "rpc") {
        try {
            const result = await dispatch(service, {
                _action: msg.action,
                _cbid: String(msg.id),
                ...msg.params
            });
            post({ type: "rpc-response", id: msg.id, result: result.result, error: result.error });
        }
        catch (e) {
            post({ type: "rpc-response", id: msg.id, error: e.message });
        }
        return;
    }
    if (msg.type === "init") {
        try {
            await initialize();
            post({ type: "init-complete" });
        }
        catch (e) {
            console.error(`[worker] Init failed: ${e.message}\n${e.stack}`);
            post({ type: "init-error", error: e.message });
        }
        return;
    }
};
// ── Initialization ──
async function initialize() {
    console.log("[worker] Initializing service layer...");
    // Wire up TCP proxy for IMAP/SMTP
    initWorkerTcp(post);
    // Initialize DB (wa-sqlite runs in this Worker thread — no main thread blocking)
    db = new WebMailxDB("mailx");
    await db.waitReady();
    bodyStore = new WebMessageStore();
    // Create sync manager with injected deps
    syncManager = new SyncManager(db, bodyStore, {
        emitEvent,
        vlog,
        createTcpTransport: () => new WorkerTcpTransport(),
    });
    service = new WebMailxService(db, bodyStore, syncManager);
    // Load accounts
    let accounts = await loadAccounts();
    console.log(`[worker] ${accounts.length} account(s) found`);
    let gmailTokenProvider = null;
    for (const account of accounts) {
        if (!account.enabled)
            continue;
        const domain = account.email?.split("@")[1]?.toLowerCase() || "";
        if (domain === "gmail.com" || domain === "googlemail.com") {
            const tp = createTokenProvider(account.email);
            syncManager.setTokenProvider(account.id, tp);
            if (!gmailTokenProvider)
                gmailTokenProvider = tp;
        }
        await syncManager.addAccount(account);
    }
    // GDrive setup — read shared accounts from cloud
    if (gmailTokenProvider) {
        setGDriveTokenProvider(gmailTokenProvider);
        try {
            console.log("[worker] Looking up GDrive .rmfmail folder...");
            const folder = await findGDriveMailxFolder(gmailTokenProvider);
            if (!folder) {
                emitEvent({
                    type: "fatal",
                    key: "gdrive-folder-missing",
                    message: "GDrive folder '.rmfmail' not found — app cannot start. Create it or sign in with the correct Google account.",
                });
            }
            else {
                setGDriveFolderId(folder.id, folder.name, folder.ownerEmail, folder.path);
                console.log(`[worker] GDrive ${folder.path} folder: ${folder.id} (owner=${folder.ownerEmail || "?"})`);
                // Load accounts from cloud
                const cloudAccounts = await loadAccountsFromCloud();
                if (cloudAccounts && cloudAccounts.length > 0) {
                    console.log(`[worker] GDrive returned ${cloudAccounts.length} accounts`);
                    for (const account of cloudAccounts) {
                        if (!account.enabled)
                            continue;
                        const domain = account.email?.split("@")[1]?.toLowerCase() || "";
                        if (domain === "gmail.com" || domain === "googlemail.com") {
                            const tp = createTokenProvider(account.email);
                            syncManager.setTokenProvider(account.id, tp);
                            if (!gmailTokenProvider)
                                gmailTokenProvider = tp;
                        }
                        await syncManager.addAccount(account);
                    }
                }
            }
        }
        catch (e) {
            emitEvent({
                type: "fatal",
                key: "gdrive-setup-failed",
                message: `GDrive setup failed — app cannot start: ${e.message}`,
            });
        }
    }
    // Periodic sync (runs in Worker — never blocks UI)
    setInterval(() => {
        console.log("[sync] periodic poll");
        syncManager.syncAll().catch((e) => console.error(`[worker] Periodic sync error: ${e.message}`));
    }, 2 * 60 * 1000);
    // Initial sync after 1 second
    setTimeout(() => {
        syncManager.syncAll().catch((e) => console.error(`[worker] Initial sync error: ${e.message}`));
    }, 1000);
    console.log("[worker] Service layer ready");
    emitEvent({ type: "connected" });
}
//# sourceMappingURL=worker-entry.js.map