// @ts-check
import { DeltaChat } from '../dist/index.js'

import { deepStrictEqual, strictEqual } from 'assert'
import chai, { expect } from 'chai'
import chaiAsPromised from 'chai-as-promised'
import { EventId2EventName, C } from '../dist/constants.js'
import { join } from 'path'
import { statSync } from 'fs'
import { Context } from '../dist/context.js'
import {fileURLToPath} from 'url';

const __dirname = fileURLToPath(new URL('.', import.meta.url));

chai.use(chaiAsPromised)
chai.config.truncateThreshold = 0 // Do not truncate assertion errors.

function createTempUser(chatmailDomain) {
  const charset = "2345789acdefghjkmnpqrstuvwxyz";
  let user = "ci-";
  for (let i = 0; i < 6; i++) {
    user += charset[Math.floor(Math.random() * charset.length)];
  }
  const email = user + "@" + chatmailDomain;
  return { email: email, password: user + "$" + user };
}

describe('static tests', function () {
  this.timeout(60 * 5 * 1000) // increase timeout to 5 min

  it('reverse lookup of events', function () {
    const eventKeys = Object.keys(EventId2EventName).map((k) => Number(k))
    const eventValues = Object.values(EventId2EventName)
    const reverse = eventValues.map((v) => C[v])
    expect(reverse).to.be.deep.equal(eventKeys)
  })

  it('event constants are consistent', function () {
    const eventKeys = Object.keys(C)
      .filter((k) => k.startsWith('DC_EVENT_'))
      .sort()
    const eventValues = Object.values(EventId2EventName).sort()
    expect(eventKeys).to.be.deep.equal(eventValues)
  })

  it('static method maybeValidAddr()', function () {
    expect(DeltaChat.maybeValidAddr(null)).to.equal(false)
    expect(DeltaChat.maybeValidAddr('')).to.equal(false)
    expect(DeltaChat.maybeValidAddr('uuu')).to.equal(false)
    expect(DeltaChat.maybeValidAddr('dd.tt')).to.equal(false)
    expect(DeltaChat.maybeValidAddr('tt.dd@yggmail')).to.equal(true)
    expect(DeltaChat.maybeValidAddr('u@d')).to.equal(true)
    //expect(DeltaChat.maybeValidAddr('u@d.')).to.equal(false)
    //expect(DeltaChat.maybeValidAddr('u@d.t')).to.equal(false)
    //expect(DeltaChat.maybeValidAddr('u@.tt')).to.equal(false)
    expect(DeltaChat.maybeValidAddr('@d.tt')).to.equal(false)
    expect(DeltaChat.maybeValidAddr('user@domain.tld')).to.equal(true)
    expect(DeltaChat.maybeValidAddr('u@d.tt')).to.equal(true)
  })

  it('static getSystemInfo()', function () {
    const info = Context.getSystemInfo()
    expect(info).to.contain.keys([
      'arch',
      'deltachat_core_version',
      'sqlite_version',
    ])
  })

  it('static context.getProviderFromEmail("example@example.com")', function () {
    const provider = DeltaChat.getProviderFromEmail('example@example.com')

    expect(provider).to.deep.equal({
      before_login_hint: "Hush this provider doesn't exist!",
      overview_page: 'https://providers.delta.chat/example-com',
      status: 3,
    })
  })
})

