/**
 * Browser-compatible settings for Android/WebView.
 * Replaces @bobfrankston/mailx-settings which depends on node:fs.
 *
 * Settings are stored in IndexedDB and synced to Google Drive
 * via the GDrive API (same API as desktop cloud mode).
 *
 * On first run, settings are fetched from GDrive. Subsequent reads
 * use the local IndexedDB cache. Writes go to both.
 */
const IDB_NAME = "mailx-settings";
const IDB_VERSION = 1;
const STORE_NAME = "files";
// ── IndexedDB helpers ──
function openSettingsDb() {
    return new Promise((resolve, reject) => {
        const req = indexedDB.open(IDB_NAME, IDB_VERSION);
        req.onupgradeneeded = () => {
            const db = req.result;
            if (!db.objectStoreNames.contains(STORE_NAME)) {
                db.createObjectStore(STORE_NAME);
            }
        };
        req.onsuccess = () => resolve(req.result);
        req.onerror = () => reject(req.error);
    });
}
async function idbRead(key) {
    const db = await openSettingsDb();
    return new Promise((resolve, reject) => {
        const tx = db.transaction(STORE_NAME, "readonly");
        const req = tx.objectStore(STORE_NAME).get(key);
        req.onsuccess = () => resolve(req.result);
        req.onerror = () => reject(req.error);
    });
}
async function idbWrite(key, value) {
    const db = await openSettingsDb();
    return new Promise((resolve, reject) => {
        const tx = db.transaction(STORE_NAME, "readwrite");
        tx.objectStore(STORE_NAME).put(value, key);
        tx.oncomplete = () => resolve();
        tx.onerror = () => reject(tx.error);
    });
}
async function idbDelete(key) {
    const db = await openSettingsDb();
    return new Promise((resolve, reject) => {
        const tx = db.transaction(STORE_NAME, "readwrite");
        tx.objectStore(STORE_NAME).delete(key);
        tx.oncomplete = () => resolve();
        tx.onerror = () => reject(tx.error);
    });
}
// ── GDrive API ──
/** GDrive folder ID for the ".rmfmail" app folder */
let gDriveFolderId = null;
/** GDrive folder name (resolved from query — ".rmfmail" today, but kept dynamic in case the query changes) */
let gDriveFolderName = null;
/** Full path "My Drive/home/.rmfmail" — surfaced in About so the user can verify */
let gDriveFolderPath = null;
/** Owner email of the matched .rmfmail folder — surfaces wrong-account picks (e.g. spouse's shared folder) */
let gDriveFolderOwner = null;
/** OAuth token provider — set by bootstrap */
let tokenProvider = null;
export function setGDriveTokenProvider(provider) {
    tokenProvider = provider;
}
export function setGDriveFolderId(folderId, folderName, ownerEmail, folderPath) {
    gDriveFolderId = folderId;
    if (folderName)
        gDriveFolderName = folderName;
    if (ownerEmail)
        gDriveFolderOwner = ownerEmail;
    if (folderPath)
        gDriveFolderPath = folderPath;
}
export async function cloudRead(filename) {
    return gDriveRead(filename);
}
export async function cloudWrite(filename, content) {
    return gDriveWrite(filename, content);
}
/** Cheap freshness probe: one Drive metadata request (no content download).
 *  Returns the file's modifiedTime, or null if unreachable / missing. */
