import React from 'react'
import { List } from 'immutable'

import {find, mount, stub, within, wait, waitForExpect, expect, accessible} from '@instructure/ui-test-utils'

import Sidebar from '../index'

describe('Common Sidebar Presenter', () => {
  let props, scrollToItemStub, toggleSidebarStub
  beforeEach(() => {
    scrollToItemStub = stub()
    toggleSidebarStub = stub()
    props = {
      children: [<div key="1">child1</div>, <div key="2">child2</div>],
      scrollToItem: () => scrollToItemStub,
      sidebarOpen: true,
      screenreaderNotification: Function.prototype,
      toggleSidebar: toggleSidebarStub,
      pinnedItems: List([{ sessionItemId: '11', position: 1 }])
    }
  })

  it('displays children', async () => {
    const subject = await mount(<Sidebar {...props} />)
    const text = within(subject.getDOMNode()).getTextContent()
    expect(text).to.contain('child1')
    expect(text).to.contain('child2')
  })

  it('passes the role prop to the wrapper div', async () => {
    const role = 'complementary'
    await mount(<Sidebar {...props} role={role} />)

    const wrapperDiv = await find(`[role="${role}"]`)

    expect(wrapperDiv).to.exist()
  })

  describe('#scrollToTitle', () => {
    it('calls the sidebarActions#scrollToItem with title', async () => {
      await mount(<Sidebar {...props} />)

      const titleButton = await find(':label(Scroll to title)', {visible: false})
      await titleButton.click()

      expect(scrollToItemStub.getCall(0).args[0]).to.eq('title')
    })
  })

  describe('#scrollToInfo', () => {
    it('calls the sidebarActions#scrollToItem with info', async () => {
      await mount(<Sidebar {...props} />)

      const instructionsButton = await find(':label(Scroll to instructions)', {visible: false})
      await instructionsButton.click()

      expect(scrollToItemStub.getCall(0).args[0]).to.eq('instructions')
    })
  })

  describe('sidebar handleBlur', () => {
    it('should close the sidebar on close on blur is enabled', async () => {
      const role = 'complementary'
      await mount(<Sidebar {...props} role={role} sidebarOpen shouldCloseOnBlur />)

      const wrapperDiv = await find(`[role="${role}"]`)
      await wrapperDiv.blur()
      await wait()

      expect(toggleSidebarStub).to.have.been.called()
    })

    it('should keep the sidebar open when close on blur is not enabled', async () => {
      const role = 'complementary'
      await mount(<Sidebar {...props} role={role} sidebarOpen shouldCloseOnBlur={false} />)

      const wrapperDiv = await find(`[role="${role}"]`)
      await wrapperDiv.blur()
      await wait()

      expect(toggleSidebarStub).to.have.not.been.called()
    })

    it('should not toggle the sidebar if the sidebar was closed', async () => {
      const role = 'complementary'
      await mount(<Sidebar {...props} role={role} sidebarOpen={false} shouldCloseOnBlur />)

      const wrapperDiv = await find(`[role="${role}"]`)
      await wrapperDiv.blur()
      await wait()

      expect(toggleSidebarStub).to.have.not.been.called()
    })
  })

  describe('sidebar handleTransitionEnd', () => {
    let resizeStub
    beforeEach(() => {
      resizeStub = stub()
      window.addEventListener('resize', resizeStub)
    })

    afterEach(() => {
      window.removeEventListener('resize', resizeStub)
    })

    it('fires a window resize event on transition end', async () => {
      const subject = await mount(<Sidebar {...props} sidebarOpen={false} />)

      await subject.setProps({
        sidebarOpen: true
      })

      await waitForExpect(() => expect(resizeStub).to.have.been.called())
    })
  })

  describe('header', () => {
    context('open', () => {
      it("shows 'Question Navigator' text", async () => {
        const subject = await mount(<Sidebar {...props} />)
        const text = within(subject.getDOMNode()).getTextContent()
        expect(text).to.include('Question Navigator')
      })

      it('renders the correct button text', async () => {
        const subject = await mount(<Sidebar {...props} />)
        const text = within(subject.getDOMNode()).getTextContent()
        expect(text).to.include('Close Menu')
      })
    })

    context('closed', () => {
      it("hides 'Question Navigator' text", async () => {
        const subject = await mount(<Sidebar {...props} sidebarOpen={false} />)
        const text = within(subject.getDOMNode()).getTextContent()
        expect(text).to.include('Question Navigator')
      })

      it('renders the correct button text', async () => {
        const subject = await mount(<Sidebar {...props} sidebarOpen={false} />)
        const text = within(subject.getDOMNode()).getTextContent()
        expect(text).to.include('Open Menu')
      })
    })

    it('calls toggle sidebar when menu button clicked', async () => {
      await mount(<Sidebar {...props} sidebarOpen />)
      const toggleButton = await find(':label(Close Menu)', {visible: false})
      await toggleButton.click()
      expect(toggleSidebarStub).to.have.been.called()
    })
  })

  describe('total points possible', () => {
    it('does not render when missing', async () => {
      const subject = await mount(<Sidebar {...props} />)
      const text = within(subject.getDOMNode()).getTextContent()
      expect(text).not.to.include('pts')
    })

    it('renders when provided', async () => {
      const subject = await mount(<Sidebar {...props} totalPointsPossible={42} />)
      const text = within(subject.getDOMNode()).getTextContent()
      expect(text).to.include('42 pts')
    })
  })

  describe('title', () => {
    it("renders the string 'Title' if no prop is given", async () => {
      const subject = await mount(<Sidebar {...props} />)
      const text = within(subject.getDOMNode()).getTextContent()
      expect(text).to.include('Title')
    })

    it('shows the title prop if given', async () => {
      const subject = await mount(<Sidebar {...props} title="hola" />)
      const text = within(subject.getDOMNode()).getTextContent()
      expect(text).to.include('hola')
      expect(text).not.to.include('Title')
    })
  })

  describe('shows instructions', () => {
    it("Renders 'Instructions' in sidebar", async () => {
      const subject = await mount(<Sidebar {...props} taking />)
      const text = within(subject.getDOMNode()).getTextContent()
      expect(text).to.include('Instructions')
    })

    it('shows instructions by default', async () => {
      const subject = await mount(<Sidebar {...props} />)
      const text = within(subject.getDOMNode()).getTextContent()
      expect(text).to.include('Instructions')
    })
  })

  describe('pinned section', () => {
    context('taking is true', () => {
      it('renders the title of the section', async () => {
        const subject = await mount(<Sidebar {...props} taking />)
        const text = within(subject.getDOMNode()).getTextContent()
        expect(text).to.include('Pinned Questions')
      })

      it('renders the proper number of pins', async () => {
        await mount(<Sidebar {...props} taking />)
        const questionOnePin = await find(':label(Question 1)')
        expect(questionOnePin).to.exist()
      })

      it('calls the scrollToItem stub when clicked', async () => {
        await mount(<Sidebar {...props} taking />)
        const questionOnePin = await find(':label(Question 1)')
        await questionOnePin.click()
        expect(scrollToItemStub).to.be.calledWith('11')
      })
    })

    context('taking is not true', () => {
      it('does not render the title of the section', async () => {
        const subject = await mount(<Sidebar {...props} />)
        const text = within(subject.getDOMNode()).getTextContent()
        expect(text).to.not.include('Pinned Questions')
      })
    })
  })

  describe('a11y tests', () => {
    it('should meet a11y standards', async () => {
      await mount(<Sidebar {...props} />)
      expect(await accessible()).to.be.true()
    })
  })
})
