import { expect, test } from 'vitest'
import _ from 'lodash'
import * as utils from './utils'
import * as config from './config'
import * as chat from '../src'

const apiUrl = config.get('API_URL')

const protocols = ['sse', 'websocket'] as const

test.each(protocols)('api allows adding and removing conversation participants', async (protocol) => {
  const client = new chat.Client({ apiUrl })

  const { key: userKey1 } = await client.createUser({})
  const { user: user2, key: userKey2 } = await client.createUser({})

  const { conversation } = await client.createConversation({ 'x-user-key': userKey1 })

  const listener = await client.listenConversation({ id: conversation.id, 'x-user-key': userKey1, protocol })

  await expect(
    client.createMessage({
      'x-user-key': userKey2,
      conversationId: conversation.id,
      payload: {
        type: 'text',
        text: 'hello world',
      },
    })
  ).rejects.toThrow(chat.ForbiddenError)

  await Promise.all([
    utils.waitFor(listener, 'participant_added'),
    client.addParticipant({
      'x-user-key': userKey1,
      conversationId: conversation.id,
      userId: user2.id,
    }),
  ])

  await expect(
    client.createMessage({
      'x-user-key': userKey2,
      conversationId: conversation.id,
      payload: {
        type: 'text',
        text: 'hello world',
      },
    })
  ).resolves.toBeTruthy()

  await Promise.all([
    utils.waitFor(listener, 'participant_removed'),
    client.removeParticipant({
      'x-user-key': userKey1,
      conversationId: conversation.id,
      userId: user2.id,
    }),
  ])

  await expect(
    client.createMessage({
      'x-user-key': userKey2,
      conversationId: conversation.id,
      payload: {
        type: 'text',
        text: 'hello world',
      },
    })
  ).rejects.toThrow(chat.ForbiddenError)
})

test('api forbids non-participants from listing participants', async () => {
  const client = new chat.Client({ apiUrl })

  const user1 = await client.createUser({})
  const user2 = await client.createUser({})
  const user3 = await client.createUser({})

  const { conversation } = await client.createConversation({ 'x-user-key': user1.key })
  await client.addParticipant({ 'x-user-key': user1.key, conversationId: conversation.id, userId: user2.user.id })

  const promise1 = client.listParticipants({ 'x-user-key': user1.key, conversationId: conversation.id })
  await expect(promise1).resolves.toBeTruthy()
  const promise2 = client.listParticipants({ 'x-user-key': user2.key, conversationId: conversation.id })
  await expect(promise2).resolves.toBeTruthy()
  const promise3 = client.listParticipants({ 'x-user-key': user3.key, conversationId: conversation.id })
  await expect(promise3).rejects.toThrow(chat.ForbiddenError)
})

test.each(protocols)('signal listener is disconnected when participant is removed', async (protocol) => {
  const client = new chat.Client({ apiUrl })

  const { user: user1, key: userKey1 } = await client.createUser({})
  const { user: user2, key: userKey2 } = await client.createUser({})

  const { conversation } = await client.createConversation({ 'x-user-key': userKey1 })
  await client.addParticipant({ 'x-user-key': userKey1, conversationId: conversation.id, userId: user2.id })

  const listener1 = await client.listenConversation({ id: conversation.id, 'x-user-key': userKey1, protocol })
  const listener2 = await client.listenConversation({ id: conversation.id, 'x-user-key': userKey2, protocol })

  const messages1: chat.Signals['message_created'][] = []
  const messages2: chat.Signals['message_created'][] = []

  listener1.on('message_created', (message) => messages1.push(message))
  listener2.on('message_created', (message) => messages2.push(message))

  await Promise.all([
    utils.waitFor(listener1, 'message_created'),
    client.createMessage({
      'x-user-key': userKey1,
      conversationId: conversation.id,
      payload: {
        type: 'text',
        text: 'foo',
      },
    }),
  ])

  await client.removeParticipant({ 'x-user-key': userKey1, conversationId: conversation.id, userId: user2.id })

  await Promise.all([
    utils.waitFor(listener1, 'message_created'),
    client.createMessage({
      'x-user-key': userKey1,
      conversationId: conversation.id,
      payload: {
        type: 'text',
        text: 'bar',
      },
    }),
  ])

  const incomingMessages1 = messages1.filter((m) => !m.isBot)
  const incomingMessages2 = messages2.filter((m) => !m.isBot)
  expect(incomingMessages1.length).toBe(2)
  expect(incomingMessages2.length).toBe(1)

  await listener1.disconnect()
  listener1.cleanup()

  await listener2.disconnect()
  listener2.cleanup()
})

test('api forbids removing owner from conversation participants', async () => {
  const client = new chat.Client({ apiUrl })

  const { user: user1, key: userKey1 } = await client.createUser({})

  const { conversation } = await client.createConversation({ 'x-user-key': userKey1 })

  await expect(
    client.removeParticipant({
      'x-user-key': userKey1,
      conversationId: conversation.id,
      userId: user1.id,
    })
  ).rejects.toThrow(chat.InvalidPayloadError)
})