describe('JSON RPC', function () {
  it('smoketest', async function () {
    const { dc } = DeltaChat.newTemporary()
    let promise_resolve
    const promise = new Promise((res, _rej) => {
      promise_resolve = (response) => {
        // ignore events
        const answer = JSON.parse(response)
        if (answer['method'] !== 'event') res(answer)
      }
    })
    dc.startJsonRpcHandler(promise_resolve)
    dc.jsonRpcRequest(
      JSON.stringify({
        jsonrpc: '2.0',
        method: 'get_all_account_ids',
        params: [],
        id: 2,
      })
    )
    deepStrictEqual(
      {
        jsonrpc: '2.0',
        id: 2,
        result: [1],
      },
      await promise
    )
    dc.close()
  })

  it('basic test', async function () {
    const { dc } = DeltaChat.newTemporary()

    const promises = {}
    dc.startJsonRpcHandler((msg) => {
      const response = JSON.parse(msg)
      if (response.hasOwnProperty('id')) promises[response.id](response)
      delete promises[response.id]
    })
    const call = (request) => {
      dc.jsonRpcRequest(JSON.stringify(request))
      return new Promise((res, _rej) => {
        promises[request.id] = res
      })
    }

    deepStrictEqual(
      {
        jsonrpc: '2.0',
        id: 2,
        result: [1],
      },
      await call({
        jsonrpc: '2.0',
        method: 'get_all_account_ids',
        params: [],
        id: 2,
      })
    )

    deepStrictEqual(
      {
        jsonrpc: '2.0',
        id: 3,
        result: 2,
      },
      await call({
        jsonrpc: '2.0',
        method: 'add_account',
        params: [],
        id: 3,
      })
    )

    deepStrictEqual(
      {
        jsonrpc: '2.0',
        id: 4,
        result: [1, 2],
      },
      await call({
        jsonrpc: '2.0',
        method: 'get_all_account_ids',
        params: [],
        id: 4,
      })
    )

    dc.close()
  })
})

describe('Basic offline Tests', function () {
  it('opens a context', async function () {
    const { dc, context } = DeltaChat.newTemporary()

    strictEqual(context.isConfigured(), false)
    dc.close()
  })

  it('set config', async function () {
    const { dc, context } = DeltaChat.newTemporary()

    context.setConfig('bot', true)
    strictEqual(context.getConfig('bot'), '1')
    context.setConfig('bot', false)
    strictEqual(context.getConfig('bot'), '0')
    context.setConfig('bot', '1')
    strictEqual(context.getConfig('bot'), '1')
    context.setConfig('bot', '0')
    strictEqual(context.getConfig('bot'), '0')
    context.setConfig('bot', 1)
    strictEqual(context.getConfig('bot'), '1')
    context.setConfig('bot', 0)
    strictEqual(context.getConfig('bot'), '0')

    context.setConfig('bot', null)
    strictEqual(context.getConfig('bot'), '')

    strictEqual(context.getConfig('selfstatus'), '')
    context.setConfig('selfstatus', 'hello')
    strictEqual(context.getConfig('selfstatus'), 'hello')
    context.setConfig('selfstatus', '')
    strictEqual(context.getConfig('selfstatus'), '')
    context.setConfig('selfstatus', null)
    strictEqual(context.getConfig('selfstatus'), '')

    dc.close()
  })

  it('configure with either missing addr or missing mail_pw throws', async function () {
    const { dc, context } = DeltaChat.newTemporary()
    dc.startEvents()

    await expect(
      context.configure({ addr: 'delta1@delta.localhost' })
    ).to.eventually.be.rejectedWith('Missing (IMAP) password.')
    await expect(context.configure({ mailPw: 'delta1' })).to.eventually.be
      .rejected

    context.stopOngoingProcess()
    dc.close()
  })

  it('context.getInfo()', async function () {
    const { dc, context } = DeltaChat.newTemporary()

    const info = await context.getInfo()
    expect(typeof info).to.be.equal('object')
    expect(info).to.contain.keys([
      'arch',
      'bcc_self',
      'blobdir',
      'bot',
      'configured_mvbox_folder',
      'configured_sentbox_folder',
      'database_dir',
      'database_encrypted',
      'database_version',
      'delete_device_after',
      'delete_server_after',
      'deltachat_core_version',
      'displayname',
      'download_limit',
      'e2ee_enabled',
      'entered_account_settings',
      'fetch_existing_msgs',
      'fingerprint',
      'folders_configured',
      'is_configured',
      'journal_mode',
      'key_gen_type',
      'last_housekeeping',
      'last_cant_decrypt_outgoing_msgs',
      'level',
      'mdns_enabled',
      'media_quality',
      'messages_in_contact_requests',
      'mvbox_move',
      'num_cpus',
      'number_of_chat_messages',
      'number_of_chats',
      'number_of_contacts',
      'only_fetch_mvbox',
      'private_key_count',
      'public_key_count',
      'quota_exceeding',
      'scan_all_folders_debounce_secs',
      'selfavatar',
      'sync_msgs',
      'sentbox_watch',
      'show_emails',
      'proxy_enabled',
      'sqlite_version',
      'uptime',
      'used_account_settings',
      'webrtc_instance',
    ])

    dc.close()
  })
})

