import { describe, expect, it, vi } from 'vite-plus/test'
import { screen, waitFor } from '@testing-library/vue'
import { createHarness } from '@/__tests__/TestHarness'
import { assertOpenModal } from '@/__tests__/assertions'
import { MessageToasterStub } from '@/__tests__/stubs'
import { playlistStore } from '@/stores/playlistStore'
import { playableStore } from '@/stores/playableStore'
import { playbackService } from '@/services/QueuePlaybackService'
import Router from '@/router'
import { playlistFolderStore } from '@/stores/playlistFolderStore'
import EditPlaylistFolderForm from '@/components/playlist/EditPlaylistFolderForm.vue'

const openModalMock = vi.fn()

vi.mock('@/composables/useModal', () => ({
  useModal: () => ({
    openModal: openModalMock,
  }),
}))

import Component from './PlaylistFolderContextMenu.vue'

describe('playlistFolderContextMenu.vue', () => {
  const h = createHarness({
    beforeEach: () => openModalMock.mockClear(),
  })

  const renderComponent = async (folder?: PlaylistFolder) => {
    folder = folder || h.factory('playlist-folder').make()

    const rendered = h.render(Component, {
      props: {
        folder,
      },
    })

    return {
      ...rendered,
      folder,
    }
  }

  const createPlayableFolder = () => {
    const folder = h.factory('playlist-folder').make()
    h.mock(playlistStore, 'byFolder', h.factory('playlist').make({ folder_id: folder.id }, 3))
    return folder
  }

  it('renames', async () => {
    const { folder } = await renderComponent()

    await h.user.click(screen.getByText('Rename'))

    await assertOpenModal(openModalMock, EditPlaylistFolderForm, { folder })
  })

  it('deletes', async () => {
    const { folder } = await renderComponent()
    const deleteMock = h.mock(playlistFolderStore, 'delete')

    await h.user.click(screen.getByText('Delete'))
    expect(deleteMock).toHaveBeenCalledWith(folder)
  })

  it('plays', async () => {
    h.createAudioPlayer()

    const songs = h.factory('song').make(3)
    const fetchMock = h.mock(playableStore, 'fetchForPlaylistFolder').mockResolvedValue(songs)
    const queueMock = h.mock(playbackService, 'queueAndPlay')
    const goMock = h.mock(Router, 'go')
    const { folder } = await renderComponent(createPlayableFolder())

    await h.user.click(screen.getByText('Play All'))

    await waitFor(() => {
      expect(fetchMock).toHaveBeenCalledWith(folder)
      expect(queueMock).toHaveBeenCalledWith(songs)
      expect(goMock).toHaveBeenCalledWith('/#/queue')
    })
  })

  it('warns if attempting to play with no songs in folder', async () => {
    h.createAudioPlayer()

    const fetchMock = h.mock(playableStore, 'fetchForPlaylistFolder').mockResolvedValue([])
    const queueMock = h.mock(playbackService, 'queueAndPlay')
    const goMock = h.mock(Router, 'go')
    const warnMock = h.mock(MessageToasterStub.value, 'warning')

    const { folder } = await renderComponent(createPlayableFolder())

    await h.user.click(screen.getByText('Play All'))

    await waitFor(() => {
      expect(fetchMock).toHaveBeenCalledWith(folder)
      expect(queueMock).not.toHaveBeenCalled()
      expect(goMock).not.toHaveBeenCalled()
      expect(warnMock).toHaveBeenCalledWith('No songs available.')
    })
  })

  it('shuffles', async () => {
    h.createAudioPlayer()

    const songs = h.factory('song').make(3)
    const fetchMock = h.mock(playableStore, 'fetchForPlaylistFolder').mockResolvedValue(songs)
    const queueMock = h.mock(playbackService, 'queueAndPlay')
    const goMock = h.mock(Router, 'go')

    const { folder } = await renderComponent(createPlayableFolder())

    await h.user.click(screen.getByText('Shuffle All'))

    await waitFor(() => {
      expect(fetchMock).toHaveBeenCalledWith(folder)
      expect(queueMock).toHaveBeenCalledWith(songs, true)
      expect(goMock).toHaveBeenCalledWith('/#/queue')
    })
  })

  it('does not show shuffle option if folder is empty', async () => {
    await renderComponent()

    expect(screen.queryByText('Shuffle All')).toBeNull()
    expect(screen.queryByText('Play All')).toBeNull()
  })

  it('warns if attempting to shuffle with no songs in folder', async () => {
    h.createAudioPlayer()

    const fetchMock = h.mock(playableStore, 'fetchForPlaylistFolder').mockResolvedValue([])
    const queueMock = h.mock(playbackService, 'queueAndPlay')
    const goMock = h.mock(Router, 'go')
    const warnMock = h.mock(MessageToasterStub.value, 'warning')

    const { folder } = await renderComponent(createPlayableFolder())

    await h.user.click(screen.getByText('Shuffle All'))

    await waitFor(() => {
      expect(fetchMock).toHaveBeenCalledWith(folder)
      expect(queueMock).not.toHaveBeenCalled()
      expect(goMock).not.toHaveBeenCalled()
      expect(warnMock).toHaveBeenCalledWith('No songs available.')
    })
  })
})
