/*
 * Copyright (C) 2019 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */
import {fireEvent, render, wait} from '@testing-library/react'
import {MockedProvider} from '@apollo/react-testing'
import {mockQuery} from '../../mocks'
import range from 'lodash/range'
import React from 'react'
import {STUDENT_VIEW_QUERY, SUBMISSION_HISTORIES_QUERY} from '../../graphqlData/Queries'
import {SubmissionMocks} from '../../graphqlData/Submission'
import ViewManager from '../ViewManager'

jest.mock('../AttemptSelect')

async function mockStudentViewResult(overrides = {}) {
  const variables = {assignmentLid: '1', submissionID: '1'}
  const result = await mockQuery(STUDENT_VIEW_QUERY, overrides, variables)
  result.data.assignment.env = {
    assignmentUrl: 'mocked-assignment-url',
    courseId: '1',
    currentUser: {id: '1', display_name: 'bob', avatar_image_url: 'awesome.avatar.url'},
    modulePrereq: null,
    moduleUrl: 'mocked-module-url'
  }
  return result.data
}

async function mockSubmissionHistoriesResult(overrides = {}) {
  const variables = {submissionID: '1'}
  const allOverrides = [overrides, {Node: {__typename: 'Submission'}}]
  const result = await mockQuery(SUBMISSION_HISTORIES_QUERY, allOverrides, variables)
  return result.data
}

async function makeProps(opts = {}) {
  const currentAttempt = opts.currentAttempt
  const numSubmissionHistories =
    opts.numSubmissionHistories === undefined ? currentAttempt - 1 : opts.numSubmissionHistories
  const withDraft = !!opts.withDraft

  // Mock the current submission
  const submittedStateOverrides = currentAttempt === 0 ? {} : SubmissionMocks.submitted
  const studentViewOverrides = [
    {
      Submission: {
        ...submittedStateOverrides,
        attempt: currentAttempt
      }
    }
  ]
  if (withDraft) {
    studentViewOverrides.push({
      Submission: SubmissionMocks.onlineUploadReadyToSubmit
    })
  }
  const studentViewResult = await mockStudentViewResult(studentViewOverrides)

  // Mock the submission histories, as needed.
  let submissionHistoriesResult = null
  if (numSubmissionHistories > 0) {
    const mockedNodeResults = range(0, numSubmissionHistories).map(attempt => ({
      ...SubmissionMocks.graded,
      attempt
    }))

    submissionHistoriesResult = await mockSubmissionHistoriesResult({
      SubmissionHistoryConnection: {nodes: mockedNodeResults}
    })
  }

  return {
    initialQueryData: studentViewResult,
    submissionHistoriesQueryData: submissionHistoriesResult
  }
}

describe('ViewManager', () => {
  beforeEach(() => {
    window.ENV = {
      context_asset_string: 'test_1',
      COURSE_ID: '1',
      current_user: {display_name: 'bob', avatar_url: 'awesome.avatar.url'},
      PREREQS: {}
    }
  })

  describe('New Attempt Button', () => {
    describe('behaves correctly', () => {
      it('by creating a new dummy submission when clicked', async () => {
        const props = await makeProps({currentAttempt: 1})
        const {getAllByText, getByText} = render(
          <MockedProvider>
            <ViewManager {...props} />
          </MockedProvider>
        )
        const newAttemptButton = getByText('New Attempt')
        fireEvent.click(newAttemptButton)
        expect(getAllByText('Attempt 2')[0]).toBeInTheDocument()
      })

      it('by not displaying the new attempt button on a dummy submission', async () => {
        const props = await makeProps({currentAttempt: 1})
        const {queryByText, getByText} = render(
          <MockedProvider>
            <ViewManager {...props} />
          </MockedProvider>
        )
        const newAttemptButton = getByText('New Attempt')
        fireEvent.click(newAttemptButton)
        expect(queryByText('New Attempt')).not.toBeInTheDocument()
      })
    })

    describe('when a submission draft exists', () => {
      it('is not displayed when the draft is the selected', async () => {
        const props = await makeProps({currentAttempt: 1, withDraft: true})
        const {queryByText} = render(
          <MockedProvider>
            <ViewManager {...props} />
          </MockedProvider>
        )
        expect(queryByText('New Attempt')).not.toBeInTheDocument()
      })
    })

    describe('when there is no submission draft', () => {
      it('is not displayed on attempt 0', async () => {
        const props = await makeProps({currentAttempt: 0})
        const {queryByText} = render(
          <MockedProvider>
            <ViewManager {...props} />
          </MockedProvider>
        )
        expect(queryByText('New Attempt')).not.toBeInTheDocument()
      })

      it('is displayed on the latest submitted attempt', async () => {
        const props = await makeProps({currentAttempt: 1})
        const {queryByText} = render(
          <MockedProvider>
            <ViewManager {...props} />
          </MockedProvider>
        )
        expect(queryByText('New Attempt')).toBeInTheDocument()
      })

      it('sets focus on the assignment toggle details when clicked', async () => {
        const props = await makeProps({currentAttempt: 1})
        const {getByText} = render(
          <MockedProvider>
            <ViewManager {...props} />
          </MockedProvider>
        )

        const mockElement = {
          focus: jest.fn()
        }
        document.querySelector = jest.fn().mockReturnValue(mockElement)

        const newButton = getByText('New Attempt')
        fireEvent.click(newButton)

        await wait(() => {
          expect(document.querySelector).toHaveBeenCalledWith(
            'button[data-test-id=assignments-2-assignment-toggle-details]'
          )
          expect(mockElement.focus).toHaveBeenCalled()
        })
      })

      it('is not displayed if you are not on the latest submission attempt', async () => {
        const props = await makeProps({currentAttempt: 3, numSubmissionHistories: 4})
        const {queryByText} = render(
          <MockedProvider>
            <ViewManager {...props} />
          </MockedProvider>
        )
        expect(queryByText('New Attempt')).not.toBeInTheDocument()
      })
    })
  })

  describe('Submission Drafts', () => {
    it('are initially displayed if they exist', async () => {
      const props = await makeProps({currentAttempt: 1, withDraft: true})
      const {getAllByText} = render(
        <MockedProvider>
          <ViewManager {...props} />
        </MockedProvider>
      )
      expect(getAllByText('Attempt 2')[0]).toBeInTheDocument()
    })
  })
})