describe('Offline Tests with unconfigured account', function () {
  let [dc, context, accountId, directory] = [null, null, null, null]

  this.beforeEach(async function () {
    let tmp = DeltaChat.newTemporary()
    dc = tmp.dc
    context = tmp.context
    accountId = tmp.accountId
    directory = tmp.directory
    dc.startEvents()
  })

  this.afterEach(async function () {
    if (context) {
      context.stopOngoingProcess()
    }
    if (dc) {
      try {
        dc.stopIO()
        dc.close()
      } catch (error) {
        console.error(error)
      }
    }

    dc = null
    context = null
    accountId = null
    directory = null
  })

  it('invalid context.joinSecurejoin', async function () {
    expect(context.joinSecurejoin('test')).to.be.eq(0)
  })

  it('Device Chat', async function () {
    const deviceChatMessageText = 'test234'

    expect((await context.getChatList(0, '', null)).getCount()).to.equal(
      0,
      'no device chat after setup'
    )

    await context.addDeviceMessage('test', deviceChatMessageText)

    const chatList = await context.getChatList(0, '', null)
    expect(chatList.getCount()).to.equal(
      1,
      'device chat after adding device msg'
    )

    const deviceChatId = await chatList.getChatId(0)
    const deviceChat = await context.getChat(deviceChatId)
    expect(deviceChat.isDeviceTalk()).to.be.true
    expect(deviceChat.toJson().isDeviceTalk).to.be.true

    const deviceChatMessages = await context.getChatMessages(deviceChatId, 0, 0)
    expect(deviceChatMessages.length).to.be.equal(
      1,
      'device chat has added message'
    )

    const deviceChatMessage = await context.getMessage(deviceChatMessages[0])
    expect(deviceChatMessage.getText()).to.equal(
      deviceChatMessageText,
      'device chat message has the inserted text'
    )
  })

  it('should have e2ee enabled and right blobdir', function () {
    expect(context.getConfig('e2ee_enabled')).to.equal(
      '1',
      'e2eeEnabled correct'
    )
    expect(
      String(context.getBlobdir()).startsWith(directory),
      'blobdir should be inside temp directory'
    )
    expect(
      String(context.getBlobdir()).endsWith('db.sqlite-blobs'),
      'blobdir end with "db.sqlite-blobs"'
    )
  })

  it('should create chat from contact and Chat methods', async function () {
    const contactId = context.createContact('aaa', 'aaa@site.org')

    strictEqual(context.lookupContactIdByAddr('aaa@site.org'), contactId)
    strictEqual(context.lookupContactIdByAddr('nope@site.net'), 0)

    let chatId = context.createChatByContactId(contactId)
    let chat = context.getChat(chatId)

    strictEqual(
      chat.getVisibility(),
      C.DC_CHAT_VISIBILITY_NORMAL,
      'not archived'
    )
    strictEqual(chat.getId(), chatId, 'chat id matches')
    strictEqual(chat.getName(), 'aaa', 'chat name matches')
    strictEqual(chat.getProfileImage(), null, 'no profile image')
    strictEqual(chat.getType(), C.DC_CHAT_TYPE_SINGLE, 'single chat')
    strictEqual(chat.isSelfTalk(), false, 'no self talk')
    // TODO make sure this is really the case!
    strictEqual(chat.isUnpromoted(), false, 'not unpromoted')
    strictEqual(chat.isProtected(), false, 'not verified')
    strictEqual(typeof chat.color, 'string', 'color is a string')

    strictEqual(context.getDraft(chatId), null, 'no draft message')
    context.setDraft(chatId, context.messageNew().setText('w00t!'))
    strictEqual(
      context.getDraft(chatId).toJson().text,
      'w00t!',
      'draft text correct'
    )
    context.setDraft(chatId, null)
    strictEqual(context.getDraft(chatId), null, 'draft removed')

    strictEqual(context.getChatIdByContactId(contactId), chatId)
    expect(context.getChatContacts(chatId)).to.deep.equal([contactId])

    context.setChatVisibility(chatId, C.DC_CHAT_VISIBILITY_ARCHIVED)
    strictEqual(
      context.getChat(chatId).getVisibility(),
      C.DC_CHAT_VISIBILITY_ARCHIVED,
      'chat archived'
    )
    context.setChatVisibility(chatId, C.DC_CHAT_VISIBILITY_NORMAL)
    strictEqual(
      chat.getVisibility(),
      C.DC_CHAT_VISIBILITY_NORMAL,
      'chat unarchived'
    )

    chatId = context.createGroupChat('unverified group', false)
    chat = context.getChat(chatId)
    strictEqual(chat.isProtected(), false, 'is not verified')
    strictEqual(chat.getType(), C.DC_CHAT_TYPE_GROUP, 'group chat')
    expect(context.getChatContacts(chatId)).to.deep.equal([
      C.DC_CONTACT_ID_SELF,
    ])

    const draft2 = context.getDraft(chatId)
    expect(draft2 == null, 'unpromoted group has no draft by default')

    context.setChatName(chatId, 'NEW NAME')
    strictEqual(context.getChat(chatId).getName(), 'NEW NAME', 'name updated')

    chatId = context.createGroupChat('a verified group', true)
    chat = context.getChat(chatId)
    strictEqual(chat.isProtected(), true, 'is verified')
  })

  it('test setting profile image', async function () {
    const chatId = context.createGroupChat('testing profile image group', false)
    const image = 'image.jpeg'
    const imagePath = join(__dirname, 'fixtures', image)
    const blobs = context.getBlobdir()

    context.setChatProfileImage(chatId, imagePath)
    const blobPath = context.getChat(chatId).getProfileImage()
    expect(blobPath.startsWith(blobs)).to.be.true
    expect(blobPath.includes('image')).to.be.true
    expect(blobPath.endsWith('.jpeg')).to.be.true

    context.setChatProfileImage(chatId, null)
    expect(context.getChat(chatId).getProfileImage()).to.be.equal(
      null,
      'image is null'
    )
  })

  it('test setting ephemeral timer', function () {
    const chatId = context.createGroupChat('testing ephemeral timer')

    strictEqual(
      context.getChatEphemeralTimer(chatId),
      0,
      'ephemeral timer is not set by default'
    )

    context.setChatEphemeralTimer(chatId, 60)
    strictEqual(
      context.getChatEphemeralTimer(chatId),
      60,
      'ephemeral timer is set to 1 minute'
    )

    context.setChatEphemeralTimer(chatId, 0)
    strictEqual(
      context.getChatEphemeralTimer(chatId),
      0,
      'ephemeral timer is reset'
    )
  })

  it('should create and delete chat', function () {
    const chatId = context.createGroupChat('GROUPCHAT')
    const chat = context.getChat(chatId)
    strictEqual(chat.getId(), chatId, 'correct chatId')
    context.deleteChat(chat.getId())
    strictEqual(context.getChat(chatId), null, 'chat removed')
  })

  it('new message and Message methods', function () {
    const text = 'w00t!'
    const msg = context.messageNew().setText(text)

    strictEqual(msg.getChatId(), 0, 'chat id 0 before sent')
    strictEqual(msg.getDuration(), 0, 'duration 0 before sent')
    strictEqual(msg.getFile(), '', 'no file set by default')
    strictEqual(msg.getFilebytes(), 0, 'and file bytes is 0')
    strictEqual(msg.getFilemime(), '', 'no filemime by default')
    strictEqual(msg.getFilename(), '', 'no filename set by default')
    strictEqual(msg.getFromId(), 0, 'no contact id set by default')
    strictEqual(msg.getHeight(), 0, 'plain text message have height 0')
    strictEqual(msg.getId(), 0, 'id 0 before sent')
    strictEqual(msg.getSetupcodebegin(), '', 'no setupcode begin')
    strictEqual(msg.getShowpadlock(), false, 'no padlock by default')

    const state = msg.getState()
    strictEqual(state.isUndefined(), true, 'no state by default')
    strictEqual(state.isFresh(), false, 'no state by default')
    strictEqual(state.isNoticed(), false, 'no state by default')
    strictEqual(state.isSeen(), false, 'no state by default')
    strictEqual(state.isPending(), false, 'no state by default')
    strictEqual(state.isFailed(), false, 'no state by default')
    strictEqual(state.isDelivered(), false, 'no state by default')
    strictEqual(state.isReceived(), false, 'no state by default')

    const summary = msg.getSummary()
    strictEqual(summary.getId(), 0, 'no summary id')
    strictEqual(summary.getState(), 0, 'no summary state')
    strictEqual(summary.getText1(), null, 'no summary text1')
    strictEqual(summary.getText1Meaning(), 0, 'no summary text1 meaning')
    strictEqual(summary.getText2(), '', 'no summary text2')
    strictEqual(summary.getTimestamp(), 0, 'no summary timestamp')

    //strictEqual(msg.getSummarytext(50), text, 'summary text is text')
    strictEqual(msg.getText(), text, 'msg text set correctly')
    strictEqual(msg.getTimestamp(), 0, 'no timestamp')

    const viewType = msg.getViewType()
    strictEqual(viewType.isText(), true)
    strictEqual(viewType.isImage(), false)
    strictEqual(viewType.isGif(), false)
    strictEqual(viewType.isAudio(), false)
    strictEqual(viewType.isVoice(), false)
    strictEqual(viewType.isVideo(), false)
    strictEqual(viewType.isFile(), false)

    strictEqual(msg.getWidth(), 0, 'no message width')
    strictEqual(msg.isDeadDrop(), false, 'not deaddrop')
    strictEqual(msg.isForwarded(), false, 'not forwarded')
    strictEqual(msg.isInfo(), false, 'not an info message')
    strictEqual(msg.isSent(), false, 'messge is not sent')
    strictEqual(msg.isSetupmessage(), false, 'not an autocrypt setup message')

    msg.latefilingMediasize(10, 20, 30)
    strictEqual(msg.getWidth(), 10, 'message width set correctly')
    strictEqual(msg.getHeight(), 20, 'message height set correctly')
    strictEqual(msg.getDuration(), 30, 'message duration set correctly')

    msg.setDimension(100, 200)
    strictEqual(msg.getWidth(), 100, 'message width set correctly')
    strictEqual(msg.getHeight(), 200, 'message height set correctly')

    msg.setDuration(314)
    strictEqual(msg.getDuration(), 314, 'message duration set correctly')

    expect(() => {
      msg.setFile(null)
    }).to.throw('Missing filename')

    const logo = join(__dirname, 'fixtures', 'logo.png')
    const stat = statSync(logo)
    msg.setFile(logo)
    strictEqual(msg.getFilebytes(), stat.size, 'correct file size')
    strictEqual(msg.getFile(), logo, 'correct file name')
    strictEqual(msg.getFilemime(), 'image/png', 'mime set implicitly')
    msg.setFile(logo, 'image/gif')
    strictEqual(msg.getFilemime(), 'image/gif', 'mime set (in)correctly')
    msg.setFile(logo, 'image/png')
    strictEqual(msg.getFilemime(), 'image/png', 'mime set correctly')

    const json = msg.toJson()
    expect(json).to.not.equal(null, 'not null')
    strictEqual(typeof json, 'object', 'json object')
  })

  it('Contact methods', function () {
    const contactId = context.createContact('First Last', 'first.last@site.org')
    const contact = context.getContact(contactId)

    strictEqual(contact.getAddress(), 'first.last@site.org', 'correct address')
    strictEqual(typeof contact.color, 'string', 'color is a string')
    strictEqual(contact.getDisplayName(), 'First Last', 'correct display name')
    strictEqual(contact.getId(), contactId, 'contact id matches')
    strictEqual(contact.getName(), 'First Last', 'correct name')
    strictEqual(contact.getNameAndAddress(), 'First Last (first.last@site.org)')
    strictEqual(contact.getProfileImage(), null, 'no contact image')
    strictEqual(contact.isBlocked(), false, 'not blocked')
    strictEqual(contact.isVerified(), false, 'unverified status')
    strictEqual(contact.lastSeen, 0, 'last seen unknown')
  })

  it('create contacts from address book', function () {
    const addresses = [
      'Name One',
      'name1@site.org',
      'Name Two',
      'name2@site.org',
      'Name Three',
      'name3@site.org',
    ]
    const count = context.addAddressBook(addresses.join('\n'))
    strictEqual(count, addresses.length / 2)
    context
      .getContacts(0, 'Name ')
      .map((id) => context.getContact(id))
      .forEach((contact) => {
        expect(contact.getName().startsWith('Name ')).to.be.true
      })
  })

  it('delete contacts', function () {
    const id = context.createContact('someuser', 'someuser@site.com')
    const contact = context.getContact(id)
    strictEqual(contact.getId(), id, 'contact id matches')
    context.deleteContact(id)
    strictEqual(context.getContact(id), null, 'contact is gone')
  })

  it('adding and removing a contact from a chat', function () {
    const chatId = context.createGroupChat('adding_and_removing')
    const contactId = context.createContact('Add Remove', 'add.remove@site.com')
    strictEqual(
      context.addContactToChat(chatId, contactId),
      true,
      'contact added'
    )
    strictEqual(
      context.isContactInChat(chatId, contactId),
      true,
      'contact in chat'
    )
    strictEqual(
      context.removeContactFromChat(chatId, contactId),
      true,
      'contact removed'
    )
    strictEqual(
      context.isContactInChat(chatId, contactId),
      false,
      'contact not in chat'
    )
  })

  it('blocking contacts', function () {
    const id = context.createContact('badcontact', 'bad@site.com')

    strictEqual(context.getBlockedCount(), 0)
    strictEqual(context.getContact(id).isBlocked(), false)
    expect(context.getBlockedContacts()).to.be.empty

    context.blockContact(id, true)
    strictEqual(context.getBlockedCount(), 1)
    strictEqual(context.getContact(id).isBlocked(), true)
    expect(context.getBlockedContacts()).to.deep.equal([id])

    context.blockContact(id, false)
    strictEqual(context.getBlockedCount(), 0)
    strictEqual(context.getContact(id).isBlocked(), false)
    expect(context.getBlockedContacts()).to.be.empty
  })

  it('ChatList methods', function () {
    const ids = [
      context.createGroupChat('groupchat1'),
      context.createGroupChat('groupchat11'),
      context.createGroupChat('groupchat111'),
    ]

    let chatList = context.getChatList(0, 'groupchat1', null)
    strictEqual(chatList.getCount(), 3, 'should contain above chats')
    expect(ids.indexOf(chatList.getChatId(0))).not.to.equal(-1)
    expect(ids.indexOf(chatList.getChatId(1))).not.to.equal(-1)
    expect(ids.indexOf(chatList.getChatId(2))).not.to.equal(-1)

    const lot = chatList.getSummary(0)
    strictEqual(lot.getId(), 0, 'lot has no id')
    strictEqual(lot.getState(), C.DC_STATE_IN_NOTICED, 'correct state')

    const text = 'Others will only see this group after you sent a first message.'
    context.createGroupChat('groupchat1111')
    chatList = context.getChatList(0, 'groupchat1111', null)
    strictEqual(
      chatList.getSummary(0).getText2(),
      text,
      'custom new group message'
    )

    context.setChatVisibility(ids[0], C.DC_CHAT_VISIBILITY_ARCHIVED)
    chatList = context.getChatList(C.DC_GCL_ARCHIVED_ONLY, 'groupchat1', null)
    strictEqual(chatList.getCount(), 1, 'only one archived')
  })

  it('Remove quote from (draft) message', function () {
    context.addDeviceMessage('test_quote', 'test')
    const msgId = context.getChatMessages(10, 0, 0)[0]
    const msg = context.messageNew()

    msg.setQuote(context.getMessage(msgId))
    expect(msg.getQuotedMessage()).to.not.be.null
    msg.setQuote(null)
    expect(msg.getQuotedMessage()).to.be.null
  })
})

