import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { JoinPage } from './JoinPage'
import { Provider } from 'react-redux'
import { configureStore, EnhancedStore } from '@reduxjs/toolkit'
import { zocksStoreConfig } from '../../app/store'
import { setConnecting, setSessionConfig } from '../../store/session/sessionSlice'
import { setupMediaDevicesMock, setupMockLocalMediaStream, setupMockVirtualBackground } from '../../testUtils'
import { mock } from 'jest-mock-extended'
import { Auth, RoomInfo, StreamKind, StreamSource, VirtualBackgroundType } from '@zocks-communications/client-sdk-js'
import { MissingScopeAction } from '../ScopedBlock'
import * as zocksSdk from '@zocks-communications/client-sdk-js'

beforeEach(() => setupMediaDevicesMock())

afterEach(() => jest.restoreAllMocks())

const renderWithState = async (
  store: EnhancedStore,
  joinAction: () => void,
  props?: any,
  zocksAuthHasScope: boolean = true
) => {
  const mockAuth = mock<Auth>({ hasScope: jest.fn().mockReturnValue(zocksAuthHasScope) })
  jest.spyOn(zocksSdk, 'Auth').mockReturnValue(mockAuth)
  store.dispatch(setSessionConfig({ accountId: 'testAccountId' }))
  const view = render(
    <Provider store={store}>
      <JoinPage joinAction={joinAction} {...props} />
    </Provider>
  )
  expect(await screen.findByRole('button', { name: 'Audio devices' })).toBeInTheDocument()
  return view
}

test('renders a local stream preview, a meeting id input, join and new meeting buttons', async () => {
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())

  expect(screen.getByRole('textbox', { name: 'Enter a Meeting ID' })).toBeInTheDocument()
  expect(screen.getByRole('button', { name: 'Join the Meeting' })).toBeInTheDocument()
  expect(screen.getByRole('button', { name: 'Start New Meeting' })).toBeInTheDocument()
  expect(screen.getByRole('button', { name: 'Create New Meeting for Later' })).toBeInTheDocument()
  expect(screen.getByRole('button', { name: 'Unmute' })).toBeInTheDocument()
  expect(screen.getByRole('button', { name: 'Enable Video' })).toBeInTheDocument()
  expect(screen.getByRole('button', { name: 'Audio devices' })).toBeEnabled()
  expect(screen.getByRole('button', { name: 'Video devices' })).toBeDisabled()
})

test('sets room id when typing into the Meeting ID input field', async () => {
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())

  const roomIdInput = screen.getByLabelText('Enter a Meeting ID')
  userEvent.clear(roomIdInput)
  userEvent.type(roomIdInput, 'test room id')

  expect(roomIdInput).toHaveValue('test room id')
})

test('starts the local audio stream when the Unmute button is clicked', async () => {
  const { mockCreateMediaStream } = setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())

  const unmuteAudioButton = screen.getByRole('button', { name: 'Unmute' })
  userEvent.click(unmuteAudioButton)

  expect(mockCreateMediaStream).toHaveBeenCalledTimes(1)
  expect(mockCreateMediaStream).toHaveBeenCalledWith({
    audio: { deviceId: 'testMicrophone1MediaDeviceId' },
    video: false,
  })
  expect(await screen.findByRole('button', { name: 'Mute' })).toBeInTheDocument()
})

test('stops the local audio stream when the Mute button is clicked', async () => {
  const { mockLocalMediaStream: mockLocalAudioStream } = setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())
  const unmuteAudioButton = screen.getByRole('button', { name: 'Unmute' })
  userEvent.click(unmuteAudioButton)

  const muteAudioButton = await screen.findByRole('button', { name: 'Mute' })
  userEvent.click(muteAudioButton)

  expect(mockLocalAudioStream.stopStream).toHaveBeenCalledTimes(1)
  expect(screen.getByRole('button', { name: 'Unmute' })).toBeInTheDocument()
})

