/**
 * MailxApi — the single typed contract that every mailx service implementation
 * must satisfy. Both the Node-side desktop service (`MailxService`) and the
 * browser-side Android service (`WebMailxService`) declare
 * `implements MailxApi`. Missing methods become build errors instead of
 * silent runtime "parent bridge has no method X" failures.
 *
 * **Why this exists**: until now the two implementations drifted independently.
 * A method added to MailxService for one platform release would be silently
 * absent on the other for months. spellcheck-on-GDrive (2026-05-13) added
 * `getUserDict` / `addUserDictWord` / `removeUserDictWord` to the desktop
 * service; the Android service got none of them; the relay returned
 * `parent bridge has no method`, the client `.catch(() => {})` swallowed the
 * rejection, and the feature silently regressed on phone. With this contract
 * any such addition is a compile-time failure on whichever side it's missing.
 *
 * **Surface**: the 83 method names dispatched by
 * `mailx-service/jsonrpc.ts` plus a handful of internal helpers both
 * implementations need. IPC-only names (`sendMessage`, `createCalendarEvent`,
 * etc.) are listed under their dispatch names — desktop classes that today
 * use a different internal name (e.g., `send`, `createCalendarEventLocal`)
 * add a thin alias method so the contract holds without renaming call sites.
 *
 * **Optional methods**: a handful of methods require an OS popup or native
 * filesystem hook and have no meaningful browser equivalent
 * (`openInWord`, `closeWordEdit`, `showReminderPopup`, `openInTextEditor`,
 * `openLocalPath`). They are declared `?:` so the browser service can
 * legitimately omit them. The desktop service must implement them.
 *
 * **Types**: signatures use `any` where the shape genuinely varies across
 * implementations (envelope rows, settings, AI payloads). The contract's
 * job is name + arity enforcement, not deep schema typing — that lives in
 * the relevant domain types (`Folder`, `AccountConfig`, etc.).
 *
 * **Where to add a new method**: add the method here first. Both
 * implementations will fail to compile until they implement it. Don't add
 * methods to a service class without the interface entry — that's how we
 * got into the silent-drift hole.
 */

import type { AutocompleteSettings, AutocompleteRequest, AutocompleteResponse, AiTransformRequest, AiTransformResponse } from "./index.js";
import type { Folder } from "./index.js";

export interface MailxApi {
    // ── Accounts / folders / messages ─────────────────────────────────
    getAccounts(): any[] | Promise<any[]>;
    getFolders(accountId: string): Folder[] | Promise<Folder[]>;
    getMessages(accountId: string, folderId: number, page?: number, pageSize?: number, sort?: string, sortDir?: string, search?: string, flaggedOnly?: boolean): any | Promise<any>;
    getUnifiedInbox(page?: number, pageSize?: number): any | Promise<any>;
    getMessage(accountId: string, uid: number, allowRemote?: boolean, folderId?: number): Promise<any>;
    getThreadMessages(accountId: string, threadId: string): any | Promise<any>;
    getAttachment(accountId: string, uid: number, attachmentId: number, folderId?: number): Promise<{ content: any; contentType: string; filename: string }>;
    openAttachment(accountId: string, uid: number, attachmentId: number, folderId?: number, filename?: string): Promise<{ ok: boolean; path: string }>;
    getMessageSource(accountId: string, uid: number, folderId?: number): Promise<{ dataBase64: string; filename: string }>;

    // ── Flags / move / delete ─────────────────────────────────────────
    updateFlags(accountId: string, uid: number, flags: string[], folderId?: number): Promise<void>;
    deleteMessage(accountId: string, uid: number): Promise<void>;
    deleteMessages(accountId: string, uids: number[]): Promise<void>;
    undeleteMessage(accountId: string, uid: number, folderId: number): Promise<void>;
    moveMessage(accountId: string, uid: number, targetFolderId: number, targetAccountId?: string): Promise<void>;
    moveMessages(accountId: string, uids: number[], targetFolderId: number, folderIds?: number[]): Promise<void>;
    copyMessages(accountId: string, uids: number[], folderIds: number[] | undefined, targetFolderId: number): Promise<void>;
    markAsSpamMessages(accountId: string, uids: number[], folderIds?: number[]): Promise<{ targetFolderId: number; moved: number }>;
    recordSpamReport(accountId: string, uid: number, folderId: number): Promise<any>;
    markFolderRead(folderId: number): void | Promise<void>;

