import {vi} from 'vitest'
import React from 'react'

import {render, screen, fireEvent} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

import EssayEdit from '../index'
import runAxeCheck from '@instructure/ui-axe-check'

const merge = (objA, objB) => {
  return Object.assign({}, objA, objB)
}

const testRender = (overrideProps, overrideInteractionData) => {
  const changeItemStateStub = vi.fn()
  const {container} = render(
    <EssayEdit
      {...merge(
        {
          changeItemState: changeItemStateStub,
          enableRichContentEditor: false, // disable to make testing easier
          itemBody: 'STEM',
          openImportModal: () => {},
          scoringData: {value: 'there better be some text here'},
          additionalOptions: [],
          interactionData: merge(
            {
              rce: false,
              spellCheck: false,
              wordCount: false,
              wordLimitEnabled: false,
              wordLimitMax: null,
              wordLimitMin: null,
              notes: '',
            },
            overrideInteractionData,
          ),
        },
        overrideProps,
      )}
    />,
  )
  return {
    changeItemStateStub,
    container,
  }
}

const assertNewAttrEqual = (changeItemStateStub, attr, expectedVal) => {
  expect(changeItemStateStub).toHaveBeenCalled()
  const newVal = changeItemStateStub.mock.lastCall[0].interactionData[attr]
  expect(newVal).toBe(expectedVal)
}