test('starts the local video stream when the Enable Video button is clicked', async () => {
  const { mockCreateMediaStream } = setupMockLocalMediaStream()

  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())

  const enableVideoButton = screen.getByRole('button', { name: 'Enable Video' })
  userEvent.click(enableVideoButton)

  await waitFor(() => {
    expect(mockCreateMediaStream).toHaveBeenCalledTimes(1)
  })
  expect(mockCreateMediaStream).toHaveBeenCalledWith({ audio: false, video: { deviceId: 'testCamera1MediaDeviceId' } })
  expect(await screen.findByRole('button', { name: 'Disable Video' })).toBeInTheDocument()
})

test('displays a loader for the enable video button until the local video stream is played', async () => {
  setupMockLocalMediaStream()

  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())

  const enableVideoButton = screen.getByRole('button', { name: 'Enable Video' })
  userEvent.click(enableVideoButton)

  expect(screen.getByRole('status', { busy: true })).toBeInTheDocument()
  expect(await screen.findByRole('status', { busy: true })).not.toBeInTheDocument()
})

test('stops the local video stream when the Disable Video button is clicked', async () => {
  const { mockLocalMediaStream: mockLocalVideoStream } = setupMockLocalMediaStream()

  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())
  const enableVideButton = screen.getByRole('button', { name: 'Enable Video' })
  userEvent.click(enableVideButton)

  const disableVideoButton = await screen.findByRole('button', { name: 'Disable Video' })
  userEvent.click(disableVideoButton)

  expect(mockLocalVideoStream.stopStream).toHaveBeenCalledTimes(1)
  expect(screen.getByRole('button', { name: 'Enable Video' })).toBeInTheDocument()
})

test('renders the virtual background when selecting a new virtual background', async () => {
  const { mockCreateVirtualBackground, mockVirtualBackground, mockStartVirtualBackground } =
    setupMockVirtualBackground()
  const { mockLocalMediaStream, mockLocalMediaStreamFromTrack, mockCreateMediaStreamFromTrack } =
    setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())
  userEvent.click(screen.getByRole('button', { name: 'Enable Video' }))
  expect(await screen.findByRole('button', { name: 'Disable Video' })).toBeInTheDocument()
  userEvent.click(screen.getByRole('button', { name: 'Video devices' }))
  userEvent.click(screen.getByText('Blur My Background'))

  await waitFor(() => expect(mockStartVirtualBackground).toHaveBeenCalledTimes(1))
  expect(mockCreateVirtualBackground).toHaveBeenCalledTimes(1)
  expect(mockCreateVirtualBackground).toHaveBeenCalledWith(
    VirtualBackgroundType.BLUR,
    mockLocalMediaStream.mediaStream,
    { backgroundImageUrl: undefined }
  )
  expect(mockCreateMediaStreamFromTrack).toHaveBeenCalledTimes(1)
  expect(mockCreateMediaStreamFromTrack).toHaveBeenCalledWith(
    mockVirtualBackground.outputStream,
    mockVirtualBackground.outputStreamTrack,
    StreamKind.VIDEO,
    StreamSource.CAMERA
  )
  await waitFor(() => expect(mockLocalMediaStreamFromTrack.attach).toHaveBeenCalledTimes(1))
})

test('displays a loader for the enable video button until the virtual background stream is played', async () => {
  setupMockVirtualBackground()
  setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())
  userEvent.click(screen.getByRole('button', { name: 'Enable Video' }))
  expect(await screen.findByRole('status', { busy: true })).not.toBeInTheDocument()
  userEvent.click(screen.getByRole('button', { name: 'Video devices' }))
  userEvent.click(screen.getByText('Blur My Background'))

  expect(screen.getByRole('status', { busy: true })).toBeInTheDocument()
  expect(await screen.findByRole('status', { busy: true })).not.toBeInTheDocument()
})

