import sinon from 'sinon'

import { appendFeature, destroyFeature, toggleFeature } from '../index'

describe('learnosity', () => {
  const sandbox = sinon.createSandbox()
  const questionsApp = {
    append: sandbox.stub(),
    feature: sandbox.stub()
  }
  const learnosityApp = {
    init: sandbox.stub()
  }
  const loader = sandbox.stub()

  const fetcher = {
    post: sandbox.stub()
  }

  const calculator = {
    destroy: sandbox.stub(),
    toggle: sandbox.stub()
  }

  beforeEach(() => {
    loader.resolves(learnosityApp)
    learnosityApp.init.returns(questionsApp)
    fetcher.post.resolves()
  })

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

  const quizSessionId = '42'
  const feature = {
    type: 'calculator',
    mode: 'basic',
    feature_id: 'calc_1553188426534'
  }
  const timeout = 200

  function waitForLearnosityAppInit () {
    return new Promise(resolve => {
      const interval = setInterval(() => {
        if (learnosityApp.init.called) {
          clearInterval(interval)
          const callbacks = learnosityApp.init.firstCall.args[1]
          callbacks.readyListener()
          resolve(callbacks)
        }
      }, 50)
    })
  }

  describe('appendFeature', () => {
    context('with a feature container in the DOM', () => {
      let featureContainer

      beforeEach(() => {
        featureContainer = document.createElement('div')
        featureContainer.className = `learnosity-feature feature-${feature.feature_id}`
        document.body.appendChild(featureContainer)
      })

      afterEach(() => {
        featureContainer.remove()
      })

      it('calls questionsApp.append and resolves when the learnosity ready callback is called', async () => {
        const promise = appendFeature(quizSessionId, feature, timeout, loader, fetcher)

        // Learnosity calls the ready callback when the calculator has been
        // appended to the DOM
        const callbacks = await waitForLearnosityAppInit()
        callbacks.readyListener()

        await expect(promise).to.be.fulfilled()
        expect(questionsApp.append).to.have.been.calledWithMatch({ features: [feature] })
      })

      it("times out if the learnosity ready callback isn't called", async () => {
        await expect(
          appendFeature(quizSessionId, feature, timeout, loader, fetcher)
        ).to.be.rejectedWith(/timed out/i)
      })
    })

    context('without a feature container in the DOM', () => {
      it('rejects', async () => {
        await expect(
          appendFeature(quizSessionId, feature, timeout, loader, fetcher)
        ).to.be.rejectedWith(/container element not found/i)
      })
    })
  })

  describe('destroyFeature', () => {
    context('when calculator feature is found', () => {
      beforeEach(() => {
        questionsApp.feature.returns(calculator)
      })

      it('calls destroy on the feature', () => {
        destroyFeature(feature.feature_id, loader, fetcher)
        expect(calculator.destroy).to.have.been.calledOnce()
      })
    })

    context('when calculator feature is not found', () => {
      it('does nothing', () => {
        destroyFeature(feature.feature_id, loader, fetcher)
        expect(calculator.destroy).not.to.have.been.called()
      })
    })
  })

  describe('toggleFeature', () => {
    for (const onOff of [true, false, void 0]) {
      context(`with toggle state ${onOff}`, () => {
        context('when calculator feature is found', () => {
          beforeEach(() => {
            questionsApp.feature.returns(calculator)
          })

          it('calls toggle on the feature', () => {
            toggleFeature(feature.feature_id, onOff, loader, fetcher)
            expect(calculator.toggle).to.have.been.calledOnceWith(onOff)
          })
        })

        context('when calculator feature is not found', () => {
          it('does nothing', () => {
            toggleFeature(feature.feature_id, onOff, loader, fetcher)
            expect(calculator.toggle).not.to.have.been.called()
          })
        })
      })
    }
  })
})
