import ReactDOM from 'react-dom'
import ReactTestUtils from 'react-dom/test-utils'

import Button from '@instructure/ui-buttons/lib/components/Button'
import Item from '../../../../../records/Item'

import EntrySave from '../presenter'

describe('Entry Save', () => {
  const EntryWithContext = withContext(EntrySave)
  const item = new Item({
    id: '666',
    title: 'Tough Color Question',
    itemBody: 'Which color is happiest?',
    scoringData: {
      value: 'id'
    }
  })

  const sandbox = sinon.createSandbox()
  const notificationStub = sandbox.stub()
  const submitHandlerStub = sandbox.stub()
  const cancelHandlerStub = sandbox.stub()
  const clearChangesStub = sandbox.stub()
  const clearNextQuizEntryStub = sandbox.stub()
  const switchOnEditingStub = sandbox.stub()
  const switchOffEditingStub = sandbox.stub()
  const showErrorStub = sandbox.stub()
  const hideErrorStub = sandbox.stub()

  const testbed = createTestbed(EntryWithContext, {
    clearNextQuizEntry: clearNextQuizEntryStub,
    errorsAreShowing: true,
    isValid: true,
    showError: showErrorStub,
    hideError: hideErrorStub,
    entry: item,
    submitHandler: submitHandlerStub,
    switchOnEditing: switchOnEditingStub,
    switchOffEditing: switchOffEditingStub,
    cancelHandler: cancelHandlerStub,
    clearChanges: clearChangesStub,
    screenreaderNotification: notificationStub
  })

  const CANCEL_BUTTON_NUMBER = 0
  const SUBMIT_BUTTON_NUMBER = 1

  afterEach(() => {
    sandbox.reset()
  })

  shouldRenderComponent(
    testbed, {}, EntryWithContext
  )

  const confirmNotifiesScreenreader = (expectedMessage) => {
    expect(notificationStub).to.have.been.calledWith(expectedMessage)
  }

  it('switches off editing and calls clearChanges when it unmounts', async () => {
    testbed.render({
      clearChanges: clearChangesStub,
      switchOffEditing: switchOffEditingStub
    })

    ReactDOM.unmountComponentAtNode(testbed.rootNode)

    expect(clearChangesStub).to.have.been.called()
    expect(switchOffEditingStub).to.have.been.called()
    confirmNotifiesScreenreader('Item editing mode turned off.')
  })

  const clickButton = (buttonComponent) => {
    const buttonNode = ReactDOM.findDOMNode(buttonComponent)
    ReactTestUtils.Simulate.click(buttonNode)
  }

  const clickSubmit = () => {
    const submitButton = testbed.findChildrenByType(Button)[SUBMIT_BUTTON_NUMBER]
    clickButton(submitButton)
  }

  const clickCancel = () => {
    const cancelButton = testbed.findChildrenByType(Button)[CANCEL_BUTTON_NUMBER]
    clickButton(cancelButton)
  }

  describe('#onSubmit', () => {
    context('is valid', () => {
      it('calls submitHandler', async () => {
        testbed.render()
        clickSubmit()
        expect(submitHandlerStub).to.have.been.called()
      })

      it('calls switchOffEditing', (done) => {
        testbed.render()
        clickSubmit()
        setTimeout(() => {
          expect(switchOffEditingStub).to.have.been.called()
          done()
        })
      })

      it('notifies the screenreader', (done) => {
        testbed.render()
        clickSubmit()
        setTimeout(() => {
          confirmNotifiesScreenreader('Changes to item saved.')
          done()
        })
      })

      it('cannot be called twice on double-click', async () => {
        testbed.render()
        clickSubmit()
        clickSubmit()
        expect(submitHandlerStub).to.have.been.calledOnce()
      })

      it('disables the button', async () => {
        testbed.render()
        clickSubmit()
        const submitButton = testbed.findChildrenByType(Button)[SUBMIT_BUTTON_NUMBER]
        expect(submitButton.props.disabled).to.be.true()
      })

      context('when submitHandler fails', () => {
        beforeEach(() => {
          submitHandlerStub.rejects()
        })

        it('does not call switchOffEditing', (done) => {
          testbed.render()
          clickSubmit()
          setTimeout(() => {
            expect(switchOffEditingStub).not.to.have.been.called()
            done()
          })
        })

        it('does not notify the screenreader', (done) => {
          testbed.render()
          clickSubmit()
          setTimeout(() => {
            expect(notificationStub).not.to.have.been.called()
            done()
          })
        })

        it('re-enables the button', (done) => {
          testbed.render()
          clickSubmit()
          setTimeout(() => {
            const submitButton = testbed.findChildrenByType(Button)[SUBMIT_BUTTON_NUMBER]
            expect(submitButton.props.disabled).to.be.false()
            done()
          })
        })
      })
    })

    context('is invalid', () => {
      it('does not call submitHandler', async () => {
        testbed.render({ isValid: false })
        clickSubmit()
        expect(submitHandlerStub).to.have.not.been.called()
      })

      it('calls showError', async () => {
        testbed.render({ isValid: false })
        clickSubmit()
        expect(showErrorStub).to.have.been.called()
      })

      it('calls clearNextQuizEntry', async () => {
        testbed.render({ isValid: false })
        clickSubmit()
        expect(clearNextQuizEntryStub).to.have.been.called()
      })

      it('does not call switchOffEditing', async () => {
        testbed.render({ isValid: false })
        clickSubmit()
        expect(switchOffEditingStub).to.have.not.been.called()
      })

      it('notifies the screenreader', async () => {
        testbed.render({ isValid: false })
        clickSubmit()
        confirmNotifiesScreenreader('This item cannot be saved due to invalid input data.')
      })
    })
  })

  describe('#onCancel', () => {
    it('calls switchOffEditing', async () => {
      testbed.render()
      clickCancel()
      expect(switchOffEditingStub).to.have.been.called()
    })

    it('calls hideError', async () => {
      testbed.render()
      clickCancel()
      expect(hideErrorStub).to.have.been.called()
    })

    it('calls cancelHandler', async () => {
      testbed.render()
      clickCancel()
      expect(cancelHandlerStub).to.have.been.called()
    })

    it('calls clearChanges', async () => {
      testbed.render()
      clickCancel()
      expect(clearChangesStub).to.have.been.called()
    })
  })

  describe('a11y tests', () => {
    it('should meet a11y standards', (done) => {
      testbed.render()
      testbed.checkA11yStandards(done)
    })
  })
})