test('stops the virtual background when removing the background', async () => {
  const { mockStartVirtualBackground, mockStopVirtualBackground } = setupMockVirtualBackground()
  const { mockLocalMediaStream } = setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())
  const enableVideoButton = screen.getByRole('button', { name: 'Enable Video' })
  userEvent.click(enableVideoButton)
  expect(await screen.findByRole('button', { name: 'Disable Video' })).toBeInTheDocument()
  userEvent.click(screen.getByRole('button', { name: 'Video devices' }))
  userEvent.click(screen.getByText('Blur My Background'))
  await waitFor(() => expect(mockStartVirtualBackground).toHaveBeenCalledTimes(1))
  jest.clearAllMocks()

  userEvent.click(screen.getByRole('button', { name: 'Video devices' }))
  userEvent.click(screen.getByText('Unblur My Background'))

  await waitFor(() => expect(mockLocalMediaStream.attach).toHaveBeenCalledTimes(1))
  expect(mockStopVirtualBackground).toHaveBeenCalledTimes(1)
})

test('switches between video devices when selecting a new video device', async () => {
  const { mockLocalMediaStream, mockCreateMediaStream } = setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())
  const enableVideoButton = screen.getByRole('button', { name: 'Enable Video' })
  userEvent.click(enableVideoButton)
  expect(await screen.findByRole('button', { name: 'Disable Video' })).toBeInTheDocument()
  jest.clearAllMocks()

  userEvent.click(screen.getByRole('button', { name: 'Video devices' }))
  userEvent.click(screen.getByText('Test Camera 2'))

  await waitFor(() => expect(mockLocalMediaStream.attach).toHaveBeenCalledTimes(1))
  expect(mockCreateMediaStream).toHaveBeenCalledTimes(1)
  expect(mockCreateMediaStream).toHaveBeenCalledWith({ audio: false, video: { deviceId: 'testCamera2MediaDeviceId' } })
  expect(mockLocalMediaStream.stopStream).toHaveBeenCalledTimes(1)
})

test('renders a new virtual background when selecting a new video device', async () => {
  const { mockCreateVirtualBackground, mockStartVirtualBackground } = setupMockVirtualBackground()
  const { mockLocalMediaStream, mockLocalMediaStreamFromTrack, mockCreateMediaStream, mockCreateMediaStreamFromTrack } =
    setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())
  const enableVideoButton = screen.getByRole('button', { name: 'Enable Video' })
  userEvent.click(enableVideoButton)
  expect(await screen.findByRole('button', { name: 'Disable Video' })).toBeInTheDocument()
  userEvent.click(screen.getByRole('button', { name: 'Video devices' }))
  userEvent.click(screen.getByText('Blur My Background'))
  await waitFor(() => expect(mockStartVirtualBackground).toHaveBeenCalledTimes(1))
  await waitFor(() => expect(mockLocalMediaStreamFromTrack.attach).toHaveBeenCalledTimes(1))
  jest.clearAllMocks()

  userEvent.click(screen.getByRole('button', { name: 'Video devices' }))
  userEvent.click(screen.getByText('Test Camera 2'))

  await waitFor(() => expect(mockStartVirtualBackground).toHaveBeenCalledTimes(1))
  expect(mockCreateMediaStream).toHaveBeenCalledTimes(1)
  expect(mockCreateMediaStream).toHaveBeenLastCalledWith({
    audio: false,
    video: { deviceId: 'testCamera2MediaDeviceId', width: { ideal: 1280 }, height: { ideal: 720 } },
  })
  expect(mockCreateVirtualBackground).toHaveBeenCalledTimes(1)
  expect(mockCreateVirtualBackground).toHaveBeenCalledWith(
    VirtualBackgroundType.BLUR,
    mockLocalMediaStream.mediaStream,
    { backgroundImageUrl: undefined }
  )
  expect(mockCreateMediaStreamFromTrack).toHaveBeenCalledTimes(1)
})

