import ReactDOM from 'react-dom'
import { List } from 'immutable'

import Menu from '@instructure/ui-menu/lib/components/Menu'
import MenuItem from '@instructure/ui-menu/lib/components/Menu/MenuItem'
import ScreenReaderContent from '@instructure/ui-a11y/lib/components/ScreenReaderContent'
import IconDragHandleLine from '@instructure/ui-icons/lib/Line/IconDragHandle'
import Button from '@instructure/ui-buttons/lib/components/Button'

import DragHandle from '../presenter'
import Item from '../../../../../../common/records/Item'
import QuizEntry from '../../../../../../common/records/QuizEntry'
import { TEMPORARY_QUIZ_ENTRY_ID } from '@instructure/quiz-common/lib/constants'

describe('DragHandle', () => {
  const stubGetEntry = (quizEntry, status) => {
    sinon.stub(quizEntry, 'getEntry').returns(new Item({
      status: status || 'mutable'
    }))
    return quizEntry
  }
  const tempQuizEntry = new QuizEntry({id: TEMPORARY_QUIZ_ENTRY_ID})
  const quizEntry1 = stubGetEntry(new QuizEntry({position: 1}))
  const quizEntry2 = new QuizEntry({position: 2})
  sinon.stub(quizEntry2, 'getEntry').returns(new Item({id: '22', itemBody: '<div>Hello</div>'}))
  const quizEntry3 = stubGetEntry(new QuizEntry({position: 3}))

  const stimulusQuizEntry1 = stubGetEntry(new QuizEntry({id: 5, entryType: 'Stimulus', position: 1}))
  const stimulusQuizEntry2 = stubGetEntry(new QuizEntry({id: 6, entryType: 'Stimulus', position: 2}))
  const stimulusQuizEntry3 = stubGetEntry(new QuizEntry({id: 7, entryType: 'Stimulus', position: 3}))
  const quizEntryInStimulus1 = stubGetEntry(new QuizEntry({stimulusQuizEntryId: 5, position: 2}))
  const quizEntryInStimulus2 = stubGetEntry(new QuizEntry({stimulusQuizEntryId: 6, position: 2}))
  sinon.stub(stimulusQuizEntry1, 'getNestedEntries').returns(List([1]))
  sinon.stub(stimulusQuizEntry2, 'getNestedEntries').returns(List([1]))
  const immutableQuizEntryInStimulus1 = stubGetEntry(new QuizEntry({
    entryType: 'Item',
    stimulusQuizEntry: 5,
    position: 2
  }), 'immutable')

  const SRNotificationStub = sinon.stub()
  const moveQuizEntryStub = sinon.stub()
  const setUIStub = sinon.stub()
  const clearUIStub = sinon.stub()
  const openMoveImmutableModalStub = sinon.stub()

  const defaultProps = {
    clearUI: clearUIStub,
    disabled: false,
    displayPosition: 1,
    onSidebar: false,
    quizEntries: List([quizEntry1, quizEntry2, quizEntry3]),
    quizEntry: quizEntry2,
    quizId: '1',
    screenreaderNotification: SRNotificationStub,
    setUI: setUIStub,
    openMoveImmutableModal: openMoveImmutableModalStub,
    moveQuizEntry: moveQuizEntryStub,
    moveTemporaryQuizEntry: moveQuizEntryStub
  }

  const testbed = createTestbed(DragHandle, defaultProps)

  afterEach(() => {
    moveQuizEntryStub.resetHistory()
    SRNotificationStub.resetHistory()
    setUIStub.resetHistory()
    clearUIStub.resetHistory()
    openMoveImmutableModalStub.resetHistory()
  })

  shouldRenderComponent(testbed, {}, DragHandle)

  describe('#conditionalFocus', () => {
    it('focuses the trigger when movedQuizEntry matches quizEntry id', () => {
      testbed.render({
        movedQuizEntry: defaultProps.quizEntry.id,
        movedInSidebar: defaultProps.onSidebar
      })
      const trigger = ReactDOM.findDOMNode(testbed.findChildByType(Button))
      expect(document.activeElement).to.equal(trigger)
    })

    it('calls clearUI after focusing', () => {
      testbed.render({
        movedQuizEntry: defaultProps.quizEntry.id,
        movedInSidebar: defaultProps.onSidebar
      })
      expect(clearUIStub).to.have.been.called()
    })
  })

  describe('#renderIcon', () => {
    it('returns an IconDragHandleLine component', () => {
      testbed.render()
      const iconComponents = testbed.findChildByType(IconDragHandleLine)
      expect(iconComponents).to.not.be.empty()
    })

    it('returns a ScreenReaderContent component', () => {
      testbed.render()
      const screenReaderComponents = testbed.findChildByType(ScreenReaderContent)
      expect(screenReaderComponents).to.not.be.empty()
    })
  })

  describe('#renderMenuItems', () => {
    context('when isFirstItem and isLastItem are both true', () => {
      it('does not render Menu', () => {
        testbed.render({
          quizEntry: new QuizEntry({position: 1}),
          quizEntries: List([1])
        })
        const popoverMenu = testbed.findChildrenByType(Menu)
        expect(popoverMenu).to.be.empty()
      })
    })

    context('when disabled', () => {
      it('does not render Menu', () => {
        testbed.render({disabled: true})
        const popoverMenu = testbed.findChildrenByType(Menu)
        expect(popoverMenu).to.be.empty()
      })
    })

    context('when isFirstItem and isLastItem are not both true', () => {
      it('renders popovermenu', () => {
        testbed.render({
          quizEntry: new QuizEntry({position: 1}),
          quizEntries: List([1, 2])
        })
        const popoverMenu = testbed.findChildrenByType(Menu)
        expect(popoverMenu).to.not.be.empty()
      })

      it('renders all move menu items', () => {
        testbed.render({
          quizEntry: new QuizEntry({position: 2}),
          quizEntries: List([1, 2, 3])
        })
        expect(testbed.findChildByType(DragHandle).renderMenuItems().length).to.eq(4)
      })

      context('is the first item', () => {
        it('renders only two menu items', () => {
          testbed.render({
            quizEntry: new QuizEntry({position: 1}),
            quizEntries: List([1, 2])
          })
          const popoverMenuItems = testbed.findChildByType(DragHandle).renderMenuItems()
          expect(popoverMenuItems.length).to.eq(2)
        })

        it('menu items only contain down as option', () => {
          testbed.render({
            quizEntry: new QuizEntry({position: 1}),
            quizEntries: List([1, 2])
          })
          const popoverMenuItems = testbed.findChildByType(DragHandle).renderMenuItems()
          expect(popoverMenuItems[0].props.children).to.include('Move Down')
          expect(popoverMenuItems[1].props.children).to.include('Move to Bottom')
        })
      })

      context('is the last item', () => {
        it('renders only two menu items', () => {
          testbed.render({
            quizEntry: new QuizEntry({position: 2}),
            quizEntries: List([1, 2])
          })
          const popoverMenuItems = testbed.findChildByType(DragHandle).renderMenuItems()
          expect(popoverMenuItems.length).to.eq(2)
        })

        it('menu items only contain up as option', () => {
          testbed.render({
            quizEntry: new QuizEntry({position: 2}),
            quizEntries: List([1, 2])
          })
          const popoverMenuItems = testbed.findChildByType(DragHandle).renderMenuItems()

          expect(popoverMenuItems[0].props.children).to.include('Move to Top')
          expect(popoverMenuItems[1].props.children).to.include('Move Up')
        })
      })
    })

    it('returns an array of MenuItems', () => {
      const output = testbed.renderShallowly()
      expect(output.find(MenuItem).length).to.eq(4)
    })
  })

  describe('#renderConditionally', () => {
    context('when isFirstItem and isLastItem are not both true', () => {
      context('disabled set to false', () => {
        it('renders Menu', () => {
          testbed.render({
            disabled: false,
            quizEntry: new QuizEntry({position: 1}),
            quizEntries: List([1, 2])
          })
          const popoverMenu = testbed.findChildByType(Menu)
          expect(popoverMenu).to.not.be.undefined()
        })
      })

      context('disabled set to true', () => {
        it('does not render Menu', () => {
          testbed.render({
            disabled: true,
            quizEntry: new QuizEntry({position: 1}),
            quizEntries: List([1])
          })
          const popoverMenu = testbed.findChildrenByType(Menu)
          expect(popoverMenu).to.be.empty()
        })
      })
    })

    context('when isFirstItem and isLastItem are both true', () => {
      context('disabled set to false', () => {
        it('does not render Menu', () => {
          testbed.render({
            disabled: false,
            quizEntry: new QuizEntry({position: 1}),
            quizEntries: List([1])
          })
          const popoverMenu = testbed.findChildrenByType(Menu)
          expect(popoverMenu).to.be.empty()
        })
      })

      context('disabled set to true', () => {
        it('does not render Menu', () => {
          testbed.render({
            disabled: true,
            quizEntry: new QuizEntry({position: 1}),
            quizEntries: List([1])
          })
          const popoverMenu = testbed.findChildrenByType(Menu)
          expect(popoverMenu).to.be.empty()
        })
      })
    })
  })

  describe('#moveUp', () => {
    it('changes the item position by -1', () => {
      testbed.render()
      const subject = testbed.findChildByType(DragHandle)
      subject.moveUp()
      expect(moveQuizEntryStub.getCall(0).args[1]).to.eq(1)
    })

    it('moves out of stimulus if at the top', () => {
      testbed.render({
        quizEntry: quizEntryInStimulus1,
        quizEntries: List([stimulusQuizEntry1, quizEntryInStimulus1])
      })
      const subject = testbed.findChildByType(DragHandle)
      subject.moveUp()
      expect(moveQuizEntryStub.firstCall.args[1]).to.eq(1)
      expect(moveQuizEntryStub.firstCall.args[2]).to.eq('')
    })

    it('moves into stimulus if next item is in stimulus', () => {
      testbed.render({
        quizEntry: quizEntry3,
        quizEntries: List([stimulusQuizEntry1, quizEntryInStimulus1, quizEntry3])
      })
      const subject = testbed.findChildByType(DragHandle)
      subject.moveUp()
      expect(moveQuizEntryStub.firstCall.args[1]).to.eq(3)
      expect(moveQuizEntryStub.firstCall.args[2]).to.eq(5)
    })

    it('moves into stimulus if next item is empty stimulus', () => {
      testbed.render({
        quizEntry: quizEntry2,
        quizEntries: List([stimulusQuizEntry1, quizEntry2])
      })
      const subject = testbed.findChildByType(DragHandle)
      subject.moveUp()
      expect(moveQuizEntryStub.firstCall.args[1]).to.eq(2)
      expect(moveQuizEntryStub.firstCall.args[2]).to.eq(5)
    })

    context('moving stimulus', () => {
      it('moves around other empty stimulus', () => {
        testbed.render({
          quizEntry: stimulusQuizEntry2,
          quizEntries: List([stimulusQuizEntry1, stimulusQuizEntry2])
        })
        const subject = testbed.findChildByType(DragHandle)
        subject.moveUp()
        expect(moveQuizEntryStub.firstCall.args[1]).to.eq(1)
      })

      it('moves around other stimulus with item', () => {
        testbed.render({
          quizEntry: stimulusQuizEntry3,
          quizEntries: List([stimulusQuizEntry1, quizEntryInStimulus1, stimulusQuizEntry3])
        })
        const subject = testbed.findChildByType(DragHandle)
        subject.moveUp()
        expect(moveQuizEntryStub.firstCall.args[1]).to.eq(1)
      })
    })
  })

  describe('#moveDown', () => {
    it('changes the item position by +1', () => {
      testbed.render()
      const subject = testbed.findChildByType(DragHandle)
      subject.moveDown()
      expect(moveQuizEntryStub.getCall(0).args[1]).to.eq(3)
    })

    it('moves out of stimulus if at the bottom with no question after', () => {
      testbed.render({
        quizEntry: quizEntryInStimulus1,
        quizEntries: List([stimulusQuizEntry1, quizEntryInStimulus1])
      })
      const subject = testbed.findChildByType(DragHandle)
      subject.moveDown()
      expect(moveQuizEntryStub.firstCall.args[1]).to.eq(2)
      expect(moveQuizEntryStub.firstCall.args[2]).to.eq('')
    })

    it('moves out of stimulus if at the bottom with question after', () => {
      testbed.render({
        quizEntry: quizEntryInStimulus1,
        quizEntries: List([stimulusQuizEntry1, quizEntryInStimulus1, quizEntry3])
      })
      const subject = testbed.findChildByType(DragHandle)
      subject.moveDown()
      expect(moveQuizEntryStub.firstCall.args[1]).to.eq(2)
      expect(moveQuizEntryStub.firstCall.args[2]).to.eq('')
    })

    it('moves into stimulus if next item is stimulus', () => {
      testbed.render({
        quizEntry: quizEntry1,
        quizEntries: List([quizEntry1, stimulusQuizEntry2])
      })
      const subject = testbed.findChildByType(DragHandle)
      subject.moveDown()
      expect(moveQuizEntryStub.firstCall.args[1]).to.eq(2)
      expect(moveQuizEntryStub.firstCall.args[2]).to.eq(6)
    })

    context('moving stimulus', () => {
      it('moves around other empty stimulus', () => {
        testbed.render({
          quizEntry: stimulusQuizEntry1,
          quizEntries: List([stimulusQuizEntry1, stimulusQuizEntry2])
        })
        const subject = testbed.findChildByType(DragHandle)
        subject.moveDown()
        expect(moveQuizEntryStub.firstCall.args[1]).to.eq(2)
      })

      it('moves around other stimulus with item', () => {
        testbed.render({
          quizEntry: stimulusQuizEntry1,
          quizEntries: List([stimulusQuizEntry1, stimulusQuizEntry2, quizEntryInStimulus2])
        })
        const subject = testbed.findChildByType(DragHandle)
        subject.moveDown()
        expect(moveQuizEntryStub.firstCall.args[1]).to.eq(3)
      })
    })
  })

  describe('#moveToTop', () => {
    it('changes the item position to 1', () => {
      testbed.render()
      const subject = testbed.findChildByType(DragHandle)
      subject.moveToTop()
      expect(moveQuizEntryStub.getCall(0).args[1]).to.eq(1)
    })

    context('with temporary item', () => {
      it('changes the item position to 1', () => {
        testbed.render({quizEntry: tempQuizEntry})
        const subject = testbed.findChildByType(DragHandle)
        subject.moveToTop()
        expect(moveQuizEntryStub.getCall(0).args[0]).to.eq(1)
      })
    })
  })

  describe('#moveToBottom', () => {
    it('changes the item position to last position', () => {
      testbed.render()
      const subject = testbed.findChildByType(DragHandle)
      subject.moveToBottom()
      expect(moveQuizEntryStub.getCall(0).args[1]).to.eq(3)
    })

    context('with temporary item', () => {
      it('changes the item position to last position', () => {
        testbed.render({quizEntry: tempQuizEntry})
        const subject = testbed.findChildByType(DragHandle)
        subject.moveToBottom()
        expect(moveQuizEntryStub.getCall(0).args[0]).to.eq(3)
      })
    })
  })

  describe('#moveQuizEntry', () => {
    context('with an immutable item', () => {
      const moveImmutable = () => {
        testbed.render({
          quizEntry: immutableQuizEntryInStimulus1,
          quizEntries: List([stimulusQuizEntry1, immutableQuizEntryInStimulus1])
        })
        const subject = testbed.findChildByType(DragHandle)
        subject.moveToTop()
      }
      it('sets move immutable data correctly', () => {
        moveImmutable()
        expect(setUIStub.firstCall.args[1].toJS()).to.deep.eq({
          movedInSidebar: false,
          quizId: '1',
          position: 1,
          stimulusQuizEntryId: '',
          quizEntryId: '0'
        })
      })

      it('opens the moveImmutable modal', () => {
        moveImmutable()
        expect(openMoveImmutableModalStub).to.be.called()
      })

      it('does not call moveQuizEntry', () => {
        moveImmutable()
        expect(moveQuizEntryStub).not.to.have.been.called()
      })
    })
  })

  describe('a11y tests', () => {
    it('should meet a11y standards', (done) => {
      testbed.render()
      testbed.checkA11yStandards(done, { ignores: ['label', 'label-title-only'] })
    })
  })
})