describe('Integration tests', function () {
  this.timeout(60 * 5 * 1000) // increase timeout to 5 min

  let [dc, context, accountId, directory, account] = [
    null,
    null,
    null,
    null,
    null,
  ]

  let [dc2, context2, accountId2, directory2, account2] = [
    null,
    null,
    null,
    null,
    null,
  ]

  this.beforeEach(async function () {
    let tmp = DeltaChat.newTemporary()
    dc = tmp.dc
    context = tmp.context
    accountId = tmp.accountId
    directory = tmp.directory
    dc.startEvents()
  })

  this.afterEach(async function () {
    if (context) {
      try {
        context.stopOngoingProcess()
      } catch (error) {
        console.error(error)
      }
    }
    if (context2) {
      try {
        context2.stopOngoingProcess()
      } catch (error) {
        console.error(error)
      }
    }

    if (dc) {
      try {
        dc.stopIO()
        dc.close()
      } catch (error) {
        console.error(error)
      }
    }

    dc = null
    context = null
    accountId = null
    directory = null

    context2 = null
    accountId2 = null
    directory2 = null
  })

  this.beforeAll(async function () {
    account = createTempUser(process.env.CHATMAIL_DOMAIN)
    if (!account || !account.email || !account.password) {
      console.log(
        "We didn't got back an account from the api, skip integration tests"
      )
      this.skip()
    }
  })

  it('configure', async function () {
    strictEqual(context.isConfigured(), false, 'should not be configured')

    // Not sure what's the best way to check the events
    // TODO: check the events

    // dc.once('DC_EVENT_CONFIGURE_PROGRESS', (data) => {
    //   t.pass('DC_EVENT_CONFIGURE_PROGRESS called at least once')
    // })
    // dc.on('DC_EVENT_ERROR', (error) => {
    //   console.error('DC_EVENT_ERROR', error)
    // })
    // dc.on('DC_EVENT_ERROR_NETWORK', (first, error) => {
    //   console.error('DC_EVENT_ERROR_NETWORK', error)
    // })

    // dc.on('ALL', (event, data1, data2) => console.log('ALL', event, data1, data2))

    await expect(
      context.configure({
        addr: account.email,
        mail_pw: account.password,

        displayname: 'Delta One',
        selfstatus: 'From Delta One with <3',
        selfavatar: join(__dirname, 'fixtures', 'avatar.png'),
      })
    ).to.be.eventually.fulfilled

    strictEqual(context.getConfig('addr'), account.email, 'addr correct')
    strictEqual(
      context.getConfig('displayname'),
      'Delta One',
      'displayName correct'
    )
    strictEqual(
      context.getConfig('selfstatus'),
      'From Delta One with <3',
      'selfStatus correct'
    )
    expect(
      context.getConfig('selfavatar').endsWith('avatar.png'),
      'selfavatar correct'
    )
    strictEqual(context.getConfig('e2ee_enabled'), '1', 'e2ee_enabled correct')
    strictEqual(
      context.getConfig('save_mime_headers'),
      '',
      'save_mime_headers correct'
    )

    expect(context.getBlobdir().endsWith('db.sqlite-blobs'), 'correct blobdir')
    strictEqual(context.isConfigured(), true, 'is configured')

    // whole re-configure to only change displayname: what the heck? (copied this from the old test)
    await expect(
      context.configure({
        addr: account.email,
        mail_pw: account.password,
        displayname: 'Delta Two',
        selfstatus: 'From Delta One with <3',
        selfavatar: join(__dirname, 'fixtures', 'avatar.png'),
      })
    ).to.be.eventually.fulfilled
    strictEqual(
      context.getConfig('displayname'),
      'Delta Two',
      'updated displayName correct'
    )
  })

  it('Autocrypt setup - key transfer', async function () {
    // Spawn a second dc instance with same account
    // dc.on('ALL', (event, data1, data2) =>
    //   console.log('FIRST ', event, data1, data2)
    // )
    dc.stopIO()
    await expect(
      context.configure({
        addr: account.email,
        mail_pw: account.password,

        displayname: 'Delta One',
        selfstatus: 'From Delta One with <3',
        selfavatar: join(__dirname, 'fixtures', 'avatar.png'),
      })
    ).to.be.eventually.fulfilled

    const accountId2 = dc.addAccount()
    console.log('accountId2:', accountId2)
    context2 = dc.accountContext(accountId2)

    let setupCode = null
    const waitForSetupCode = waitForSomething()
    const waitForEnd = waitForSomething()

    dc.on('ALL', (event, accountId, data1, data2) => {
      console.log('[' + accountId + ']', event, data1, data2)
    })

    dc.on('DC_EVENT_MSGS_CHANGED', async (aId, chatId, msgId) => {
      console.log('[' + accountId + '] DC_EVENT_MSGS_CHANGED', chatId, msgId)
      if (
        aId != accountId ||
        !context.getChat(chatId).isSelfTalk() ||
        !context.getMessage(msgId).isSetupmessage()
      ) {
        return
      }
      console.log('Setupcode!')
      let setupCode = await waitForSetupCode.promise
      // console.log('incoming msg', { setupCode })
      const messages = context.getChatMessages(chatId, 0, 0)
      expect(messages.indexOf(msgId) !== -1, 'msgId is in chat messages').to.be
        .true
      const result = await context.continueKeyTransfer(msgId, setupCode)
      expect(result === true, 'continueKeyTransfer was successful').to.be.true

      waitForEnd.done()
    })

    dc.stopIO()
    await expect(
      context2.configure({
        addr: account.email,
        mail_pw: account.password,

        displayname: 'Delta One',
        selfstatus: 'From Delta One with <3',
        selfavatar: join(__dirname, 'fixtures', 'avatar.png'),
      })
    ).to.be.eventually.fulfilled
    dc.startIO()

    console.log('Sending autocrypt setup code')
    setupCode = await context2.initiateKeyTransfer()
    console.log('Sent autocrypt setup code')
    waitForSetupCode.done(setupCode)
    console.log('setupCode is: ' + setupCode)
    expect(typeof setupCode).to.equal('string', 'setupCode is string')

    await waitForEnd.promise
  })

  it('configure using invalid password should fail', async function () {
    await expect(
      context.configure({
        addr: 'hpk5@testrun.org',
        mail_pw: 'asd',
      })
    ).to.be.eventually.rejected
  })
})

/**
 * @returns {{done: (result?)=>void, promise:Promise<any> }}
 */
function waitForSomething() {
  let resolvePromise
  const promise = new Promise((res, rej) => {
    resolvePromise = res
  })
  return {
    done: resolvePromise,
    promise,
  }
}