test('renders the virtual background if it was enabled previously, and the video is turned on', async () => {
  const { mockCreateVirtualBackground, mockStartVirtualBackground } = setupMockVirtualBackground()
  const { mockLocalMediaStream, mockLocalMediaStreamFromTrack, mockCreateMediaStreamFromTrack } =
    setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn())
  userEvent.click(screen.getByRole('button', { name: 'Enable Video' }))
  expect(await screen.findByRole('button', { name: 'Disable Video' })).toBeInTheDocument()
  userEvent.click(screen.getByRole('button', { name: 'Video devices' }))
  userEvent.click(screen.getByText('Blur My Background'))
  await waitFor(() => expect(mockStartVirtualBackground).toHaveBeenCalledTimes(1))
  await waitFor(() => expect(mockLocalMediaStreamFromTrack.attach).toHaveBeenCalledTimes(1))
  userEvent.click(screen.getByRole('button', { name: 'Disable Video' }))
  await waitFor(() => expect(mockLocalMediaStreamFromTrack.detach).toHaveBeenCalledTimes(1))
  jest.clearAllMocks()

  userEvent.click(screen.getByRole('button', { name: 'Enable Video' }))

  await waitFor(() => expect(mockStartVirtualBackground).toHaveBeenCalledTimes(1))
  expect(mockCreateVirtualBackground).toHaveBeenCalledTimes(1)
  expect(mockCreateVirtualBackground).toHaveBeenCalledWith(
    VirtualBackgroundType.BLUR,
    mockLocalMediaStream.mediaStream,
    { backgroundImageUrl: undefined }
  )
  expect(mockCreateMediaStreamFromTrack).toHaveBeenCalledTimes(1)
  await waitFor(() => expect(mockLocalMediaStreamFromTrack.attach).toHaveBeenCalledTimes(1))
})

test('renders the virtual background by default when a default virtual background is given', async () => {
  const { mockCreateVirtualBackground, mockVirtualBackground, mockStartVirtualBackground } =
    setupMockVirtualBackground()
  const { mockLocalMediaStream, mockLocalMediaStreamFromTrack, mockCreateMediaStreamFromTrack } =
    setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn(), { defaultVirtualBackground: { type: VirtualBackgroundType.BLUR } })

  await waitFor(() => expect(mockStartVirtualBackground).toHaveBeenCalledTimes(1))
  expect(mockCreateVirtualBackground).toHaveBeenCalledTimes(1)
  expect(mockCreateVirtualBackground).toHaveBeenCalledWith(
    VirtualBackgroundType.BLUR,
    mockLocalMediaStream.mediaStream,
    { backgroundImageUrl: undefined }
  )
  expect(mockCreateMediaStreamFromTrack).toHaveBeenCalledTimes(1)
  expect(mockCreateMediaStreamFromTrack).toHaveBeenCalledWith(
    mockVirtualBackground.outputStream,
    mockVirtualBackground.outputStreamTrack,
    StreamKind.VIDEO,
    StreamSource.CAMERA
  )
  await waitFor(() => expect(mockLocalMediaStreamFromTrack.attach).toHaveBeenCalledTimes(1))
})

test('calls the join action with the room id, audio and video start and after join callback parameters when clicking on the Join button without any streams', async () => {
  const store = configureStore(zocksStoreConfig)
  const joinAction = jest.fn()
  await renderWithState(store, joinAction)
  const roomIdInput = screen.getByLabelText('Enter a Meeting ID')
  userEvent.clear(roomIdInput)
  userEvent.type(roomIdInput, 'test room id')

  const joinButton = screen.getByRole('button', { name: 'Join the Meeting' })
  userEvent.click(joinButton)

  expect(joinAction).toHaveBeenCalledTimes(1)
  expect(joinAction).toHaveBeenCalledWith({
    afterJoinCallback: expect.any(Function),
    joinErrorCallback: expect.any(Function),
    audioDeviceSettings: undefined,
    videoDeviceSettings: undefined,
    virtualBackground: null,
    roomId: 'test room id',
    roomName: '',
    autoCreate: false,
    autoSubscribe: true,
  })
})

