import { expect } from 'chai'
import {
  changeQuizTitle,
  changeQuizInstructions,
  changeQuizEntryEntry,
  changeQuizEntryPoints,
  changeQuizHasTimeLimit,
  changeQuizOutcomeAlignmentSetGuid,
  changeSessionTimeLimitInSeconds,
  changeQuizAllowBacktracking,
  changeQuizOneAtATimeType,
  changeQuizShuffleQuestions,
  changeItemTitle,
  changeItemBody,
  changeItemState,
  changeItemFeedback,
  changeOutcomeAlignmentSetGuid,
  clearModifications,
  clearTemporaryItem,
  clearTemporaryBankItem,
  clearTemporaryStimulus,
  createItem,
  createBankItem,
  createBank,
  createStimulus,
  changeBank,
  moveTemporaryQuizEntry,
  modifyStimulus,
  changeFudgePoints,
  sessionItemResultsModification,
  changeQuizRequireStudentAccessCode,
  changeQuizStudentAccessCode,
  changeQuizCalculatorType
} from '../modifications'

import {
  CLEAR_MODIFICATIONS,
  CLEAR_TEMPORARY_ITEM,
  CLEAR_TEMPORARY_BANK_ITEM,
  CLEAR_TEMPORARY_STIMULUS,
  CREATE_TEMPORARY_ITEM,
  CREATE_TEMPORARY_BANK_ITEM,
  CREATE_TEMPORARY_BANK,
  CREATE_TEMPORARY_STIMULUS,
  BANK_MODIFICATION,
  QUIZ_MODIFICATION,
  ITEM_MODIFICATION,
  STIMULUS_MODIFICATION,
  MOVE_TEMPORARY_ITEM,
  SESSION_ITEM_RESULTS_MODIFICATION,
  QUIZ_ENTRIES_MODIFICATION,
  QUIZ_SESSION_RESULT_MODIFICATION
} from '@instructure/quiz-common/lib/constants'

