declare module 'imapflow' {
  import { EventEmitter } from 'stream';
  export type Readable = import('stream').Readable;

  export class ImapFlow extends EventEmitter {
    constructor(options: ImapFlowOptions);
    authenticated: string | boolean;
    capabilities: Map<string, boolean | number>;
    emitLogs: boolean;
    enabled: Set<string>;
    id: string;
    idling: boolean;
    mailbox: MailboxObject | boolean;
    secureConnection: boolean;
    serverInfo: IdInfoObject;
    usable: boolean;

    append(
      path: string,
      content: string | Buffer,
      flags?: string[],
      idate?: Date | string,
    ): Promise<AppendResponseObject>;

    connect(): Promise<void>;
    logout(): Promise<void>;
    close(): void;
    download(
      range: SequenceString,
      part?: string,
      options?: { uid?: boolean; maxBytes?: number; chunkSize?: number },
    ): Promise<DownloadObject>;

    getMailboxLock(path: string, options?: null | { readonly?: boolean }): Promise<MailboxLockObject>;

    getQuota(path: string): Promise<QuotaResponse | boolean>;

    idle(): Promise<boolean>;

    /**
     * @see {@link https://imapflow.com/module-imapflow-ImapFlow.html#list}
     */
    list(options?: ListOptions): Promise<ListResponse[]>;

    listTree(options?: ListOptions): Promise<ListTreeResponse>;

    mailboxClose(): Promise<boolean>;

    mailboxCreate(path: string | string[]): Promise<MailboxCreateResponse>;

    mailboxDelete(path: string | string[]): Promise<MailboxDeleteResponse>;

    mailboxOpen(path: string | string[], options?: { readOnly?: boolean }): Promise<MailboxObject>;

    mailboxRename(path: string | string[], newPath: string | string[]): Promise<MailboxRenameResponse>;

    mailboxSubscribe(path: string | string[]): Promise<boolean>;

    mailboxUnsubscribe(path: string | string[]): Promise<boolean>;

    messageCopy(
      range: SequenceString | number[] | SearchObject,
      destination: string,
      options?: { uid?: boolean },
    ): Promise<CopyResponseObject>;

    messageDelete(range: SequenceString | number[] | SearchObject, options?: { uid?: boolean }): Promise<boolean>;

    messageFlagsAdd(
      range: SequenceString | number[] | SearchObject,
      Array: string[],
      options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean },
    ): Promise<boolean>;