test('calls the join action with the room name when the user changes the room input mode to name, and clicks the Join button', async () => {
  const store = configureStore(zocksStoreConfig)
  const joinAction = jest.fn()
  await renderWithState(store, joinAction)
  userEvent.selectOptions(screen.getByRole('combobox'), 'name')

  const roomJoinInput = screen.getByLabelText('Enter a Meeting ID')
  userEvent.clear(roomJoinInput)
  userEvent.type(roomJoinInput, 'test room name')

  const joinButton = screen.getByRole('button', { name: 'Join the Meeting' })
  userEvent.click(joinButton)

  expect(joinAction).toHaveBeenCalledTimes(1)
  expect(joinAction).toHaveBeenCalledWith({
    afterJoinCallback: expect.any(Function),
    joinErrorCallback: expect.any(Function),
    audioDeviceSettings: undefined,
    videoDeviceSettings: undefined,
    virtualBackground: null,
    roomId: '',
    roomName: 'test room name',
    autoCreate: false,
    autoSubscribe: true,
  })
})

test('calls the join action with the room name when the component is rendered with room name input mode, and the user clicks the Join button', async () => {
  const store = configureStore(zocksStoreConfig)
  const joinAction = jest.fn()
  await renderWithState(store, joinAction, { initialRoomJoinSelectValue: 'name' })

  const roomJoinInput = screen.getByLabelText('Enter a Meeting ID')
  userEvent.clear(roomJoinInput)
  userEvent.type(roomJoinInput, 'test room name')

  const joinButton = screen.getByRole('button', { name: 'Join the Meeting' })
  userEvent.click(joinButton)

  expect(joinAction).toHaveBeenCalledTimes(1)
  expect(joinAction).toHaveBeenCalledWith({
    afterJoinCallback: expect.any(Function),
    joinErrorCallback: expect.any(Function),
    audioDeviceSettings: undefined,
    videoDeviceSettings: undefined,
    virtualBackground: null,
    roomId: '',
    roomName: 'test room name',
    autoCreate: false,
    autoSubscribe: true,
  })
})

test('calls the join action with room id, audio and video start parameters when clicking on the Join button with audio and video streams', async () => {
  setupMockLocalMediaStream('testMediaDeviceId')
  const store = configureStore(zocksStoreConfig)
  const joinAction = jest.fn()
  await renderWithState(store, joinAction)
  const roomIdInput = screen.getByLabelText('Enter a Meeting ID')
  userEvent.clear(roomIdInput)
  userEvent.type(roomIdInput, 'test room id')
  const unmuteAudioButton = screen.getByRole('button', { name: 'Unmute' })
  const enableVideoButton = screen.getByRole('button', { name: 'Enable Video' })
  userEvent.click(unmuteAudioButton)
  userEvent.click(enableVideoButton)

  expect(await screen.findByRole('button', { name: 'Disable Video' })).toBeInTheDocument()
  const joinButton = screen.getByRole('button', { name: 'Join the Meeting' })
  userEvent.click(joinButton)

  expect(joinAction).toHaveBeenCalledTimes(1)
  expect(joinAction).toHaveBeenCalledWith({
    afterJoinCallback: expect.any(Function),
    joinErrorCallback: expect.any(Function),
    audioDeviceSettings: { deviceId: 'testMicrophone1MediaDeviceId' },
    videoDeviceSettings: { deviceId: 'testCamera1MediaDeviceId' },
    virtualBackground: null,
    roomId: 'test room id',
    roomName: '',
    autoCreate: false,
    autoSubscribe: true,
  })
})

test('calls the join action without a room id and room name when clicking on the Start New Meeting button', async () => {
  const store = configureStore(zocksStoreConfig)
  const joinAction = jest.fn()
  await renderWithState(store, joinAction)

  const newMeetingButton = screen.getByRole('button', { name: 'Start New Meeting' })
  userEvent.click(newMeetingButton)

  expect(joinAction).toHaveBeenCalledTimes(1)
  expect(joinAction).toHaveBeenCalledWith({
    afterJoinCallback: expect.any(Function),
    joinErrorCallback: expect.any(Function),
    audioDeviceSettings: undefined,
    videoDeviceSettings: undefined,
    virtualBackground: null,
    roomId: '',
    roomName: '',
    autoCreate: true,
    autoSubscribe: true,
  })
})