export async function cloudStat(filename) {
    if (!tokenProvider || !gDriveFolderId)
        return null;
    try {
        const token = await tokenProvider();
        const q = encodeURIComponent(`name='${filename}' and '${gDriveFolderId}' in parents and trashed=false`);
        const res = await globalThis.fetch(`https://www.googleapis.com/drive/v3/files?q=${q}&fields=files(id,modifiedTime)`, { headers: { "Authorization": `Bearer ${token}` } });
        if (!res.ok)
            return null;
        const data = await res.json();
        const f = data.files?.[0];
        return f?.id ? { id: f.id, modifiedTime: f.modifiedTime || "" } : null;
    }
    catch (e) {
        console.error(`[settings] GDrive stat ${filename}: ${e.message}`);
        return null;
    }
}
async function gDriveRead(filename) {
    if (!tokenProvider || !gDriveFolderId)
        return null;
    try {
        const token = await tokenProvider();
        // Find file by name in folder
        const q = encodeURIComponent(`name='${filename}' and '${gDriveFolderId}' in parents and trashed=false`);
        const listRes = await globalThis.fetch(`https://www.googleapis.com/drive/v3/files?q=${q}&fields=files(id)`, { headers: { "Authorization": `Bearer ${token}` } });
        if (!listRes.ok)
            return null;
        const listData = await listRes.json();
        const fileId = listData.files?.[0]?.id;
        if (!fileId)
            return null;
        // Download content
        const res = await globalThis.fetch(`https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`, { headers: { "Authorization": `Bearer ${token}` } });
        if (!res.ok)
            return null;
        return res.text();
    }
    catch (e) {
        console.error(`[settings] GDrive read ${filename}: ${e.message}`);
        return null;
    }
}
async function gDriveWrite(filename, content) {
    if (!tokenProvider || !gDriveFolderId)
        return false;
    try {
        const token = await tokenProvider();
        // Check if file exists
        const q = encodeURIComponent(`name='${filename}' and '${gDriveFolderId}' in parents and trashed=false`);
        const listRes = await globalThis.fetch(`https://www.googleapis.com/drive/v3/files?q=${q}&fields=files(id)`, { headers: { "Authorization": `Bearer ${token}` } });
        if (!listRes.ok)
            return false;
        const listData = await listRes.json();
        const fileId = listData.files?.[0]?.id;
        if (fileId) {
            // Update existing
            const res = await globalThis.fetch(`https://www.googleapis.com/upload/drive/v3/files/${fileId}?uploadType=media`, {
                method: "PATCH",
                headers: {
                    "Authorization": `Bearer ${token}`,
                    "Content-Type": "application/json",
                },
                body: content,
            });
            return res.ok;
        }
        else {
            // Create new
            const metadata = JSON.stringify({
                name: filename,
                parents: [gDriveFolderId],
                mimeType: "application/json",
            });
            const boundary = "----mailx" + Date.now();
            const body = `--${boundary}\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n${metadata}\r\n--${boundary}\r\nContent-Type: application/json\r\n\r\n${content}\r\n--${boundary}--`;
            const res = await globalThis.fetch("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart", {
                method: "POST",
                headers: {
                    "Authorization": `Bearer ${token}`,
                    "Content-Type": `multipart/related; boundary=${boundary}`,
                },
                body,
            });
            return res.ok;
        }
    }
    catch (e) {
        console.error(`[settings] GDrive write ${filename}: ${e.message}`);
        return false;
    }
}
const PROVIDERS = {
    "gmail.com": {
        label: "Gmail",
        imap: { host: "imap.gmail.com", port: 993, tls: true, auth: "oauth2" },
        smtp: { host: "smtp.gmail.com", port: 587, tls: true, auth: "oauth2" },
    },
    "googlemail.com": {
        label: "Gmail",
        imap: { host: "imap.gmail.com", port: 993, tls: true, auth: "oauth2" },
        smtp: { host: "smtp.gmail.com", port: 587, tls: true, auth: "oauth2" },
    },
    "outlook.com": {
        label: "Outlook",
        imap: { host: "outlook.office365.com", port: 993, tls: true, auth: "oauth2" },
        smtp: { host: "smtp.office365.com", port: 587, tls: true, auth: "oauth2" },
    },
    "hotmail.com": {
        label: "Hotmail",
        imap: { host: "outlook.office365.com", port: 993, tls: true, auth: "oauth2" },
        smtp: { host: "smtp.office365.com", port: 587, tls: true, auth: "oauth2" },
    },
    "yahoo.com": {
        label: "Yahoo",
        imap: { host: "imap.mail.yahoo.com", port: 993, tls: true, auth: "password" },
        smtp: { host: "smtp.mail.yahoo.com", port: 587, tls: true, auth: "password" },
    },
    "icloud.com": {
        label: "iCloud",
        imap: { host: "imap.mail.me.com", port: 993, tls: true, auth: "password" },
        smtp: { host: "smtp.mail.me.com", port: 587, tls: true, auth: "password" },
    },
};
function normalizeAccount(acct, globalName) {
    const email = acct.email || "";
    const domain = email.split("@")[1]?.toLowerCase() || "";
    const provider = PROVIDERS[domain];
    const user = acct.imap?.user || acct.user || email;
    return {
        id: acct.id || domain.split(".")[0] || "account",
        name: acct.name || globalName || email.split("@")[0],
        label: acct.label || provider?.label,
        email,
        imap: {
            host: acct.imap?.host || provider?.imap.host || `imap.${domain}`,
            port: acct.imap?.port || provider?.imap.port || 993,
            tls: acct.imap?.tls ?? provider?.imap.tls ?? true,
            auth: acct.imap?.auth || provider?.imap.auth || "password",
            user: acct.imap?.user || user,
            password: acct.imap?.password || acct.password,
        },
        smtp: {
            host: acct.smtp?.host || provider?.smtp.host || `smtp.${domain}`,
            port: acct.smtp?.port || provider?.smtp.port || 587,
            tls: acct.smtp?.tls ?? provider?.smtp.tls ?? true,
            auth: acct.smtp?.auth || provider?.smtp.auth || "password",
            user: acct.smtp?.user || user,
            password: acct.smtp?.password || acct.password,
        },
        enabled: acct.enabled ?? true,
        defaultSend: acct.defaultSend,
        syncContacts: acct.syncContacts ?? (provider?.imap.auth === "oauth2"),
        relayDomains: acct.relayDomains,
    };
}
// ── Default settings ──
const DEFAULT_PREFERENCES = {
    ui: {
        theme: "system",
        editor: "quill",
        folderWidth: 220,
        listViewerSplit: 40,
        fontSize: 15,
    },
    sync: {
        intervalMinutes: 5,
        historyDays: 30,
        prefetch: true,
    },
    autocomplete: {
        enabled: false,
        provider: "off",
        ollamaUrl: "",
        ollamaModel: "",
        cloudApiKey: "",
        cloudModel: "",
        debounceMs: 600,
        maxTokens: 60,
    },
};
const DEFAULT_ALLOWLIST = {
    senders: [],
    domains: [],
    recipients: [],
    flaggedSenders: [],
    flaggedDomains: [],
};
// ── JSONC parser (strips comments and trailing commas) ──
function parseJsonc(text) {
    // Strip /* block comments */ and // line comments, but preserve content inside strings
    let stripped = "";
    let i = 0;
    let inString = false;
    let stringChar = "";
    while (i < text.length) {
        const c = text[i];
        const next = text[i + 1];
        if (inString) {
            stripped += c;
            if (c === "\\" && i + 1 < text.length) {
                stripped += text[i + 1];
                i += 2;
                continue;
            }
            if (c === stringChar)
                inString = false;
            i++;
            continue;
        }
        if (c === '"' || c === "'") {
            inString = true;
            stringChar = c;
            stripped += c;
            i++;
            continue;
        }
        if (c === "/" && next === "/") {
            // Line comment — skip to end of line
            while (i < text.length && text[i] !== "\n")
                i++;
            continue;
        }
        if (c === "/" && next === "*") {
            // Block comment — skip to */
            i += 2;
            while (i < text.length - 1 && !(text[i] === "*" && text[i + 1] === "/"))
                i++;
            i += 2;
            continue;
        }
        stripped += c;
        i++;
    }
    // Strip trailing commas before } or ]
    stripped = stripped.replace(/,(\s*[}\]])/g, "$1");
    return JSON.parse(stripped);
}
// ── Public API ──
/** Load accounts — first from IndexedDB cache, then GDrive */
export async function loadAccounts() {
    // Try local cache first
    const cached = await idbRead("accounts.jsonc");
    if (cached) {
        try {
            const data = parseJsonc(cached);
            const raw = data.accounts || (Array.isArray(data) ? data : []);
            if (raw.length > 0) {
                return raw.map((a) => normalizeAccount(a, data.name));
            }
        }
        catch (e) {
            console.warn(`[settings] Cached accounts.jsonc parse failed: ${e.message}`);
        }
    }
    // Try GDrive
    const content = await gDriveRead("accounts.jsonc");
    if (content) {
        await idbWrite("accounts.jsonc", content);
        try {
            const data = parseJsonc(content);
            const raw = data.accounts || (Array.isArray(data) ? data : []);
            return raw.map((a) => normalizeAccount(a, data.name));
        }
        catch (e) {
            console.warn(`[settings] GDrive accounts.jsonc parse failed: ${e.message}`);
        }
    }
    return [];
}
/** Load accounts directly from GDrive, bypassing local cache */
export async function loadAccountsFromCloud() {
    const content = await gDriveRead("accounts.jsonc");
    if (content) {
        await idbWrite("accounts.jsonc", content);
        try {
            const data = parseJsonc(content);
            const raw = data.accounts || (Array.isArray(data) ? data : []);
            return raw.map((a) => normalizeAccount(a, data.name));
        }
        catch (e) {
            console.warn(`[settings] loadAccountsFromCloud parse failed: ${e.message}`);
        }
    }
    return [];
}
/** Save accounts to IndexedDB and GDrive */
export async function saveAccounts(accounts) {
    const content = JSON.stringify({ accounts }, null, 2);
    await idbWrite("accounts.jsonc", content);
    await gDriveWrite("accounts.jsonc", content);
}
/** Load preferences */
export async function loadPreferences() {
    const cached = await idbRead("preferences.jsonc");
    if (cached) {
        try {
            const data = parseJsonc(cached);
            return {
                ui: { ...DEFAULT_PREFERENCES.ui, ...data.ui },
                sync: { ...DEFAULT_PREFERENCES.sync, ...data.sync },
                autocomplete: { ...DEFAULT_PREFERENCES.autocomplete, ...data.autocomplete },
            };
        }
        catch { /* parse error */ }
    }
    // Try GDrive
    const content = await gDriveRead("preferences.jsonc");
    if (content) {
        await idbWrite("preferences.jsonc", content);
        try {
            const data = parseJsonc(content);
            return {
                ui: { ...DEFAULT_PREFERENCES.ui, ...data.ui },
                sync: { ...DEFAULT_PREFERENCES.sync, ...data.sync },
                autocomplete: { ...DEFAULT_PREFERENCES.autocomplete, ...data.autocomplete },
            };
        }
        catch { /* parse error */ }
    }
    return { ...DEFAULT_PREFERENCES };
}
/** Save preferences */
export async function savePreferences(prefs) {
    const content = JSON.stringify(prefs, null, 2);
    await idbWrite("preferences.jsonc", content);
    await gDriveWrite("preferences.jsonc", content);
}
/** Load full settings (accounts + preferences combined) */
export async function loadSettings() {
    const accounts = await loadAccounts();
    const prefs = await loadPreferences();
    return {
        accounts,
        ui: prefs.ui,
        sync: prefs.sync,
        autocomplete: prefs.autocomplete,
        store: {
            basePath: "indexeddb",
            compressionBoundaryDays: 365,
        },
    };
}
/** Save full settings */
export async function saveSettings(settings) {
    await saveAccounts(settings.accounts);
    await savePreferences({ ui: settings.ui, sync: settings.sync, autocomplete: settings.autocomplete });
}
/** Load allowlist */
export async function loadAllowlist() {
    const cached = await idbRead("allowlist.jsonc");
    if (cached) {
        try {
            return parseJsonc(cached);
        }
        catch { /* */ }
    }
    const content = await gDriveRead("allowlist.jsonc");
    if (content) {
        await idbWrite("allowlist.jsonc", content);
        try {
            return parseJsonc(content);
        }
        catch { /* */ }
    }
    return { ...DEFAULT_ALLOWLIST };
}
/** Save allowlist */
export async function saveAllowlist(list) {
    const content = JSON.stringify(list, null, 2);
    await idbWrite("allowlist.jsonc", content);
    await gDriveWrite("allowlist.jsonc", content);
}
/** Load autocomplete settings */
export async function loadAutocomplete() {
    const prefs = await loadPreferences();
    return prefs.autocomplete;
}
/** Save autocomplete settings */
export async function saveAutocomplete(settings) {
    const prefs = await loadPreferences();
    prefs.autocomplete = settings;
    await savePreferences(prefs);
}
/** Get history days — read from preferences */
export async function getHistoryDays() {
    const prefs = await loadPreferences();
    return prefs.sync.historyDays || 30;
}
/** Get prefetch setting */
export async function getPrefetch() {
    const prefs = await loadPreferences();
    return prefs.sync.prefetch !== false;
}
/** Get storage info */
export function getStorageInfo() {
    return {
        provider: gDriveFolderId ? "gdrive" : "local",
        mode: gDriveFolderId ? "api" : "local",
        folderId: gDriveFolderId || undefined,
        folderName: gDriveFolderName || undefined,
        folderPath: gDriveFolderPath || undefined,
        folderOwner: gDriveFolderOwner || undefined,
    };
}
/** Clear all cached settings — used for "Reset Store" */
export async function clearSettings() {
    await idbDelete("accounts.jsonc");
    await idbDelete("preferences.jsonc");
    await idbDelete("allowlist.jsonc");
}
// ── Per-device settings ──
const DEVICE_ID_KEY = "mailx-device-id";
/** Get or create a stable device ID (UUID stored in localStorage) */
export function getDeviceId() {
    let id = localStorage.getItem(DEVICE_ID_KEY);
    if (!id) {
        id = crypto.randomUUID();
        localStorage.setItem(DEVICE_ID_KEY, id);
    }
    return id;
}
/** Save device-specific settings to GDrive (devices/<deviceId>/state.json) */
export async function saveDeviceState(state) {
    const deviceId = getDeviceId();
    const filename = `devices/${deviceId}/state.json`;
    const content = JSON.stringify(state, null, 2);
    await idbWrite(filename, content);
    await gDriveWrite(filename, content);
}
/** Load device-specific settings */
export async function loadDeviceState() {
    const deviceId = getDeviceId();
    const filename = `devices/${deviceId}/state.json`;
    const cached = await idbRead(filename);
    if (cached) {
        try {
            return parseJsonc(cached);
        }
        catch { /* */ }
    }
    const content = await gDriveRead(filename);
    if (content) {
        await idbWrite(filename, content);
        try {
            return parseJsonc(content);
        }
        catch { /* */ }
    }
    return {};
}
//# sourceMappingURL=web-settings.js.map