import forEach from 'lodash/forEach'
import {
  fromJS,
  is,
  List,
  Map
} from 'immutable'

import {
  defaultChecks,
  singleRelationCheck,
  manyRelationCheck
} from '../../../../tests/util/recordTests'

import {
  AUTOGRADING,
  CREATING_RESPONSES,
  CREATING_SESSION_ITEMS,
  FAILED_TO_START,
  FAILURE,
  GRADED,
  IN_PROGRESS,
  REOPENING_GRADED,
  NEEDS_GRADING,
  PENDING,
  STARTING
} from '@instructure/quiz-common/lib/constants'

import { setFakeStore, unsetFakeStore } from '../../../reduxStore'

import SessionItem from '../SessionItem'
import QuizSession from '../QuizSession'
import Quiz from '../Quiz'
import QuizSessionResult from '../QuizSessionResult'
import QuizSessionEvent from '../QuizSessionEvent'
import Response from '../Response'

describe('QuizSession Record', () => {
  describe('#constructor', () => {
    const defaultsToCheck = {
      id: '0'
    }
    defaultChecks(QuizSession, defaultsToCheck)
  })

  describe('relationships', () => {
    const quiz = {id: '1'}
    const sessionItem = {position: '2'}
    const quizSessionResult = {position: '4'}
    const resumeData = {id: '1_22'}

    const exampleState = {
      quizzes: {[quiz.id]: quiz},
      sessionItems: {[sessionItem.position]: sessionItem},
      quizSessionResults: {[quizSessionResult.position]: quizSessionResult},
      taking: {
        responses: {'1_22': resumeData}
      }
    }

    describe('#getQuiz', () => {
      const options = {
        RelationRecordType: Quiz,
        relationAttrs: quiz,
        recordAttributes: {quiz: quiz.id},
        necessaryState: fromJS(exampleState)
      }

      singleRelationCheck(QuizSession, 'getQuiz', options)
    })

    describe('#findAuthoritativeResult', () => {
      const options = {
        RelationRecordType: QuizSessionResult,
        relationAttrs: quizSessionResult,
        recordAttributes: {authoritativeResult: quiz.id},
        necessaryState: fromJS(exampleState)
      }
      singleRelationCheck(QuizSession, 'findAuthoritativeResult', options)
    })

    describe('#getSessionItems', () => {
      const options = {
        RelationRecordType: SessionItem,
        relationAttrs: sessionItem,
        recordAttributes: {sessionItems: [sessionItem.position]},
        necessaryState: fromJS(exampleState)
      }

      manyRelationCheck(QuizSession, 'getSessionItems', options)
    })

    describe('#getQuizSessionResults', () => {
      const options = {
        RelationRecordType: QuizSessionResult,
        relationAttrs: quizSessionResult,
        recordAttributes: {quizSessionResults: [quizSessionResult.position]},
        necessaryState: fromJS(exampleState)
      }

      manyRelationCheck(QuizSession, 'getQuizSessionResults', options)
    })

    describe('#getEvents', () => {
      it('returns records for event data matching the session id', () => {
        const dateA = '2016-09-28T18:45:29.929Z|462090529'
        const dateB = '2015-05-28T18:45:29.929Z|462090522'
        const dateC = '2014-01-28T18:45:29.929Z|462090521'
        const quizSessionId = '22'
        const otherQuizSessionId = '44'

        const eventData = fromJS({
          [quizSessionId]: [
            {createdAt: dateA, quizSessionId},
            {createdAt: dateB, quizSessionId}
          ],
          [otherQuizSessionId]: [
            {createdAt: dateC, quizSessionId: otherQuizSessionId}
          ]
        })

        const fakeState = fromJS({quizSessionEvents: eventData})
        setFakeStore(fakeState)

        const quizSession = new QuizSession({id: quizSessionId})
        const eventRecords = quizSession.getEvents()
        const expectedRecords = fromJS([
          new QuizSessionEvent({createdAt: dateA, quizSessionId}),
          new QuizSessionEvent({createdAt: dateB, quizSessionId})
        ])

        expect(
          is(eventRecords, expectedRecords)
        ).to.be.true()

        unsetFakeStore()
      })
    })

    describe('#getResumeData', () => {
      it('returns the proper resume data', () => {
        const options = {
          RelationRecordType: Response,
          relationAttrs: resumeData,
          recordAttributes: {resumeData: ['1_22']},
          necessaryState: fromJS(exampleState)
        }

        manyRelationCheck(Response, 'getResumeData', options)
      })
    })
  })

  describe('#pins', () => {
    it('returns the pins from takingData', () => {
      const qs = new QuizSession(fromJS({takingData: {pins: [1]}}))
      expect(qs.pins()).to.be.eql(List([1]))
    })

    it('returns an empty List if no takingData', () => {
      const qs = new QuizSession({})
      expect(qs.pins()).to.be.eql(List([]))
    })
  })

  describe('#userDisplayName', () => {
    const fullName = 'Some Full Name'
    const givenName = 'SomeGivenName'
    const email = 'some-email@email.edu'
    it('displays the fullName if it has it', () => {
      const user = Map({fullName, givenName, email})
      const qs = new QuizSession({user}, {})
      expect(qs.userDisplayName()).to.be.equal(fullName)
    })

    it('displays the givenName if there is no full name', () => {
      const user = Map({givenName, email})
      const qs = new QuizSession({user}, {})
      expect(qs.userDisplayName()).to.be.equal(givenName)
    })

    it('displays the email if there is no full or given name', () => {
      const user = Map({email})
      const qs = new QuizSession({user}, {})
      expect(qs.userDisplayName()).to.be.equal(email)
    })

    it('displays empty string if no user is given', () => {
      const qs = new QuizSession(null, {})
      expect(qs.userDisplayName()).to.be.empty()
    })
  })

  describe('#isOneQuestionAtATime', () => {
    it('returns true if not null value for oneAtATimeType', () => {
      const qs = new QuizSession({oneAtATimeType: 'question'})
      expect(qs.isOneQuestionAtATime()).to.eq(true)
    })

    it('returns false if null value for oneAtATimeType', () => {
      const qs = new QuizSession({oneAtATimeType: null})
      expect(qs.isOneQuestionAtATime()).to.eq(false)
    })
  })

  describe('status helpers', () => {
    describe('#isLoaded', () => {
      it('should be loaded if id is not "0"', () => {
        const qs = new QuizSession({id: '1'}, {})
        expect(qs.isLoaded()).to.be.true()
      })

      it('should not be loaded if id is "0"', () => {
        const qs = new QuizSession({id: '0'}, {})
        expect(qs.isLoaded()).to.be.false()
      })
    })

    const helperStatusMap = {
      pending: PENDING,
      starting: STARTING,
      creatingSessionItems: CREATING_SESSION_ITEMS,
      failedToStart: FAILED_TO_START,
      inProgress: IN_PROGRESS,
      reopeningGraded: REOPENING_GRADED,
      creatingResponses: CREATING_RESPONSES,
      needsGrading: NEEDS_GRADING,
      autoGrading: AUTOGRADING,
      failed: FAILURE,
      graded: GRADED
    }

    forEach(helperStatusMap, (status, check) => {
      describe(check, () => {
        it(`returns true if status is ${status}`, () => {
          const qs = new QuizSession({status}, {})
          expect(qs[check]()).to.be.true()
        })
      })

      it('returns false if status is invalid', () => {
        const qs = new QuizSession({status: 'invalid'}, {})
        expect(qs[check]()).to.be.false()
      })
    })

    describe('taking', () => {
      ['starting', 'creating_session_items', 'in_progress', 'reopening_graded'].forEach((status) => {
        it(`returns true if status is ${status}`, () => {
          const qs = new QuizSession({status}, {})
          expect(qs.taking()).to.be.true()
        })
      })

      it('returns false if status is invalid', () => {
        const qs = new QuizSession({status: 'invalid'}, {})
        expect(qs.taking()).to.be.false()
      })
    })

    describe('submitted', () => {
      ['creating_responses', 'autograding', 'needs_grading', 'graded', 'failure'].forEach((status) => {
        it(`returns true if status is ${status}`, () => {
          const qs = new QuizSession({status}, {})
          expect(qs.submitted()).to.be.true()
        })
      })

      it('returns false if status is invalid', () => {
        const qs = new QuizSession({status: 'invalid'}, {})
        expect(qs.submitted()).to.be.false()
      })
    })
  })
})