test('calls the join action with overridden parameters when join options are given', async () => {
  const store = configureStore(zocksStoreConfig)
  const joinAction = jest.fn()
  await renderWithState(store, joinAction, { joinOptions: { autoSubscribe: false } })

  const newMeetingButton = screen.getByRole('button', { name: 'Start New Meeting' })
  userEvent.click(newMeetingButton)

  expect(joinAction).toHaveBeenCalledTimes(1)
  expect(joinAction).toHaveBeenCalledWith({
    afterJoinCallback: expect.any(Function),
    joinErrorCallback: expect.any(Function),
    audioDeviceSettings: undefined,
    videoDeviceSettings: undefined,
    virtualBackground: null,
    roomId: '',
    roomName: '',
    autoCreate: true,
    autoSubscribe: false,
  })
})

test('calls the create room action when clicking on the Create New Meeting for Later button, which calls the callback if provided when the room is created', async () => {
  const mockRoomInfo = mock<RoomInfo>()
  const createRoomAction = jest.fn().mockResolvedValue(mockRoomInfo)
  const onNewRoomCreated = jest.fn()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn(), { createRoomAction, onNewRoomCreated })

  const createMeetingButton = screen.getByRole('button', { name: 'Create New Meeting for Later' })
  await userEvent.click(createMeetingButton)

  expect(createRoomAction).toHaveBeenCalledTimes(1)
  await waitFor(() => expect(onNewRoomCreated).toHaveBeenCalledTimes(1))
  expect(onNewRoomCreated).toHaveBeenCalledWith(mockRoomInfo)
})

test('the join and new meeting buttons are disabled if the participant is already connecting', async () => {
  const store = configureStore(zocksStoreConfig)
  store.dispatch(setConnecting(true))
  await renderWithState(store, jest.fn())

  expect(screen.getByRole('button', { name: 'Join the Meeting' })).toBeDisabled()
  expect(screen.getByRole('button', { name: 'Start New Meeting' })).toBeDisabled()
  expect(screen.getByRole('button', { name: 'Create New Meeting for Later' })).toBeDisabled()
})

test('the New Meeting button is disabled if set', async () => {
  const store = configureStore(zocksStoreConfig)
  render(
    <Provider store={store}>
      <JoinPage
        joinAction={jest.fn()}
        createRoomAction={jest.fn()}
        initialRoomJoinInputValue='testInitialRoomId'
        newMeetingDisabled={true}
      />
    </Provider>
  )
  expect(await screen.findByRole('button', { name: 'Audio devices' })).toBeInTheDocument()

  expect(screen.getByRole('button', { name: 'Start New Meeting' })).toBeDisabled()
})

test('the New Meeting button and Create later is hidden if action is set to HIDE and user scope missing', async () => {
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn(), { missingScopeAction: MissingScopeAction.HIDE }, false)

  expect(screen.queryByRole('button', { name: 'Start New Meeting' })).not.toBeInTheDocument()
  expect(screen.queryByRole('button', { name: 'Create New Meeting for Later' })).not.toBeInTheDocument()
})

test('the New Meeting button and Create later is disabled if user scope missing and missing action is disable', async () => {
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn(), {}, false)

  expect(screen.queryByRole('button', { name: 'Start New Meeting' })).toBeDisabled()
  expect(screen.queryByRole('button', { name: 'Create New Meeting for Later' })).toBeDisabled()
})

test('the room id field is filled if the initial room id is given', async () => {
  const store = configureStore(zocksStoreConfig)
  render(
    <Provider store={store}>
      <JoinPage joinAction={jest.fn()} createRoomAction={jest.fn()} initialRoomJoinInputValue='testInitialRoomId' />
    </Provider>
  )
  expect(await screen.findByRole('button', { name: 'Audio devices' })).toBeInTheDocument()

  expect(screen.getByRole('textbox', { name: 'Enter a Meeting ID' })).toHaveValue('testInitialRoomId')
})

test('automatically starts the local audio stream if the audio is enabled by default', async () => {
  const { mockCreateMediaStream } = setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn(), { audioEnabledByDefault: true })

  expect(mockCreateMediaStream).toHaveBeenCalledTimes(1)
  expect(mockCreateMediaStream).toHaveBeenCalledWith({ audio: { deviceId: undefined }, video: false })
  expect(await screen.findByRole('button', { name: 'Mute' })).toBeInTheDocument()
})