describe('Essay Edit', () => {
  let defaultProps = {}
  let changeItemStateStub

  beforeEach(() => {
    changeItemStateStub = vi.fn()
    defaultProps = {
      ...merge({
        changeItemState: changeItemStateStub,
        enableRichContentEditor: false, // disable to make testing easier
        itemBody: 'STEM',
        openImportModal: () => {},
        scoringData: {value: 'there better be some text here'},
        additionalOptions: [],
        interactionData: merge({
          rce: false,
          spellCheck: false,
          wordCount: false,
          wordLimitEnabled: false,
          wordLimitMax: null,
          wordLimitMin: null,
          notes: '',
        }),
      }),
    }
  })

  describe('rendering', () => {
    it('renders a basic essay', () => {
      const {container} = render(<EssayEdit {...defaultProps} enableRichContentEditor />)

      // Check the input fields
      expect(container.querySelector('[data-automation="sdk-question-stem"]')).toBeInTheDocument()
      expect(screen.queryByRole('textbox', {name: /rich content editor/i})).toBeInTheDocument()

      // Check the tabs
      expect(screen.queryByRole('button', {name: /options/i})).toBeInTheDocument()
      expect(screen.queryByRole('button', {name: /grading notes/i})).toBeInTheDocument()

      // Check the word limit inputs
      expect(screen.queryByRole('textbox', {name: /Minimum/i})).not.toBeInTheDocument()
      expect(screen.queryByRole('textbox', {name: /Maximum/i})).not.toBeInTheDocument()
    })

    it('renders with the rce disabled', () => {
      const {container} = testRender({enableRichContentEditor: false})

      expect(container).toBeDefined()
      const rces = container.querySelectorAll('[data-automation^="sdk-rce"]')
      expect(rces).toHaveLength(0)
    })

    it('renders word limit inputs', () => {
      testRender(
        {},
        {
          wordLimitEnabled: true,
          wordLimitMin: 0,
          wordLimitMax: 0,
        },
      )

      const minInput = screen.getByLabelText(/Minimum/i)
      expect(minInput).toBeDefined()
      expect(minInput.value).toBe('0')

      const maxInput = screen.getByLabelText(/Maximum/i)
      expect(maxInput).toBeDefined()
      expect(maxInput.value).toBe('0')
    })

    it('overrideEditableForRegrading disables a bunch of things', () => {
      const {container} = render(<EssayEdit {...defaultProps} overrideEditableForRegrading />)

      const questionStemTextArea = container.querySelector(
        '[data-automation="sdk-question-stem"] textarea',
      )
      expect(questionStemTextArea.getAttribute('disabled')).not.toBeNull()

      const checkboxes = screen.queryAllByRole('checkbox')

      for (const checkbox of checkboxes) {
        expect(checkbox.getAttribute('disabled')).not.toBeNull()
      }

      const gradingNotesTab = screen.getByRole('button', {name: /grading notes/i})
      userEvent.click(gradingNotesTab)

      const gradingNotesTextArea = screen.getByRole('textbox', {name: /grading notes/i})
      expect(gradingNotesTextArea.getAttribute('disabled')).not.toBeNull()
    })
  })

  describe('#changeItemState', () => {
    it('handles changes to itemBody', () => {
      const {changeItemStateStub, container} = testRender()

      const stem = container.querySelector('[data-automation="sdk-question-stem"] textarea')
      expect(stem).not.toBeNull()
      fireEvent.change(stem, {target: {value: 'new stem val'}})
      expect(changeItemStateStub).toHaveBeenCalled()
      expect(changeItemStateStub.mock.calls[0][0].itemBody).toBe('new stem val')
    })

    it('saves the updated notes with changeItemState', () => {
      render(<EssayEdit {...defaultProps} />)

      const gradingNotesTab = screen.getByRole('button', {name: /grading notes/i})
      userEvent.click(gradingNotesTab)

      const gradingNotesTextArea = screen.getByRole('textbox', {name: /grading notes/i})
      userEvent.clear(gradingNotesTextArea)
      userEvent.type(gradingNotesTextArea, 'new val')

      expect(changeItemStateStub).toHaveBeenCalledWith({scoringData: {value: 'new val'}})
    })

    // TODO: QUIZ-XXXXX — migrate "handles all the toggles" test
    // This test relies on @instructure/ui-test-utils' wrapper .label property
    // which maps checkbox labels to interactionData keys. Needs RTL migration.
    it.skip('handles all the toggles', () => {})

    it('handles minimum word limit changes', () => {
      const {changeItemStateStub} = testRender({}, {wordLimitEnabled: true})

      const minInput = screen.getByLabelText(/Minimum/i)
      expect(minInput).toBeDefined()

      fireEvent.change(minInput, {target: {value: '10'}})
      fireEvent.blur(minInput)
      assertNewAttrEqual(changeItemStateStub, 'wordLimitMin', '10')

      // make sure blank sets it to null
      fireEvent.change(minInput, {target: {value: ''}})
      fireEvent.blur(minInput)
      assertNewAttrEqual(changeItemStateStub, 'wordLimitMin', null)
    })

    it('handles maximum word limit changes', () => {
      const {changeItemStateStub} = testRender({}, {wordLimitEnabled: true})

      const maxInput = screen.getByLabelText(/Maximum/i)
      expect(maxInput).toBeDefined()

      fireEvent.change(maxInput, {target: {value: '100'}})
      fireEvent.blur(maxInput)
      assertNewAttrEqual(changeItemStateStub, 'wordLimitMax', '100')

      // make sure blank sets it to null
      fireEvent.change(maxInput, {target: {value: ''}})
      fireEvent.blur(maxInput)
      assertNewAttrEqual(changeItemStateStub, 'wordLimitMax', null)
    })
  })

  describe('calculator per question option', () => {
    it('does not render the calculator checkbox if showCalculatorOption is false', () => {
      render(<EssayEdit {...defaultProps} showCalculatorOption={false} />)
      expect(screen.queryByRole('checkbox', {name: /show on-screen calculator/i})).toBeNull()
    })

    it('shows with basic option selected', () => {
      testRender({calculatorType: 'basic'})

      const calculatorBasicRadioInput = screen.getByRole('radio', {name: /basic calculator/i})
      const calculatorScientificRadioInput = screen.getByRole('radio', {
        name: /scientific calculator/i,
      })

      expect(calculatorBasicRadioInput.checked).toBe(true)
      expect(calculatorScientificRadioInput.checked).toBe(false)
    })

    it('switches from none to basic calculator', () => {
      const {changeItemStateStub} = testRender({calculatorType: 'none'})

      const calculatorCheckbox = screen.getByRole('checkbox', {
        name: /show on-screen calculator/i,
      })
      userEvent.click(calculatorCheckbox)

      expect(changeItemStateStub).toHaveBeenCalled()
      expect(changeItemStateStub.mock.lastCall[0].calculatorType).toBe('basic')
    })

    it('enables one question at a time properly', () => {
      const setOQAATStub = vi.fn()
      testRender({
        calculatorType: 'basic',
        oneQuestionAtATime: false,
        setOneQuestionAtATime: setOQAATStub,
      })

      const toggle = screen.getByRole('checkbox', {name: /enable one question at a time/i})
      userEvent.click(toggle)
      expect(setOQAATStub).toHaveBeenCalledTimes(1)
    })
  })

  describe('a11y tests', () => {
    it('should meet a11y standards', async () => {
      testRender()
      expect(
        await runAxeCheck(document.body, {
          ignores: ['region'],
        }),
      ).toBe(true)
    })
  })
})