describe('modifications actions', () => {
  const expectedActionForModsAndId = (type, id, mods) => {
    return {
      type: type,
      payload: {
        id: id,
        modifications: mods
      }
    }
  }

  const expectActionsEqual = (a1, a2) => {
    expect(a1).to.deep.equal(a2)
  }

  describe('quizzes', () => {
    const quizId = '1'
    it('returns a changeQuizTitle action', () => {
      const newTitle = 'new quiz title'
      const changeQuizTitleAction = changeQuizTitle(quizId, newTitle)
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {title: newTitle})
      expectActionsEqual(changeQuizTitleAction, expectedAction)
    })

    it('returns a changeQuizInstructions action ', () => {
      const newInstructions = 'some new instructions'
      const changeInstructionsAction = changeQuizInstructions(quizId, newInstructions)
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {instructions: newInstructions})
      expectActionsEqual(changeInstructionsAction, expectedAction)
    })

    it('returns a changeQuizShuffleQuestions action ', () => {
      const newShuffleQuestions = true
      const changeShuffleQuestionsAction = changeQuizShuffleQuestions(quizId, newShuffleQuestions)
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {
        shuffleQuestions: newShuffleQuestions
      })
      expectActionsEqual(changeShuffleQuestionsAction, expectedAction)
    })

    it('returns a changeQuizRequireStudentAccessCode action ', () => {
      const newRequireStudentAccessCode = true
      const action = changeQuizRequireStudentAccessCode(quizId, newRequireStudentAccessCode)
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {
        requireStudentAccessCode: newRequireStudentAccessCode
      })
      expectActionsEqual(action, expectedAction)
    })

    it('returns a changeQuizStudentAccessCode action ', () => {
      const newStudentAccessCode = 'code2'
      const action = changeQuizStudentAccessCode(quizId, newStudentAccessCode)
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {
        studentAccessCode: newStudentAccessCode
      })
      expectActionsEqual(action, expectedAction)
    })

    it('returns a changeQuizAllowBacktracking action ', () => {
      const newBacktracking = true
      const backtrackingAction = changeQuizAllowBacktracking(quizId, newBacktracking)
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {
        allowBacktracking: newBacktracking
      })
      expectActionsEqual(backtrackingAction, expectedAction)
    })

    it('returns a changeQuizOneAtATimeType action ', () => {
      const newOQAAT = 'question'
      const oqaatAction = changeQuizOneAtATimeType(quizId, newOQAAT)
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {
        oneAtATimeType: newOQAAT
      })
      expectActionsEqual(oqaatAction, expectedAction)
    })

    it('returns a changeQuizHasTimeLimit action ', () => {
      const newHasTimeLimit = true
      const changeHasTimeLimitAction = changeQuizHasTimeLimit(quizId, newHasTimeLimit)
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {
        hasTimeLimit: newHasTimeLimit
      })
      expectActionsEqual(changeHasTimeLimitAction, expectedAction)
    })

    it('returns a changeSessionTimeLimitInSeconds action ', () => {
      const newSessionTimeLimitInSeconds = 100
      const changeSessionTimeLimitInSecondsAction = changeSessionTimeLimitInSeconds(
        quizId, newSessionTimeLimitInSeconds
      )
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {
        sessionTimeLimitInSeconds: newSessionTimeLimitInSeconds
      })
      expectActionsEqual(changeSessionTimeLimitInSecondsAction, expectedAction)
    })

    it('returns a changeQuizCalculatorType action ', () => {
      const newCalculatorType = 'scientific'
      const changeCalculatorTypeAction = changeQuizCalculatorType(quizId, newCalculatorType)
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {
        calculatorType: newCalculatorType
      })
      expectActionsEqual(changeCalculatorTypeAction, expectedAction)
    })

    it('returns a changeQuizOutcomeAlignmentSetGuid action ', () => {
      const newGuid = 'xxxx-xxxx-xxxx-xxxx'
      const changeQuizOutcomeAlignmentSetGuidAction = changeQuizOutcomeAlignmentSetGuid(quizId, newGuid)
      const expectedAction = expectedActionForModsAndId(QUIZ_MODIFICATION, quizId, {
        outcomeAlignmentSetGuid: newGuid
      })
      expectActionsEqual(changeQuizOutcomeAlignmentSetGuidAction, expectedAction)
    })
  })

  describe('#changeQuizEntryPoints', () => {
    it('returns a changeQuizEntryPoints action', () => {
      expect(changeQuizEntryPoints('1', 1)).to.deep.eq(
        {
          type: QUIZ_ENTRIES_MODIFICATION,
          payload: {
            id: '1',
            modifications: {
              pointsPossible: 1,
              regradeAdjustedPointsPossible: null
            }
          }
        }
      )
    })
  })

  describe('#changeQuizEntryEntry', () => {
    it('returns a changeQuizEntryEntry action', () => {
      expect(changeQuizEntryEntry('1', '2')).to.deep.eq({
        type: QUIZ_ENTRIES_MODIFICATION,
        payload: {
          id: '1',
          modifications: {
            entry: { id: '2' }
          }
        }
      })
    })

    it('includes entryType only if specified', () => {
      expect(changeQuizEntryEntry('1', '3', 'BankEntry')).to.deep.eq({
        type: QUIZ_ENTRIES_MODIFICATION,
        payload: {
          id: '1',
          modifications: {
            entry: { id: '3' },
            entryType: 'BankEntry'
          }
        }
      })
    })
  })

  describe('#createStimulus', () => {
    it('dispatches a createStimulus action', () => {
      const dispatch = sinon.stub()
      const getState = sinon.stub()
      createStimulus('1', 1)(dispatch, getState)
      expect(
        dispatch.calledWith(
          {
            type: CREATE_TEMPORARY_STIMULUS,
            payload: {
              quizId: '1',
              position: 1
            }
          }
        )
      ).to.be.true()
    })
  })

  describe('#modifyStimulus', () => {
    it('dispatches a modify stimulus action', () => {
      const dispatch = sinon.stub()
      const getState = sinon.stub()
      const fakeStimulus = {id: 1, title: 'hello'}
      modifyStimulus(fakeStimulus)(dispatch, getState)
      expect(
        dispatch.calledWith(
          {
            type: STIMULUS_MODIFICATION,
            payload: {
              stimulus: fakeStimulus
            }
          }
        )
      ).to.be.true()
    })
  })

  describe('#clearTemporaryStimulus', () => {
    it('dispatches a modify stimulus action', () => {
      const dispatch = sinon.stub()
      const getState = sinon.stub()
      clearTemporaryStimulus('1')(dispatch, getState)
      expect(
        dispatch.calledWith(
          {
            type: CLEAR_TEMPORARY_STIMULUS,
            payload: {
              quizId: '1'
            }
          }
        )
      ).to.be.true()
    })
  })

  describe('#createItem', () => {
    it('dispatches a createItem action', () => {
      const dispatch = sinon.stub()
      const getState = sinon.stub()
      createItem('1', 1, {}, '55', '4')(dispatch, getState)
      expect(
        dispatch.calledWith(
          {
            type: CREATE_TEMPORARY_ITEM,
            payload: {
              quizId: '1',
              position: 1,
              interactionTypeData: {},
              parentEntryId: '55',
              stimulusId: '4'
            }
          }
        )
      ).to.be.true()
    })
  })

  describe('#createBankItem', () => {
    it('dispatches a createBankItem action', () => {
      const dispatch = sinon.stub()
      const getState = sinon.stub()
      createBankItem('1', 'Item', {})(dispatch, getState)
      expect(
        dispatch.calledWith(
          {
            type: CREATE_TEMPORARY_BANK_ITEM,
            payload: {
              bankId: '1',
              interactionTypeData: {},
              bankEntryType: 'Item'
            }
          }
        )
      ).to.be.true()
    })
  })

  describe('#createBank', () => {
    it('dispatches a createBank action', () => {
      const dispatch = sinon.stub()
      const getState = sinon.stub()
      createBank()(dispatch, getState)
      expect(
        dispatch.calledWith(
          {
            type: CREATE_TEMPORARY_BANK,
            payload: { language: null }
          }
        )
      ).to.be.true()
    })
  })

  describe('#clearTemporaryItem', () => {
    it('dispatches a clearTemporaryItem action', () => {
      const dispatch = sinon.stub()
      const getState = sinon.stub()
      clearTemporaryItem('1')(dispatch, getState)
      expect(
        dispatch.calledWith(
          {
            type: CLEAR_TEMPORARY_ITEM,
            payload: {quizId: '1'}
          }
        )
      ).to.be.true()
    })

    it('returns a changeBank action', () => {
      const newName = 'new name'
      const bankId = '1'
      const changeBankAction = changeBank(bankId, {name: newName})
      const expectedAction = expectedActionForModsAndId(BANK_MODIFICATION, bankId, {name: newName})
      expectActionsEqual(changeBankAction, expectedAction)
    })
  })

  describe('#clearTemporaryBankItem', () => {
    it('dispatches a clearTemporaryBankItem action', () => {
      const dispatch = sinon.stub()
      const getState = sinon.stub()
      clearTemporaryBankItem('1')(dispatch, getState)
      expect(
        dispatch.calledWith(
          {
            type: CLEAR_TEMPORARY_BANK_ITEM,
            payload: {bankId: '1'}
          }
        )
      ).to.be.true()
    })
  })

  describe('#clearModifications', () => {
    it('returns a clearModifications actions', () => {
      const clearCall = clearModifications('testType', 'testId', [1, 2])
      expect(clearCall).to.deep.eq(
        {
          type: CLEAR_MODIFICATIONS,
          payload: {
            objectType: 'testType',
            objectId: 'testId',
            deepKeys: [1, 2]
          }
        }
      )
    })
  })

  describe('modify items', () => {
    const itemId = '1'
    it('returns a changeItemTitle action', () => {
      const newTitle = 'new quiz title'
      const changeItemTitleAction = changeItemTitle(itemId, newTitle)
      const expectedAction = expectedActionForModsAndId(ITEM_MODIFICATION, itemId, {title: newTitle})
      expectActionsEqual(changeItemTitleAction, expectedAction)
    })

    it('returns a changeItemBody action ', () => {
      const newInstructions = 'some new instructions'
      const changeItemBodyAction = changeItemBody(itemId, newInstructions)
      const expectedAction = expectedActionForModsAndId(ITEM_MODIFICATION, itemId, {itemBody: newInstructions})
      expectActionsEqual(changeItemBodyAction, expectedAction)
    })

    it('can modify an item with arbitrary changes ', () => {
      const modifications = {foo: 'bar'}
      const changeItemStateAction = changeItemState(itemId, modifications)
      const expectedAction = expectedActionForModsAndId(ITEM_MODIFICATION, itemId, modifications)
      expectActionsEqual(changeItemStateAction, expectedAction)
    })

    it('can change item feedback', () => {
      const newFeedback = {correct: 'correct', neutral: 'neutral'}
      const changeItemFeedbackAction = changeItemFeedback(itemId, newFeedback)
      const expectedAction = expectedActionForModsAndId(ITEM_MODIFICATION, itemId, {feedback: newFeedback})
      expectActionsEqual(changeItemFeedbackAction, expectedAction)
    })

    it('returns a changeOutcomeAlignmentSetGuid action ', () => {
      const newGuid = '54084912-f7a9-4bf5-96e3-887f67fe11c7'
      const changeOutcomeAlignmentSetGuidAction = changeOutcomeAlignmentSetGuid(itemId, newGuid)
      const expectedAction = expectedActionForModsAndId(ITEM_MODIFICATION, itemId, {outcomeAlignmentSetGuid: newGuid})
      expectActionsEqual(changeOutcomeAlignmentSetGuidAction, expectedAction)
    })
  })

  describe('modify session item results', () => {
    it('returns a well-formed action', () => {
      const id = '1_5'
      const modifications = {bar: 'baz'}
      const modificationAction = sessionItemResultsModification(id, modifications)
      const expectedAction = {
        type: SESSION_ITEM_RESULTS_MODIFICATION,
        payload: {
          id, modifications
        }
      }
      expectActionsEqual(expectedAction, modificationAction)
    })
  })

  describe('change fudge points', () => {
    it('sets fudge points in session results', () => {
      const resultId = '15'
      const fudgePoints = 5
      const modificationAction = changeFudgePoints(resultId, fudgePoints)
      const expectedAction = {
        type: QUIZ_SESSION_RESULT_MODIFICATION,
        payload: {
          id: resultId,
          modifications: {
            fudgePoints: fudgePoints
          }
        }
      }
      expectActionsEqual(expectedAction, modificationAction)
    })
  })

  describe('#moveTemporaryQuizEntry', () => {
    it('returns a payload with direction if up or down', () => {
      const changeItemStateAction = moveTemporaryQuizEntry('up', null, false)
      const payload = {
        direction: 'up',
        movingDown: false,
        stimulusQuizEntryId: null
      }
      const expectedAction = {type: MOVE_TEMPORARY_ITEM, payload: payload}
      expectActionsEqual(changeItemStateAction, expectedAction)
    })

    it('returns a payload with position if integer given', () => {
      const changeItemStateAction = moveTemporaryQuizEntry(20, null, false)
      const payload = {
        position: 20,
        movingDown: false,
        stimulusQuizEntryId: null
      }
      const expectedAction = {type: MOVE_TEMPORARY_ITEM, payload: payload}
      expectActionsEqual(changeItemStateAction, expectedAction)
    })
  })
})