test('automatically starts the local video stream if the video is enabled by default', async () => {
  const { mockCreateMediaStream } = setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  await renderWithState(store, jest.fn(), { videoEnabledByDefault: true })

  expect(mockCreateMediaStream).toHaveBeenCalledTimes(1)
  expect(mockCreateMediaStream).toHaveBeenCalledWith({ audio: false, video: { deviceId: undefined } })
  expect(await screen.findByRole('button', { name: 'Disable Video' })).toBeInTheDocument()
})

test('pressing the Enter key calls the join action if there is a meeting id entered', async () => {
  const store = configureStore(zocksStoreConfig)
  const joinAction = jest.fn()
  await renderWithState(store, joinAction)
  const roomIdInput = screen.getByLabelText('Enter a Meeting ID')
  userEvent.clear(roomIdInput)
  userEvent.type(roomIdInput, 'test room id')

  userEvent.type(roomIdInput, '{enter}')

  expect(joinAction).toHaveBeenCalledTimes(1)
  expect(joinAction).toHaveBeenCalledWith({
    afterJoinCallback: expect.any(Function),
    joinErrorCallback: expect.any(Function),
    audioDeviceSettings: undefined,
    videoDeviceSettings: undefined,
    virtualBackground: null,
    roomId: 'test room id',
    roomName: '',
    autoCreate: false,
    autoSubscribe: true,
  })
})

test('pressing the Enter key does not call the join action if there is no meeting id', async () => {
  const store = configureStore(zocksStoreConfig)
  const joinAction = jest.fn()
  await renderWithState(store, joinAction)
  const roomIdInput = screen.getByLabelText('Enter a Meeting ID')
  userEvent.clear(roomIdInput)

  userEvent.type(roomIdInput, '{enter}')

  expect(joinAction).toHaveBeenCalledTimes(0)
})

test('Join Page considers custom auth actions', async () => {
  const store = configureStore(zocksStoreConfig)
  const mockAuthActions = { hasScope: jest.fn().mockReturnValue(false) }
  await renderWithState(
    store,
    jest.fn(),
    { missingScopeAction: MissingScopeAction.HIDE, authActions: mockAuthActions },
    true
  )

  expect(screen.queryByRole('button', { name: 'Start New Meeting' })).not.toBeInTheDocument()
  expect(screen.queryByRole('button', { name: 'Create New Meeting for Later' })).not.toBeInTheDocument()
})

test('stops local audio stream after unmounting', async () => {
  const { mockCreateMediaStream, mockLocalMediaStream } = setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  const { unmount } = await renderWithState(store, jest.fn())

  const unmuteAudioButton = screen.getByRole('button', { name: 'Unmute' })
  userEvent.click(unmuteAudioButton)
  unmount()

  expect(mockCreateMediaStream).toHaveBeenCalledTimes(1)
  expect(mockCreateMediaStream).toHaveBeenCalledWith({
    audio: { deviceId: 'testMicrophone1MediaDeviceId' },
    video: false,
  })
  await waitFor(() => {
    expect(mockLocalMediaStream.stopStream).toHaveBeenCalledTimes(1)
  })
})

test('stops local video stream after unmounting', async () => {
  const { mockCreateMediaStream, mockLocalMediaStream } = setupMockLocalMediaStream()
  const store = configureStore(zocksStoreConfig)
  const { unmount } = await renderWithState(store, jest.fn())

  const unmuteAudioButton = screen.getByRole('button', { name: 'Enable Video' })
  userEvent.click(unmuteAudioButton)
  unmount()

  await waitFor(() => {
    expect(mockCreateMediaStream).toHaveBeenCalledTimes(1)
  })
  expect(mockCreateMediaStream).toHaveBeenCalledWith({
    audio: false,
    video: { deviceId: 'testCamera1MediaDeviceId' },
  })
  await waitFor(() => {
    expect(mockLocalMediaStream.stopStream).toHaveBeenCalledTimes(1)
  })
})