    // ── Folders ───────────────────────────────────────────────────────
    createFolder(accountId: string, parentPath: string, name: string): Promise<void>;
    renameFolder(accountId: string, folderId: number, newName: string): Promise<void>;
    deleteFolder(accountId: string, folderId: number): Promise<void>;
    moveFolderToTrash(accountId: string, folderId: number): Promise<void>;
    emptyFolder(accountId: string, folderId: number): Promise<void>;

    // ── Send / draft / outbox ─────────────────────────────────────────
    sendMessage(msg: any): Promise<void>;
    // bcc appended LAST (not next to cc) so existing positional callers —
    // Android bridge adapters, tests — keep working across the wire.
    saveDraft(accountId: string, subject: string, bodyHtml: string, bodyText: string, to?: string, cc?: string, previousDraftUid?: number, draftId?: string, bcc?: string): Promise<{ draftUid: number | null; draftId: string }>;
    deleteDraft(accountId: string, draftUid: number, draftId?: string): Promise<void>;
    getOutboxStatus(): any | Promise<any>;
    listQueuedOutgoing(): any[] | Promise<any[]>;
    cancelQueuedOutgoing(filePath: string): { ok: true } | Promise<{ ok: true }>;

    // ── Sync ──────────────────────────────────────────────────────────
    syncAll(): Promise<void>;
    syncAccount(accountId: string): Promise<void>;
    syncFolderNow(accountId: string, folderId: number): Promise<void>;
    getSyncPending(): { pending: number } | Promise<{ pending: number }>;
    drainStoreSync(): Promise<void>;
    reauthenticate(accountId: string): Promise<boolean>;
    reauthGoogleScopes(): { cleared: number } | Promise<{ cleared: number }>;

    // ── Search ────────────────────────────────────────────────────────
    searchMessages(query: string, page?: number, pageSize?: number, scope?: string, accountId?: string, folderId?: number, includeTrashSpam?: boolean): Promise<any>;
    cancelServerSearch(): void | Promise<void>;

    // ── Contacts / autocomplete ───────────────────────────────────────
    searchContacts(query: string): any[] | Promise<any[]>;
    hasCcHistoryTo(email: string): boolean | Promise<boolean>;
    hasBccHistoryTo(email: string): boolean | Promise<boolean>;
    addContact(name: string, email: string): boolean | Promise<boolean>;
    listContacts(query: string, page?: number, pageSize?: number): any | Promise<any>;
    upsertContact(name: string, email: string): { ok: true } | Promise<{ ok: true }>;
    deleteContact(email: string): { ok: true } | Promise<{ ok: true }>;
    addPreferredContact(entry: { name: string; email: string; source?: string; organization?: string }): Promise<void>;
    addToDenylist(email: string): Promise<void>;
    loadContactsConfig(): Promise<any>;

    // ── Priority lists / sender reputation ────────────────────────────
    getPriorityLists(): { senders: string[]; domains: string[] } | Promise<{ senders: string[]; domains: string[] }>;
    setPrioritySender(email: string, value: boolean, name?: string): Promise<void>;
    setPriorityDomain(domain: string, value: boolean): Promise<void>;
    flagSenderOrDomain(type: "sender" | "domain", value: string): Promise<{ flagged: boolean }>;
    allowRemoteContent(type: "sender" | "domain" | "recipient", value: string): Promise<void>;