    messageFlagsRemove(
      range: SequenceString | number[] | SearchObject,
      Array: string[],
      options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean },
    ): Promise<boolean>;

    messageFlagsSet(
      range: SequenceString | number[] | SearchObject,
      Array: string[],
      options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean },
    ): Promise<boolean>;

    messageMove(
      range: SequenceString | number[] | SearchObject,
      destination: string,
      options?: { uid?: boolean },
    ): Promise<CopyResponseObject>;

    fetchOne(
      seq: SequenceString,
      query: FetchQueryObject,
      options?: {
        uid?: boolean;
      },
    ): Promise<FetchMessageObject>;

    noop(): Promise<void>;

    search(query: SearchObject, options?: { uid?: boolean }): Promise<number[]>;

    status(
      path: string,
      query: {
        messages?: boolean;
        recent?: boolean;
        uidNext?: boolean;
        uidValidity?: boolean;
        unseen?: boolean;
        highestModseq?: boolean;
      },
    ): Promise<StatusObject>;

    fetch(
      range: SequenceString | number[] | SearchObject,
      query: FetchQueryObject,
      options?: { uid?: boolean; changedSince?: bigint; binary?: boolean },
    ): AsyncGenerator<FetchMessageObject, never, void>;

    fetchAll(
      range: SequenceString | number[] | SearchObject,
      query: FetchQueryObject,
      options?: { uid?: boolean; changedSince?: bigint; binary?: boolean },
    ): Promise<FetchMessageObject[]>;
  }

  export interface ImapFlowOptions {
    host: string;
    port: number;
    auth: {
      user: string;
      pass?: string;
      accessToken?: string;
    };
    secure?: boolean;
    servername?: string;
    disableCompression?: boolean;
    clientInfo?: IdInfoObject;
    disableAutoIdle?: boolean;
    tls?: object;
    logger?: Logger | false;
    emitLogs?: boolean;
    verifyOnly?: boolean;
    logRaw?: boolean;
    proxy?: string;
    qresync?: boolean;
    maxIdleTime?: number;
    missingIdleCommand?: string;
    disableBinary?: boolean;
    disableAutoEnable?: boolean;
    connectionTimeout?: number;
    greetingTimeout?: number;
    socketTimeout?: number;
  }

  export interface AppendResponseObject {
    path: string;
    uidValidity?: bigint;
    uid?: number;
    seq?: number;
  }

  export interface CopyResponseObject {
    path: string;
    destination: string;
    uidValidity?: bigint;
    uidMap?: Map<number, number>;
  }

  export interface DownloadObject {
    content: Readable;
    meta: {
      expectedSize: number;
      contentType: string;
      charset?: string;
      disposition?: string;
      filename?: string;
    };
  }

  export interface MailboxObject {
    path: string;
    delimiter: string;
    flags: Set<string>;
    specialUse: string;
    listed: boolean;
    subscribed: boolean;
    permanentFlags: Set<string>;
    mailboxId: string;
    highestModseq: bigint;
    uidValidity: bigint;
    uidNext: number;
    exists: number;
  }

  export interface MailboxLockObject {
    path: string;
    release: () => void;
  }

  export interface FetchMessageObject {
    seq: number;
    uid: number;
    source: Buffer;
    modseq: bigint;
    emailId: string;
    threadId?: string;
    labels: Set<string>;
    size: number;
    flags: Set<string>;
    envelope: MessageEnvelopeObject;
    bodyStructure: MessageStructureObject;
    internalDate: Date;
    bodyParts: Map<string, Buffer>;
    headers: Buffer;
  }

  export interface FetchQueryObject {
    uid?: boolean;
    flags?: boolean;
    bodyStructure?: boolean;
    envelope?: boolean;
    internalDate?: boolean;
    size?: boolean;
    source?: boolean | object;
    threadId?: boolean;
    labels?: boolean;
    headers?: boolean | string[];
    bodyParts?: string[];
  }

  export interface MailboxRenameResponse {
    path: string;
    newPath: string;
  }

  export interface MessageAddressObject {
    name?: string;
    address?: string;
  }

  export interface MessageEnvelopeObject {
    date: Date;
    subject: string;
    messageId: string;
    inReplyTo: string;
    from: MessageAddressObject[];
    sender: MessageAddressObject[];
    replyTo: MessageAddressObject[];
    to: MessageAddressObject[];
    cc: MessageAddressObject[];
    bcc: MessageAddressObject[];
  }

  export interface QuotaResponse {
    path: string;
    storage?: object;
    messages?: object;
  }

  export type SequenceString = string;

  export interface SearchObject {
    seq?: SequenceString;
    answered?: boolean;
    deleted?: boolean;
    draft?: boolean;
    flagged?: boolean;
    seen?: boolean;
    all?: boolean;
    new?: boolean;
    old?: boolean;
    recent?: boolean;
    from?: string;
    to?: string;
    cc?: string;
    bcc?: string;
    body?: string;
    subject?: string;
    larger?: number;
    smaller?: number;
    uid?: SequenceString;
    modseq?: bigint;
    emailId?: string;
    threadId?: string;
    before?: Date | string;
    on?: Date | string;
    since?: Date | string;
    sentBefore?: Date | string;
    sentOn?: Date | string;
    sentSince?: Date | string;
    keyword?: string;
    unKeyword?: string;
    header?: Record<string, boolean | string>;
    or?: SearchObject[];
  }

  export interface StatusObject {
    path: string;
    messages?: number;
    recent?: number;
    uidNext?: number;
    uidValidity?: bigint;
    unseen?: number;
    highestModseq?: bigint;
  }

  export interface IdInfoObject {
    name?: string;
    version?: string;
    os?: string;
    vendor?: string;
    'support-url'?: string;
    date?: Date;
  }

  export interface ListResponse {
    path: string;
    name: string;
    delimiter: string;
    flags: Set<string>;
    specialUse: string;
    listed: boolean;
    subscribed: boolean;
    status?: StatusObject;
  }

  export interface ListTreeResponse {
    root: boolean;
    path: string;
    name: string;
    delimiter: string;
    flags: [];
    specialUse: string;
    listed: boolean;
    subscribed: boolean;
    disabled: boolean;
    folders: ListTreeResponse[];
  }

  export interface MailboxCreateResponse {
    path: string;
    mailboxId?: string;
    created: boolean;
  }

  export interface MailboxDeleteResponse {
    path: string;
  }

  export interface MessageStructureObject {
    part: string;
    type: string;
    parameters: string;
    id: string;
    encoding: string;
    size: number;
    envelope: MessageEnvelopeObject;
    disposition: string;
    dispositionParameters: string;
    childNodes: MessageStructureObject[];
  }

  export interface Logger {
    debug: (obj: object) => void;
    info: (obj: object) => void;
    warn: (obj: object) => void;
    error: (obj: object) => void;
  }

  export interface ListOptions {
    statusQuery?: StatusQuery;
    specialUseHints?: SpecialUseHints;
  }

  export interface StatusQuery {
    messages?: boolean;
    recent?: boolean;
    uidNext?: boolean;
    uidValidity?: boolean;
    unseen?: boolean;
    highestModseq?: boolean;
  }

  export interface SpecialUseHints {
    sent: string;
    trash: string;
    junk: string;
    drafts: string;
  }
}