    // ── Calendar / tasks ──────────────────────────────────────────────
    getCalendarEvents(fromMs: number, toMs: number): Promise<any[]>;
    getCalendars(): Promise<Array<{ id: string; name: string; color: string; primary: boolean }>>;
    createCalendarEvent(ev: any): Promise<{ uuid: string }>;
    updateCalendarEvent(uuid: string, patch: any): Promise<{ ok: true }>;
    deleteCalendarEvent(uuid: string): Promise<{ ok: true }>;
    getTasks(includeCompleted?: boolean): Promise<any[]>;
    createTask(t: { title: string; notes?: string; dueMs?: number }): Promise<{ uuid: string }>;
    updateTask(uuid: string, patch: any): Promise<{ ok: true }>;
    deleteTask(uuid: string): Promise<{ ok: true }>;

    // ── User dictionary (cloud-mirrored spellcheck) ───────────────────
    getUserDict(): Promise<string[]>;
    addUserDictWord(word: string): Promise<string[]>;
    addUserDictWords(words: string[]): Promise<string[]>;
    removeUserDictWord(word: string): Promise<string[]>;

    // ── Settings / setup / diagnostics ────────────────────────────────
    getSettings(): any | Promise<any>;
    saveSettingsData(settings: any): Promise<void>;
    getDiagnostics(): any | Promise<any>;
    getPrimaryAccount(feature?: string): any | Promise<any>;
    setupAccount(name: string, email: string, password?: string): Promise<{ ok: boolean; error?: string; message?: string }>;
    repairAccounts(): Promise<{ ok: boolean; error?: string; message?: string }>;
    getAutocompleteSettings(): AutocompleteSettings | Promise<AutocompleteSettings>;
    saveAutocompleteSettings(settings: AutocompleteSettings): void | Promise<void>;

    // ── AI / autocomplete (ghost text + transforms) ───────────────────
    autocomplete(req: AutocompleteRequest): Promise<AutocompleteResponse>;
    aiTransform(req: AiTransformRequest): Promise<AiTransformResponse>;

    // ── Config JSONC editor + help docs ───────────────────────────────
    readJsoncFile(name: string): Promise<string | null>;
    writeJsoncFile(name: string, content: string): Promise<void>;
    formatJsonc(content: string): Promise<string>;
    readConfigHelp(name: string): Promise<string>;

    // ── External actions (mailto, unsubscribe, restart, etc.) ─────────
    unsubscribeOneClick(url: string): Promise<{ ok: boolean; status: number; statusText: string }>;
    consumePendingMailto(): any | Promise<any>;
    getReminderSound(): Promise<{ mute?: boolean; dataBase64?: string; mime?: string }>;
    logClientEvent?(...args: any[]): void | Promise<void>;
    getVersion?(): any | Promise<any>;

    // ── Desktop-only (OS popups, native FS, Word integration) ─────────
    // Browser/Android services may omit these — declared optional so a
    // missing implementation on the web side compiles cleanly.
    openInWord?(editId: string, html: string): Promise<{ ok: boolean; path: string; opener: string }>;
    closeWordEdit?(editId: string): Promise<void>;
    showReminderPopup?(opts: any): Promise<{ button: string; form?: any }>;
    openInTextEditor?(filePath: string): Promise<{ ok: boolean; opener: string; reason?: string }>;
    openLocalPath?(which: "config" | "log"): Promise<{ ok: true; path: string }>;
    popoutWindow?(accountId: string, uid: number, folderId?: number, subject?: string): Promise<{ ok: boolean; reason?: string }>;
    /** Register rmfmail as the OS mailto: handler. Windows completes with a
     *  manual Default-apps step described in `message`. */
    registerMailto?(): Promise<{ ok: boolean; message: string }>;
    /** Offline grammar lint (Harper). One entry per input block; spans are
     *  offsets into that block's plain text. Optional — absent host means
     *  the feature is silently off. */
    grammarLint?(blocks: string[]): Promise<{ blocks: { start: number; end: number; kind: string; message: string; suggestions: string[] }[][] }>;
}
